#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/syslog.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCValidation.h>
#include <SystemConfiguration/SCPrivate.h>
#include "globals.h"
#include "interfaces.h"
#include "config_method.h"
#include "ip6config_utils.h"
__private_extern__ CFDictionaryRef
my_SCDynamicStoreCopyValue(SCDynamicStoreRef session, CFStringRef key)
{
CFDictionaryRef dict;
dict = SCDynamicStoreCopyValue(session, key);
if (dict) {
if (isA_CFDictionary(dict) == NULL) {
my_CFRelease(&dict);
}
}
return (dict);
}
__private_extern__ int
service_set_addresses(Service_t * service_p, ip6_addrinfo_list_t * addr_list)
{
char str[64];
int i,
count = 0,
ret = 0,
s = inet6_dgram_socket();
interface_t *if_p = service_interface(service_p);
ip6_addrinfo_list_t valid_addrs;
if (s < 0) {
my_log(LOG_ERR, "service_set_addresses(%s): socket() failed, %s (%d)",
if_name(if_p), strerror(errno), errno);
return (errno);
}
valid_addrs.addr_list = malloc(addr_list->n_addrs * sizeof(ip6_addrinfo_t));
if (!valid_addrs.addr_list) {
my_log(LOG_ERR, "service_set_addresses(%s): socket() failed, %s (%d)",
if_name(if_p), strerror(errno), errno);
close(s);
return (-1);
}
for (i = 0; i < addr_list->n_addrs; i++) {
if (!IN6_IS_ADDR_UNSPECIFIED(&addr_list->addr_list[i].addr)) {
prefixLen2mask(&addr_list->addr_list[i].prefixmask, addr_list->addr_list[i].prefixlen);
if (!(addr_list->addr_list[i].flags & IN6_IFF_AUTOCONF) &&
inet6_aifaddr(s, if_name(if_p),
&addr_list->addr_list[i].addr,
NULL, &addr_list->addr_list[i].prefixmask) < 0) {
ret = errno;
my_log(LOG_DEBUG,
"service_set_addresses(%s): %s inet_aifaddr() failed, %s (%d)",
if_name(if_p), str, strerror(errno), errno);
break;
}
memcpy(&valid_addrs.addr_list[count],
&addr_list->addr_list[i],
sizeof(struct in6_addr));
count++;
}
}
if (!ret) {
if_setflags(if_p, if_flags(if_p) | IFF_UP);
ifflags_set(s, if_name(if_p), IFF_UP);
if (service_p->info.addrs.addr_list)
free(service_p->info.addrs.addr_list);
service_p->info.addrs.n_addrs = i;
service_p->info.addrs.addr_list = valid_addrs.addr_list;
if (G_verbose) {
char str[64], prefx[64];
for (i = 0; i < service_p->info.addrs.n_addrs; i++) {
inet_ntop(AF_INET6,
(const void *)&service_p->info.addrs.addr_list[i].addr,
str, sizeof(str));
inet_ntop(AF_INET6, (const void *)&service_p->info.addrs.addr_list[i].prefixmask,
prefx, sizeof(prefx));
my_log(LOG_DEBUG, "service_set_addresses(%s): %s , prefixmask: %s",
if_name(if_p), str, prefx);
}
}
}
free(valid_addrs.addr_list);
close(s);
return (ret);
}
__private_extern__ int
service_set_address(Service_t * service_p, struct in6_addr * addr, int prefixLen, int flags)
{
int ret = 0, s = inet6_dgram_socket();
char str[64];
interface_t *if_p = service_interface(service_p);
struct in6_addr prefixmask;
if (s < 0) {
my_log(LOG_ERR, "service_set_address(%s): socket() failed, %s (%d)",
if_name(if_p), strerror(errno), errno);
return (errno);
}
prefixLen2mask(&prefixmask, prefixLen);
if (!IN6_IS_ADDR_UNSPECIFIED(addr)) {
if (inet6_aifaddr(s, if_name(if_p), addr, NULL, &prefixmask) < 0) {
ret = errno;
my_log(LOG_DEBUG,
"service_set_address(%s): %s inet_aifaddr() failed, %s (%d)",
if_name(if_p), str, strerror(errno), errno);
}
}
if (!ret) {
if_setflags(if_p, if_flags(if_p) | IFF_UP);
ifflags_set(s, if_name(if_p), IFF_UP);
if (service_p->info.addrs.addr_list)
free(service_p->info.addrs.addr_list);
service_p->info.addrs.addr_list = malloc(sizeof(ip6_addrinfo_t));
if (!service_p->info.addrs.addr_list) {
my_log(LOG_ERR, "service_set_address(%s): socket() failed, %s (%d)",
if_name(if_p), strerror(errno), errno);
close(s);
return (-1);
}
service_p->info.addrs.n_addrs = 1;
memcpy(&service_p->info.addrs.addr_list->addr, addr, sizeof(struct in6_addr));
memcpy(&service_p->info.addrs.addr_list->prefixlen, &prefixLen, sizeof(int));
memcpy(&service_p->info.addrs.addr_list->flags, &flags, sizeof(int));
memcpy(&service_p->info.addrs.addr_list->prefixmask, &prefixmask, sizeof(struct in6_addr));
if (G_verbose) {
char str[64], prefx[64];
inet_ntop(AF_INET6,
(const void *)&service_p->info.addrs.addr_list->addr,
str, sizeof(str));
my_log(LOG_DEBUG, "service_set_address(%s): %s ", if_name(if_p), str);
inet_ntop(AF_INET6, (const void *)&prefixmask, prefx, sizeof(prefx));
my_log(LOG_DEBUG, "prefixmask: %s", prefx);
}
}
close(s);
return (ret);
}
__private_extern__ int
service_remove_addresses(Service_t * service_p)
{
int i, ret = 0;
int s = inet6_dgram_socket();
interface_t *if_p = service_interface(service_p);
ip6_addrinfo_list_t *addrs_p = &service_p->info.addrs;
if (s < 0) {
my_log(LOG_DEBUG,
"service_remove_addresses(%s) socket() failed, %s (%d)",
if_name(if_p), strerror(errno), errno);
return (errno);
}
for (i = 0; i < addrs_p->n_addrs; i++) {
if (!IN6_IS_ADDR_UNSPECIFIED(&addrs_p->addr_list[i].addr)) {
ip6_addrinfo_t saved_addr_info;
memcpy(&saved_addr_info,
&service_p->info.addrs.addr_list[i],
sizeof(ip6_addrinfo_t));
bzero(&addrs_p->addr_list[i], sizeof(ip6_addrinfo_t));
if (IFState_service_with_ip(service_ifstate(service_p), &saved_addr_info.addr) == NULL) {
my_log(LOG_DEBUG, "service_remove_addresses(%s): " IP6_FORMAT,
if_name(if_p), IP6_LIST(&saved_addr_info.addr));
if (inet6_difaddr(s, if_name(if_p), &saved_addr_info.addr) < 0) {
ret = errno;
my_log(LOG_DEBUG, "service_remove_addresses(%s) "
IP6_FORMAT " failed, %s (%d)", if_name(if_p),
IP6_LIST(&saved_addr_info.addr), strerror(errno), errno);
}
}
}
}
addrs_p->n_addrs = 0;
if (addrs_p->addr_list)
free(addrs_p->addr_list);
bzero(&service_p->info, sizeof(inet6_addrinfo_t));
if (s)
close(s);
return (ret);
}
__private_extern__ void
service_publish_clear(Service_t * service_p)
{
if (G_scd_session != NULL && service_p->serviceID) {
CFMutableArrayRef array;
CFStringRef key;
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (array == NULL) {
return;
}
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
service_p->serviceID,
kSCEntNetIPv6);
if (key) {
CFArrayAppendValue(array, key);
}
my_CFRelease(&key);
SCDynamicStoreSetMultiple(G_scd_session, NULL, array, NULL);
my_CFRelease(&array);
}
return;
}
__private_extern__ void
service_publish_failure(Service_t * service_p, ip6config_status_t status,
char * msg)
{
service_publish_clear(service_p);
my_log(LOG_DEBUG, "%s %s: status = '%s'",
ip6config_method_string(service_p->method),
if_name(service_interface(service_p)),
ip6config_status_string(status));
return;
}
static boolean_t
cache_key_different(SCDynamicStoreRef session, CFStringRef key,
CFDictionaryRef value)
{
CFDictionaryRef cache_value;
boolean_t ret = TRUE;
cache_value = my_SCDynamicStoreCopyValue(session, key);
if (cache_value) {
if (CFEqual(value, cache_value)) {
ret = FALSE;
}
my_CFRelease(&cache_value);
}
return (ret);
}
static __inline__ void
update_key(SCDynamicStoreRef session,
CFStringRef key, CFDictionaryRef dict,
CFMutableDictionaryRef keys_to_set,
CFMutableArrayRef keys_to_remove)
{
if (dict) {
if (cache_key_different(session, key, dict)) {
CFDictionarySetValue(keys_to_set, key, dict);
}
}
else {
CFArrayAppendValue(keys_to_remove, key);
}
return;
}
static void
publish_keys(CFStringRef ipv6_key, CFDictionaryRef ipv6_dict)
{
CFMutableDictionaryRef keys_to_set = NULL;
CFMutableArrayRef keys_to_remove = NULL;
if (ipv6_dict) {
SCLog(G_verbose, LOG_INFO, CFSTR("%@ = %@"), ipv6_key, ipv6_dict);
}
keys_to_set = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
keys_to_remove = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (keys_to_set == NULL || keys_to_remove == NULL) {
goto done;
}
update_key(G_scd_session, ipv6_key, ipv6_dict, keys_to_set, keys_to_remove);
if (CFArrayGetCount(keys_to_remove) > 0
|| CFDictionaryGetCount(keys_to_set) > 0) {
SCDynamicStoreSetMultiple(G_scd_session, keys_to_set,
keys_to_remove, NULL);
}
done:
my_CFRelease(&keys_to_remove);
my_CFRelease(&keys_to_set);
return;
}
static void
publish_service(CFStringRef serviceID, CFDictionaryRef ipv6_dict)
{
CFStringRef ipv6_key = NULL;
ipv6_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
serviceID,
kSCEntNetIPv6);
if (ipv6_key == NULL) {
goto done;
}
publish_keys(ipv6_key, ipv6_dict);
done:
my_CFRelease(&ipv6_key);
return;
}
__private_extern__ void
service_publish_success(Service_t * service_p)
{
int i;
CFMutableArrayRef array = NULL;
inet6_addrinfo_t * info_p;
CFMutableDictionaryRef ipv6_dict = NULL;
CFStringRef str;
CFNumberRef prefixlen, flags;
if (service_p->serviceID == NULL) {
return;
}
info_p = &service_p->info;
if (G_scd_session == NULL) {
return;
}
ipv6_dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < info_p->addrs.n_addrs; i++) {
str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP6_FORMAT),
IP6_LIST(&info_p->addrs.addr_list[i].addr));
if (array && str) {
CFArrayAppendValue(array, str);
}
my_CFRelease(&str);
}
CFDictionarySetValue(ipv6_dict, kSCPropNetIPv6Addresses, array);
my_CFRelease(&array);
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < info_p->addrs.n_addrs; i++) {
prefixlen = CFNumberCreate(NULL, kCFNumberIntType,
&info_p->addrs.addr_list[i].prefixlen);
if (array && prefixlen) {
CFArrayAppendValue(array, prefixlen);
}
my_CFRelease(&prefixlen);
}
CFDictionarySetValue(ipv6_dict, kSCPropNetIPv6PrefixLength, array);
my_CFRelease(&array);
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < info_p->addrs.n_addrs; i++) {
flags = CFNumberCreate(NULL, kCFNumberIntType, &info_p->addrs.addr_list[i].flags);
if (array && flags) {
CFArrayAppendValue(array, flags);
}
my_CFRelease(&flags);
}
CFDictionarySetValue(ipv6_dict, kSCPropNetIPv6Flags, array);
my_CFRelease(&array);
if (!IN6_IS_ADDR_UNSPECIFIED(&info_p->router)) {
str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP6_FORMAT), IP6_LIST(&info_p->router));
if (str) {
CFDictionarySetValue(ipv6_dict, kSCPropNetIPv6Router, str);
}
my_CFRelease(&str);
}
CFDictionarySetValue(ipv6_dict, CFSTR("InterfaceName"), service_ifstate(service_p)->ifname);
publish_service(service_p->serviceID, ipv6_dict);
my_CFRelease(&ipv6_dict);
return;
}
__private_extern__ ip6config_status_t
service_set_service(IFState_t * ifstate, ServiceConfig_t * config)
{
CFStringRef serviceID = config->serviceID;
Service_t * service_p;
IFState_t * this_ifstate = NULL;
service_p = IFState_service_with_ID(ifstate, serviceID);
if (service_p) {
boolean_t needs_stop = FALSE;
ip6config_status_t status;
if (service_p->method == config->method) {
status = config_method_change(service_p, config->method,
config->method_data,
&needs_stop);
if (status == ip6config_status_success_e && needs_stop == FALSE) {
return (ip6config_status_success_e);
}
}
IFState_service_free(ifstate, serviceID);
}
else {
this_ifstate = IFStateList_service_with_ID(&G_ifstate_list,
serviceID,
&service_p);
if (this_ifstate) {
IFState_service_free(this_ifstate, serviceID);
}
}
return (IFState_service_add(ifstate, serviceID, config->method,
config->method_data));
}
static ServiceConfig_t *
service_config_list_lookup_service(ServiceConfig_t * config_list, int count,
CFStringRef serviceID)
{
ServiceConfig_t * config;
int i;
for (config = config_list, i = 0; i < count; i++, config++) {
if (CFEqual(serviceID, config->serviceID)) {
return (config);
}
}
return (NULL);
}
__private_extern__ void
service_free_inactive_services(char * ifname, ServiceConfig_t * config_list, int count)
{
int j;
IFState_t * ifstate;
CFMutableArrayRef list = NULL;
int service_count;
ifstate = IFStateList_ifstate_with_name(&G_ifstate_list, ifname, NULL);
if (ifstate == NULL) {
goto done;
}
list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (list == NULL) {
goto done;
}
service_count = dynarray_count(&ifstate->services);
for (j = 0; j < service_count; j++) {
Service_t * service_p = dynarray_element(&ifstate->services, j);
CFStringRef serviceID = service_p->serviceID;
if (service_config_list_lookup_service(config_list, count,
serviceID) == NULL) {
CFArrayAppendValue(list, serviceID);
}
}
service_count = CFArrayGetCount(list);
for (j = 0; j < service_count; j++) {
CFStringRef serviceID = CFArrayGetValueAtIndex(list, j);
IFState_service_free(ifstate, serviceID);
}
done:
my_CFRelease(&list);
return;
}
static CFArrayRef
interface_services_copy(CFArrayRef all, CFStringRef ifn_cf)
{
int i, n;
CFMutableArrayRef list = NULL;
if (all == NULL) {
return (NULL);
}
list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (list == NULL) {
return (NULL);
}
n = CFArrayGetCount(all);
for (i = 0; i < n; i++) {
CFDictionaryRef item = CFArrayGetValueAtIndex(all, i);
CFStringRef name;
name = CFDictionaryGetValue(item, kSCPropNetInterfaceDeviceName);
if (CFEqual(name, ifn_cf)) {
CFArrayAppendValue(list, item);
}
}
if (CFArrayGetCount(list) == 0) {
my_CFRelease(&list);
}
return (list);
}
__private_extern__ void
service_config_list_free(ServiceConfig_t * * list_p_p, int count)
{
int i;
ServiceConfig_t * list_p = *list_p_p;
for (i = 0; i < count; i++) {
if (list_p[i].serviceID)
my_CFRelease(&list_p[i].serviceID);
if (list_p[i].method_data) {
if (list_p[i].method_data->stf_data.ip4_addrs_list)
free(list_p[i].method_data->stf_data.ip4_addrs_list);
if (list_p[i].method_data->stf_data.relay_address.addr_type == relay_address_type_dns_e
&& list_p[i].method_data->stf_data.relay_address.relay_address_u.dns_relay_addr)
free(list_p[i].method_data->stf_data.relay_address.relay_address_u.dns_relay_addr);
free(list_p[i].method_data);
}
}
free(list_p);
*list_p_p = NULL;
return;
}
__private_extern__ ServiceConfig_t *
service_config_list_lookup_method(ServiceConfig_t * config_list, int count,
ip6config_method_t method,
ip6config_method_data_t * method_data)
{
ServiceConfig_t * config;
int i;
switch (method) {
case ip6config_method_automatic_e:
case ip6config_method_rtadv_e:
case ip6config_method_6to4_e: {
for (config = config_list, i = 0; i < count; i++, config++) {
if (ip6config_method_is_dynamic(config->method))
return (config);
}
break;
}
case ip6config_method_manual_e: {
for (config = config_list, i = 0; i < count; i++, config++) {
if (ip6config_method_is_manual(config->method)
&& IN6_ARE_ADDR_EQUAL(&method_data->ip6[0].addr,
&config->method_data->ip6[0].addr)) {
return (config);
}
}
break;
}
default: {
break;
}
}
return (NULL);
}
__private_extern__ ServiceConfig_t *
service_config_list_init(SCDynamicStoreRef session,
CFArrayRef all_ipv6,
char * ifname, int * count_p)
{
ServiceConfig_t * config_list = NULL;
int count = 0;
int i;
CFArrayRef if_service_list;
int if_service_count;
CFStringRef ifn_cf = NULL;
ifn_cf = CFStringCreateWithCString(NULL, ifname,
kCFStringEncodingMacRoman);
if (ifn_cf == NULL) {
goto done;
}
if_service_list = interface_services_copy(all_ipv6, ifn_cf);
if (if_service_list == NULL) {
goto done;
}
if_service_count = CFArrayGetCount(if_service_list);
config_list = (ServiceConfig_t *) calloc(if_service_count, sizeof(*config_list));
if (config_list == NULL) {
goto done;
}
for (i = 0; i < if_service_count; i++) {
CFDictionaryRef ipv6_dict;
ip6config_method_t method;
ip6config_method_data_t * method_data;
CFStringRef serviceID;
ipv6_dict = CFArrayGetValueAtIndex(if_service_list, i);
serviceID = CFDictionaryGetValue(ipv6_dict, PROP_SERVICEID);
method_data = ip6config_method_data_from_dict(ipv6_dict, &method);
if (method_data == NULL) {
continue;
}
if (method == ip6config_method_6to4_e) {
if (ip6config_get_6to4_address_data(session, method_data) != 0) {
my_log(LOG_DEBUG,
"service_config_list_init: ip6config_get_6to4_address_data returned error");
}
}
if (service_config_list_lookup_method(config_list, count, method, method_data)) {
boolean_t is_manual = ip6config_method_is_manual(method);
if (is_manual) {
my_log(LOG_INFO, "%s: %s " IP6_FORMAT " duplicate service",
ifname, ip6config_method_string(method),
IP6_LIST(&method_data->ip6[0].addr));
}
else {
my_log(LOG_INFO, "%s: %s ignored",
ifname, ip6config_method_string(method));
}
free(method_data);
continue;
}
config_list[count].serviceID = CFRetain(serviceID);
config_list[count].method = method;
config_list[count].method_data = method_data;
count++;
}
done:
if (config_list && count == 0) {
service_config_list_free(&config_list, count);
}
my_CFRelease(&ifn_cf);
my_CFRelease(&if_service_list);
*count_p = count;
return (config_list);
}
__private_extern__ void
Service_free(void * arg)
{
Service_t * service_p = (Service_t *)arg;
SCLog(G_verbose, LOG_INFO, CFSTR("Service_free(%@)"),
service_p->serviceID);
config_method_stop(service_p);
service_publish_clear(service_p);
if (service_p->info.addrs.addr_list)
free(service_p->info.addrs.addr_list);
if (service_p->user_rls) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), service_p->user_rls,
kCFRunLoopDefaultMode);
my_CFRelease(&service_p->user_rls);
}
if (service_p->user_notification != NULL) {
CFUserNotificationCancel(service_p->user_notification);
my_CFRelease(&service_p->user_notification);
}
my_CFRelease(&service_p->serviceID);
free(service_p);
return;
}
__private_extern__ Service_t *
Service_init(IFState_t * ifstate, CFStringRef serviceID,
ip6config_method_t method,
void * method_data,
ip6config_status_t * status_p)
{
Service_t * service_p = NULL;
ip6config_status_t status = ip6config_status_success_e;
service_p = (Service_t *)malloc(sizeof(*service_p));
if (service_p == NULL) {
status = ip6config_status_allocation_failed_e;
goto failed;
}
bzero(service_p, sizeof(*service_p));
service_p->method = method;
service_p->ifstate = ifstate;
if (serviceID) {
service_p->serviceID = (void *)CFRetain(serviceID);
}
else {
service_p->serviceID = (void *)CFStringCreateWithFormat(NULL, NULL,
CFSTR("%s-%s"),
ip6config_method_string(method),
if_name(service_interface(service_p)));
}
status = config_method_start(service_p, method, method_data);
if (status != ip6config_status_success_e) {
goto failed;
}
*status_p = status;
return (service_p);
failed:
if (service_p) {
my_CFRelease(&service_p->serviceID);
free(service_p);
}
*status_p = status;
return (NULL);
}
#ifndef NO_CFUserNotification
static void
user_confirm(CFUserNotificationRef userNotification, CFOptionFlags responseFlags)
{
int i;
int ifstate_count;
ifstate_count = dynarray_count(&G_ifstate_list);
for (i = 0; i < ifstate_count; i++) {
IFState_t * ifstate = dynarray_element(&G_ifstate_list, i);
int j;
int service_count;
service_count = dynarray_count(&ifstate->services);
for (j = 0; j < service_count; j++) {
Service_t * service_p = dynarray_element(&ifstate->services, j);
if (service_p->user_notification == userNotification) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
service_p->user_rls, kCFRunLoopDefaultMode);
my_CFRelease(&service_p->user_rls);
my_CFRelease(&service_p->user_notification);
return;
}
}
}
return;
}
static void
service_notify_user(Service_t * service_p, CFTypeRef alert_string)
{
CFMutableDictionaryRef dict = NULL;
SInt32 error = 0;
CFUserNotificationRef notify = NULL;
CFRunLoopSourceRef rls = NULL;
CFURLRef url = NULL;
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (dict == NULL) {
goto done;
}
url = CFBundleCopyBundleURL(G_bundle);
if (url == NULL) {
goto done;
}
CFDictionarySetValue(dict, kCFUserNotificationAlertHeaderKey,
CFSTR("IP6 Configuration"));
CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey,
alert_string);
CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey,
url);
if (service_p->user_rls) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), service_p->user_rls,
kCFRunLoopDefaultMode);
my_CFRelease(&service_p->user_rls);
}
if (service_p->user_notification != NULL) {
CFUserNotificationCancel(service_p->user_notification);
my_CFRelease(&service_p->user_notification);
}
notify = CFUserNotificationCreate(NULL, 0, 0, &error, dict);
if (notify == NULL) {
my_log(LOG_ERR, "CFUserNotificationCreate() failed, %d",
error);
goto done;
}
rls = CFUserNotificationCreateRunLoopSource(NULL, notify,
user_confirm, 0);
if (rls == NULL) {
my_log(LOG_ERR, "CFUserNotificationCreateRunLoopSource() failed");
my_CFRelease(¬ify);
}
else {
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls,
kCFRunLoopDefaultMode);
service_p->user_rls = rls;
service_p->user_notification = notify;
}
done:
my_CFRelease(&dict);
my_CFRelease(&url);
return;
}
#else
static void
service_notify_user(Service_t * service_p, CFTypeRef alertMessage)
{
}
#endif
__private_extern__ void
service_report_conflict(Service_t * service_p, struct in6_addr * ip6)
{
CFMutableArrayRef array;
CFStringRef str;
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (array == NULL) {
goto done;
}
CFArrayAppendValue(array, CFSTR("The IPv6 address "));
str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP6_FORMAT), IP6_LIST(ip6));
if (str == NULL) {
goto done;
}
CFArrayAppendValue(array, str);
CFRelease(str);
CFArrayAppendValue(array, CFSTR(" is in use by another computer"));
service_notify_user(service_p, array);
done:
my_CFRelease(&array);
return;
}