/* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Modification History * * March 9, 2004 Allan Nathanson <ajn@apple.com> * - initial revision */ #include <stdlib.h> #include <mach/mach.h> #include <mach/mach_error.h> #include "dnsinfo.h" #include "dnsinfo_private.h" #include "shared_dns_info.h" static boolean_t add_list(void **padding, uint32_t *n_padding, int32_t count, int32_t size, void **list) { int32_t need; need = count * size; if (need > *n_padding) { return FALSE; } *list = (need == 0) ? NULL : *padding; *padding += need; *n_padding -= need; return TRUE; } static _dns_config_buf_t * copy_dns_info() { uint8_t *buf = NULL; dnsDataOut_t dataRef = NULL; mach_msg_type_number_t dataLen = 0; mach_port_t server; kern_return_t status; server = _dns_configuration_server_port(); if (server == MACH_PORT_NULL) { return NULL; } status = shared_dns_infoGet(server, &dataRef, &dataLen); (void)mach_port_deallocate(mach_task_self(), server); if (status != KERN_SUCCESS) { mach_error("shared_dns_infoGet():", status); return NULL; } if (dataRef != NULL) { if (dataLen >= sizeof(_dns_config_buf_t)) { _dns_config_buf_t *config = (_dns_config_buf_t *)dataRef; uint32_t len; uint32_t n_padding = ntohl(config->n_padding); len = dataLen + n_padding; buf = malloc(len); bcopy((void *)dataRef, buf, dataLen); bzero(&buf[dataLen], n_padding); } status = vm_deallocate(mach_task_self(), (vm_address_t)dataRef, dataLen); if (status != KERN_SUCCESS) { mach_error("vm_deallocate():", status); free(buf); return NULL; } } return (_dns_config_buf_t *)buf; } static dns_resolver_t * expand_resolver(_dns_resolver_buf_t *buf, uint32_t n_buf, void **padding, uint32_t *n_padding) { dns_attribute_t *attribute; uint32_t n_attribute; int32_t n_nameserver = 0; int32_t n_search = 0; int32_t n_sortaddr = 0; dns_resolver_t *resolver = (dns_resolver_t *)&buf->resolver; if (n_buf < sizeof(_dns_resolver_buf_t)) { goto error; } // initialize domain resolver->domain = NULL; // initialize nameserver list resolver->n_nameserver = ntohl(resolver->n_nameserver); if (!add_list(padding, n_padding, resolver->n_nameserver, sizeof(struct sockaddr *), (void **)&resolver->nameserver)) { goto error; } // initialize port resolver->port = ntohs(resolver->port); // initialize search list resolver->n_search = ntohl(resolver->n_search); if (!add_list(padding, n_padding, resolver->n_search, sizeof(char *), (void **)&resolver->search)) { goto error; } // initialize sortaddr list resolver->n_sortaddr = ntohl(resolver->n_sortaddr); if (!add_list(padding, n_padding, resolver->n_sortaddr, sizeof(dns_sortaddr_t *), (void **)&resolver->sortaddr)) { goto error; } // initialize options resolver->options = NULL; // initialize timeout resolver->timeout = ntohl(resolver->timeout); // initialize search_order resolver->search_order = ntohl(resolver->search_order); // process resolver buffer "attribute" data n_attribute = n_buf - sizeof(_dns_resolver_buf_t); attribute = (dns_attribute_t *)&buf->attribute[0]; if (n_attribute != ntohl(buf->n_attribute)) { goto error; } while (n_attribute >= sizeof(dns_attribute_t)) { int32_t attribute_length = ntohl(attribute->length); switch (ntohl(attribute->type)) { case RESOLVER_ATTRIBUTE_DOMAIN : resolver->domain = (char *)&attribute->attribute[0]; break; case RESOLVER_ATTRIBUTE_ADDRESS : resolver->nameserver[n_nameserver++] = (struct sockaddr *)&attribute->attribute[0]; break; case RESOLVER_ATTRIBUTE_SEARCH : resolver->search[n_search++] = (char *)&attribute->attribute[0]; break; case RESOLVER_ATTRIBUTE_SORTADDR : resolver->sortaddr[n_sortaddr++] = (dns_sortaddr_t *)&attribute->attribute[0]; break; case RESOLVER_ATTRIBUTE_OPTIONS : resolver->options = (char *)&attribute->attribute[0]; break; default : break; } attribute = (dns_attribute_t *)((void *)attribute + attribute_length); n_attribute -= attribute_length; } if ((n_nameserver != resolver->n_nameserver) || (n_search != resolver->n_search ) || (n_sortaddr != resolver->n_sortaddr )) { goto error; } return resolver; error : return NULL; } static dns_config_t * expand_config(_dns_config_buf_t *buf) { dns_attribute_t *attribute; dns_config_t *config = (dns_config_t *)buf; uint32_t n_attribute; uint32_t n_padding; int32_t n_resolver = 0; void *padding; // establish padding padding = &buf->attribute[ntohl(buf->n_attribute)]; n_padding = ntohl(buf->n_padding); // initialize resolver list config->n_resolver = ntohl(config->n_resolver); if (!add_list(&padding, &n_padding, config->n_resolver, sizeof(dns_resolver_t *), (void **)&config->resolver)) { goto error; } // process configuration buffer "attribute" data n_attribute = ntohl(buf->n_attribute); attribute = (dns_attribute_t *)&buf->attribute[0]; while (n_attribute >= sizeof(dns_attribute_t)) { int32_t attribute_length = ntohl(attribute->length); switch (ntohl(attribute->type)) { case CONFIG_ATTRIBUTE_RESOLVER : { dns_resolver_t *resolver; // expand resolver buffer resolver = expand_resolver((_dns_resolver_buf_t *)&attribute->attribute[0], attribute_length - sizeof(dns_attribute_t), &padding, &n_padding); if (resolver == NULL) { goto error; } // add resolver to config list config->resolver[n_resolver++] = resolver; break; } default : break; } attribute = (dns_attribute_t *)((void *)attribute + attribute_length); n_attribute -= attribute_length; } if (n_resolver != config->n_resolver) { goto error; } return config; error : return NULL; } __private_extern__ const char * dns_configuration_notify_key() { return _dns_configuration_notify_key(); } __private_extern__ dns_config_t * dns_configuration_copy() { _dns_config_buf_t *buf; dns_config_t *config; buf = copy_dns_info(); if (buf == NULL) { return NULL; } config = expand_config(buf); if (config == NULL) { free(buf); return NULL; } return config; } __private_extern__ void dns_configuration_free(dns_config_t *config) { if (config == NULL) { return; } free((void *)config); return; }