2 * Copyright (c) 2008 Apple Inc. All Rights Reserved.
4 * Export of this software from the United States of America may require
5 * a specific license from the United States Government. It is the
6 * responsibility of any person or organization contemplating export to
7 * obtain such a license before exporting.
9 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10 * distribute this software and its documentation for any purpose and
11 * without fee is hereby granted, provided that the above copyright
12 * notice appear in all copies and that both that copyright notice and
13 * this permission notice appear in supporting documentation, and that
14 * the name of Apple Inc. not be used in advertising or publicity pertaining
15 * to distribution of the software without specific, written prior
16 * permission. Apple Inc. makes no representations about the suitability of
17 * this software for any purpose. It is provided "as is" without express
18 * or implied warranty.
20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
22 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
28 #if defined(__APPLE__) && defined(HAVE_GCD)
30 #include <CoreFoundation/CoreFoundation.h>
31 #include <SystemConfiguration/SCDynamicStore.h>
32 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
33 #include <SystemConfiguration/SCDynamicStoreKey.h>
35 #include <dispatch/dispatch.h>
43 static krb5_kdc_configuration
*announce_config
;
44 static krb5_context announce_context
;
47 DNSRecordRef recordRef
;
56 /* #define REGISTER_SRV_RR */
58 static struct entry
*g_entries
= NULL
;
59 static CFStringRef g_hostname
= NULL
;
60 static DNSServiceRef g_dnsRef
= NULL
;
61 static SCDynamicStoreRef g_store
= NULL
;
62 static dispatch_queue_t g_queue
= NULL
;
64 #define LOG(...) asl_log(NULL, NULL, ASL_LEVEL_INFO, __VA_ARGS__)
66 static void create_dns_sd(void);
67 static void destroy_dns_sd(void);
68 static void update_all(SCDynamicStoreRef
, CFArrayRef
, void *);
72 static CFStringRef NetworkChangedKey_BackToMyMac
= CFSTR("Setup:/Network/BackToMyMac");
76 CFString2utf8(CFStringRef string
)
81 size
= 1 + CFStringGetMaximumSizeForEncoding(CFStringGetLength(string
), kCFStringEncodingUTF8
);
86 if (CFStringGetCString(string
, str
, size
, kCFStringEncodingUTF8
) == false) {
103 s
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
105 t
= dispatch_time(DISPATCH_TIME_NOW
, 5ull * NSEC_PER_SEC
);
106 dispatch_source_set_timer(s
, t
, 0, NSEC_PER_SEC
);
107 dispatch_source_set_event_handler(s
, ^{
121 DNSServiceErrorType error
;
124 error
= DNSServiceCreateConnection(&g_dnsRef
);
130 dispatch_suspend(g_queue
);
132 s
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
,
133 DNSServiceRefSockFD(g_dnsRef
),
136 dispatch_source_set_event_handler(s
, ^{
137 DNSServiceErrorType ret
= DNSServiceProcessResult(g_dnsRef
);
138 /* on error tear down and set timer to recreate */
139 if (ret
!= kDNSServiceErr_NoError
&& ret
!= kDNSServiceErr_Transient
) {
140 dispatch_source_cancel(s
);
144 dispatch_source_set_cancel_handler(s
, ^{
152 /* Do the first update ourself */
153 update_all(g_store
, NULL
, NULL
);
154 dispatch_resume(g_queue
);
158 domain_add(const char *domain
, const char *realm
, int flag
)
162 for (e
= g_entries
; e
!= NULL
; e
= e
->next
) {
163 if (strcmp(domain
, e
->domain
) == 0 && strcmp(realm
, e
->realm
) == 0) {
169 LOG("Adding realm %s to domain %s", realm
, domain
);
171 e
= calloc(1, sizeof(*e
));
174 e
->domain
= strdup(domain
);
175 e
->realm
= strdup(realm
);
176 if (e
->domain
== NULL
|| e
->realm
== NULL
) {
182 e
->flags
= flag
| F_PUSH
; /* if we allocate, we push */
193 domains_add(const void *key
, const void *value
, void *context
)
195 char *str
= CFString2utf8((CFStringRef
)value
);
196 struct addctx
*ctx
= context
;
201 domain_add(str
, ctx
->realm
, F_EXISTS
| ctx
->flags
);
207 dnsCallback(DNSServiceRef sdRef
__attribute__((unused
)),
208 DNSRecordRef RecordRef
__attribute__((unused
)),
209 DNSServiceFlags flags
__attribute__((unused
)),
210 DNSServiceErrorType errorCode
__attribute__((unused
)),
211 void *context
__attribute__((unused
)))
215 #ifdef REGISTER_SRV_RR
218 * Register DNS SRV rr for the realm.
221 static const char *register_names
[2] = {
229 } srvRefs
= { NULL
, 0 };
232 register_srv(const char *realm
, const char *hostname
, int port
)
234 unsigned char target
[1024];
238 /* skip registering LKDC realms */
239 if (strncmp(realm
, "LKDC:", 5) == 0)
243 target
[0] = 0; /* priority */
244 target
[1] = 0; /* priority */
245 target
[2] = 0; /* weight */
246 target
[3] = 0; /* weigth */
247 target
[4] = (port
>> 8) & 0xff; /* port */
248 target
[5] = (port
>> 0) & 0xff; /* port */
250 size
= dn_comp(hostname
, target
+ 6, sizeof(target
) - 6, NULL
, NULL
);
256 LOG("register SRV rr for realm %s hostname %s:%d", realm
, hostname
, port
);
258 for (i
= 0; i
< sizeof(register_names
)/sizeof(register_names
[0]); i
++) {
259 char name
[kDNSServiceMaxDomainName
];
260 DNSServiceErrorType error
;
263 ptr
= realloc(srvRefs
.val
, sizeof(srvRefs
.val
[0]) * (srvRefs
.len
+ 1));
265 errx(1, "malloc: out of memory");
268 DNSServiceConstructFullName(name
, NULL
, register_names
[i
], realm
);
270 error
= DNSServiceRegisterRecord(g_dnsRef
,
271 &srvRefs
.val
[srvRefs
.len
],
272 kDNSServiceFlagsUnique
| kDNSServiceFlagsShareConnection
,
283 LOG("Failed to register SRV rr for realm %s: %d", realm
, error
);
290 unregister_srv_realms(void)
293 for (i
= 0; i
< srvRefs
.len
; i
++)
294 DNSServiceRemoveRecord(g_dnsRef
, srvRefs
.val
[i
], 0);
302 register_srv_realms(CFStringRef host
)
308 /* first unregister old names */
310 hostname
= CFString2utf8(host
);
311 if (hostname
== NULL
)
314 for(i
= 0; i
< announce_config
->num_db
; i
++) {
317 if (announce_config
->db
[i
]->hdb_get_realms
== NULL
)
320 ret
= (announce_config
->db
[i
]->hdb_get_realms
)(announce_context
, &realms
);
322 for (r
= realms
; r
&& *r
; r
++)
323 register_srv(*r
, hostname
, 88);
324 krb5_free_host_realm(announce_context
, realms
);
330 #endif /* REGISTER_SRV_RR */
335 DNSServiceErrorType error
;
336 struct entry
**e
= &g_entries
;
339 hostname
= CFString2utf8(g_hostname
);
340 if (hostname
== NULL
)
344 /* remove if this wasn't updated */
345 if (((*e
)->flags
& F_EXISTS
) == 0) {
346 struct entry
*drop
= *e
;
349 LOG("Deleting realm %s from domain %s",
350 drop
->realm
, drop
->domain
);
352 if (drop
->recordRef
&& g_dnsRef
)
353 DNSServiceRemoveRecord(g_dnsRef
, drop
->recordRef
, 0);
359 if ((*e
)->flags
& F_PUSH
) {
360 struct entry
*update
= *e
;
361 char *dnsdata
, *name
;
364 len
= strlen(update
->realm
);
365 asprintf(&dnsdata
, "%c%s", len
, update
->realm
);
369 asprintf(&name
, "_kerberos.%s.%s", hostname
, update
->domain
);
373 if (update
->recordRef
)
374 DNSServiceRemoveRecord(g_dnsRef
, update
->recordRef
, 0);
376 error
= DNSServiceRegisterRecord(g_dnsRef
,
378 kDNSServiceFlagsShared
| kDNSServiceFlagsAllowRemoteQuery
,
391 errx(1, "failure to update entry for %s/%s",
392 update
->domain
, update
->realm
);
400 update_entries(SCDynamicStoreRef store
, const char *realm
, int flags
)
402 CFDictionaryRef btmm
;
404 /* we always announce in the local domain */
405 domain_add("local", realm
, F_EXISTS
| flags
);
408 btmm
= SCDynamicStoreCopyValue(store
, NetworkChangedKey_BackToMyMac
);
410 struct addctx addctx
;
412 addctx
.flags
= flags
;
413 addctx
.realm
= realm
;
415 CFDictionaryApplyFunction(btmm
, domains_add
, &addctx
);
421 update_all(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
427 LOG("something changed, running update");
429 host
= SCDynamicStoreCopyLocalHostName(store
);
433 if (g_hostname
== NULL
|| CFStringCompare(host
, g_hostname
, 0) != kCFCompareEqualTo
) {
435 CFRelease(g_hostname
);
436 g_hostname
= CFRetain(host
);
437 flags
= F_PUSH
; /* if hostname has changed, force push */
439 #ifdef REGISTER_SRV_RR
440 register_srv_realms(g_hostname
);
444 for (e
= g_entries
; e
!= NULL
; e
= e
->next
)
445 e
->flags
&= ~(F_EXISTS
|F_PUSH
);
447 for(i
= 0; i
< announce_config
->num_db
; i
++) {
451 if (announce_config
->db
[i
]->hdb_get_realms
== NULL
)
454 ret
= (announce_config
->db
[i
]->hdb_get_realms
)(announce_context
, announce_config
->db
[i
], &realms
);
456 for (r
= realms
; r
&& *r
; r
++)
457 update_entries(store
, *r
, flags
);
458 krb5_free_host_realm(announce_context
, realms
);
472 for (e
= g_entries
; e
!= NULL
; e
= e
->next
)
473 e
->flags
&= ~(F_EXISTS
|F_PUSH
);
476 if (g_entries
!= NULL
)
477 errx(1, "Failed to remove all bonjour entries");
483 if (g_dnsRef
== NULL
)
487 #ifdef REGISTER_SRV_RR
488 unregister_srv_realms();
491 DNSServiceRefDeallocate(g_dnsRef
);
496 static SCDynamicStoreRef
497 register_notification(void)
499 SCDynamicStoreRef store
;
500 CFStringRef computerNameKey
;
501 CFMutableArrayRef keys
;
503 computerNameKey
= SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault
);
505 store
= SCDynamicStoreCreate(kCFAllocatorDefault
, CFSTR("Network watcher"),
508 errx(1, "SCDynamicStoreCreate");
510 keys
= CFArrayCreateMutable(kCFAllocatorDefault
, 2, &kCFTypeArrayCallBacks
);
512 errx(1, "CFArrayCreateMutable");
514 CFArrayAppendValue(keys
, computerNameKey
);
515 CFArrayAppendValue(keys
, NetworkChangedKey_BackToMyMac
);
517 if (SCDynamicStoreSetNotificationKeys(store
, keys
, NULL
) == false)
518 errx(1, "SCDynamicStoreSetNotificationKeys");
520 CFRelease(computerNameKey
);
523 if (!SCDynamicStoreSetDispatchQueue(store
, g_queue
))
524 errx(1, "SCDynamicStoreSetDispatchQueue");
531 bonjour_announce(krb5_context context
, krb5_kdc_configuration
*config
)
533 #if defined(__APPLE__) && defined(HAVE_GCD)
534 g_queue
= dispatch_queue_create("com.apple.kdc_announce", NULL
);
536 errx(1, "dispatch_queue_create");
538 g_store
= register_notification();
539 announce_config
= config
;
540 announce_context
= context
;