1 /* $NetBSD: announce.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */
4 * Copyright (c) 2008 Apple Inc. All Rights Reserved.
6 * Export of this software from the United States of America may require
7 * a specific license from the United States Government. It is the
8 * responsibility of any person or organization contemplating export to
9 * obtain such a license before exporting.
11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12 * distribute this software and its documentation for any purpose and
13 * without fee is hereby granted, provided that the above copyright
14 * notice appear in all copies and that both that copyright notice and
15 * this permission notice appear in supporting documentation, and that
16 * the name of Apple Inc. not be used in advertising or publicity pertaining
17 * to distribution of the software without specific, written prior
18 * permission. Apple Inc. makes no representations about the suitability of
19 * this software for any purpose. It is provided "as is" without express
20 * or implied warranty.
22 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
24 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30 #if defined(__APPLE__) && defined(HAVE_GCD)
32 #include <CoreFoundation/CoreFoundation.h>
33 #include <SystemConfiguration/SCDynamicStore.h>
34 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
35 #include <SystemConfiguration/SCDynamicStoreKey.h>
37 #include <dispatch/dispatch.h>
45 static krb5_kdc_configuration
*announce_config
;
46 static krb5_context announce_context
;
49 DNSRecordRef recordRef
;
58 /* #define REGISTER_SRV_RR */
60 static struct entry
*g_entries
= NULL
;
61 static CFStringRef g_hostname
= NULL
;
62 static DNSServiceRef g_dnsRef
= NULL
;
63 static SCDynamicStoreRef g_store
= NULL
;
64 static dispatch_queue_t g_queue
= NULL
;
66 #define LOG(...) asl_log(NULL, NULL, ASL_LEVEL_INFO, __VA_ARGS__)
68 static void create_dns_sd(void);
69 static void destroy_dns_sd(void);
70 static void update_all(SCDynamicStoreRef
, CFArrayRef
, void *);
74 static CFStringRef NetworkChangedKey_BackToMyMac
= CFSTR("Setup:/Network/BackToMyMac");
78 CFString2utf8(CFStringRef string
)
83 size
= 1 + CFStringGetMaximumSizeForEncoding(CFStringGetLength(string
), kCFStringEncodingUTF8
);
88 if (CFStringGetCString(string
, str
, size
, kCFStringEncodingUTF8
) == false) {
105 s
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
107 t
= dispatch_time(DISPATCH_TIME_NOW
, 5ull * NSEC_PER_SEC
);
108 dispatch_source_set_timer(s
, t
, 0, NSEC_PER_SEC
);
109 dispatch_source_set_event_handler(s
, ^{
123 DNSServiceErrorType error
;
126 error
= DNSServiceCreateConnection(&g_dnsRef
);
132 dispatch_suspend(g_queue
);
134 s
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
,
135 DNSServiceRefSockFD(g_dnsRef
),
138 dispatch_source_set_event_handler(s
, ^{
139 DNSServiceErrorType ret
= DNSServiceProcessResult(g_dnsRef
);
140 /* on error tear down and set timer to recreate */
141 if (ret
!= kDNSServiceErr_NoError
&& ret
!= kDNSServiceErr_Transient
) {
142 dispatch_source_cancel(s
);
146 dispatch_source_set_cancel_handler(s
, ^{
154 /* Do the first update ourself */
155 update_all(g_store
, NULL
, NULL
);
156 dispatch_resume(g_queue
);
160 domain_add(const char *domain
, const char *realm
, int flag
)
164 for (e
= g_entries
; e
!= NULL
; e
= e
->next
) {
165 if (strcmp(domain
, e
->domain
) == 0 && strcmp(realm
, e
->realm
) == 0) {
171 LOG("Adding realm %s to domain %s", realm
, domain
);
173 e
= calloc(1, sizeof(*e
));
176 e
->domain
= strdup(domain
);
177 e
->realm
= strdup(realm
);
178 if (e
->domain
== NULL
|| e
->realm
== NULL
) {
184 e
->flags
= flag
| F_PUSH
; /* if we allocate, we push */
195 domains_add(const void *key
, const void *value
, void *context
)
197 char *str
= CFString2utf8((CFStringRef
)value
);
198 struct addctx
*ctx
= context
;
203 domain_add(str
, ctx
->realm
, F_EXISTS
| ctx
->flags
);
209 dnsCallback(DNSServiceRef sdRef
__attribute__((unused
)),
210 DNSRecordRef RecordRef
__attribute__((unused
)),
211 DNSServiceFlags flags
__attribute__((unused
)),
212 DNSServiceErrorType errorCode
__attribute__((unused
)),
213 void *context
__attribute__((unused
)))
217 #ifdef REGISTER_SRV_RR
220 * Register DNS SRV rr for the realm.
223 static const char *register_names
[2] = {
231 } srvRefs
= { NULL
, 0 };
234 register_srv(const char *realm
, const char *hostname
, int port
)
236 unsigned char target
[1024];
240 /* skip registering LKDC realms */
241 if (strncmp(realm
, "LKDC:", 5) == 0)
245 target
[0] = 0; /* priority */
246 target
[1] = 0; /* priority */
247 target
[2] = 0; /* weight */
248 target
[3] = 0; /* weigth */
249 target
[4] = (port
>> 8) & 0xff; /* port */
250 target
[5] = (port
>> 0) & 0xff; /* port */
252 size
= dn_comp(hostname
, target
+ 6, sizeof(target
) - 6, NULL
, NULL
);
258 LOG("register SRV rr for realm %s hostname %s:%d", realm
, hostname
, port
);
260 for (i
= 0; i
< sizeof(register_names
)/sizeof(register_names
[0]); i
++) {
261 char name
[kDNSServiceMaxDomainName
];
262 DNSServiceErrorType error
;
265 ptr
= realloc(srvRefs
.val
, sizeof(srvRefs
.val
[0]) * (srvRefs
.len
+ 1));
267 errx(1, "malloc: out of memory");
270 DNSServiceConstructFullName(name
, NULL
, register_names
[i
], realm
);
272 error
= DNSServiceRegisterRecord(g_dnsRef
,
273 &srvRefs
.val
[srvRefs
.len
],
274 kDNSServiceFlagsUnique
| kDNSServiceFlagsShareConnection
,
285 LOG("Failed to register SRV rr for realm %s: %d", realm
, error
);
292 unregister_srv_realms(void)
295 for (i
= 0; i
< srvRefs
.len
; i
++)
296 DNSServiceRemoveRecord(g_dnsRef
, srvRefs
.val
[i
], 0);
304 register_srv_realms(CFStringRef host
)
310 /* first unregister old names */
312 hostname
= CFString2utf8(host
);
313 if (hostname
== NULL
)
316 for(i
= 0; i
< announce_config
->num_db
; i
++) {
319 if (announce_config
->db
[i
]->hdb_get_realms
== NULL
)
322 ret
= (announce_config
->db
[i
]->hdb_get_realms
)(announce_context
, &realms
);
324 for (r
= realms
; r
&& *r
; r
++)
325 register_srv(*r
, hostname
, 88);
326 krb5_free_host_realm(announce_context
, realms
);
332 #endif /* REGISTER_SRV_RR */
337 DNSServiceErrorType error
;
338 struct entry
**e
= &g_entries
;
341 hostname
= CFString2utf8(g_hostname
);
342 if (hostname
== NULL
)
346 /* remove if this wasn't updated */
347 if (((*e
)->flags
& F_EXISTS
) == 0) {
348 struct entry
*drop
= *e
;
351 LOG("Deleting realm %s from domain %s",
352 drop
->realm
, drop
->domain
);
354 if (drop
->recordRef
&& g_dnsRef
)
355 DNSServiceRemoveRecord(g_dnsRef
, drop
->recordRef
, 0);
361 if ((*e
)->flags
& F_PUSH
) {
362 struct entry
*update
= *e
;
363 char *dnsdata
, *name
;
366 len
= strlen(update
->realm
);
367 asprintf(&dnsdata
, "%c%s", (int)len
, update
->realm
);
371 asprintf(&name
, "_kerberos.%s.%s", hostname
, update
->domain
);
375 if (update
->recordRef
)
376 DNSServiceRemoveRecord(g_dnsRef
, update
->recordRef
, 0);
378 error
= DNSServiceRegisterRecord(g_dnsRef
,
380 kDNSServiceFlagsShared
| kDNSServiceFlagsAllowRemoteQuery
,
393 errx(1, "failure to update entry for %s/%s",
394 update
->domain
, update
->realm
);
402 update_entries(SCDynamicStoreRef store
, const char *realm
, int flags
)
404 CFDictionaryRef btmm
;
406 /* we always announce in the local domain */
407 domain_add("local", realm
, F_EXISTS
| flags
);
410 btmm
= SCDynamicStoreCopyValue(store
, NetworkChangedKey_BackToMyMac
);
412 struct addctx addctx
;
414 addctx
.flags
= flags
;
415 addctx
.realm
= realm
;
417 CFDictionaryApplyFunction(btmm
, domains_add
, &addctx
);
423 update_all(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
429 LOG("something changed, running update");
431 host
= SCDynamicStoreCopyLocalHostName(store
);
435 if (g_hostname
== NULL
|| CFStringCompare(host
, g_hostname
, 0) != kCFCompareEqualTo
) {
437 CFRelease(g_hostname
);
438 g_hostname
= CFRetain(host
);
439 flags
= F_PUSH
; /* if hostname has changed, force push */
441 #ifdef REGISTER_SRV_RR
442 register_srv_realms(g_hostname
);
446 for (e
= g_entries
; e
!= NULL
; e
= e
->next
)
447 e
->flags
&= ~(F_EXISTS
|F_PUSH
);
449 for(i
= 0; i
< announce_config
->num_db
; i
++) {
453 if (announce_config
->db
[i
]->hdb_get_realms
== NULL
)
456 ret
= (announce_config
->db
[i
]->hdb_get_realms
)(announce_context
, announce_config
->db
[i
], &realms
);
458 for (r
= realms
; r
&& *r
; r
++)
459 update_entries(store
, *r
, flags
);
460 krb5_free_host_realm(announce_context
, realms
);
474 for (e
= g_entries
; e
!= NULL
; e
= e
->next
)
475 e
->flags
&= ~(F_EXISTS
|F_PUSH
);
478 if (g_entries
!= NULL
)
479 errx(1, "Failed to remove all bonjour entries");
485 if (g_dnsRef
== NULL
)
489 #ifdef REGISTER_SRV_RR
490 unregister_srv_realms();
493 DNSServiceRefDeallocate(g_dnsRef
);
498 static SCDynamicStoreRef
499 register_notification(void)
501 SCDynamicStoreRef store
;
502 CFStringRef computerNameKey
;
503 CFMutableArrayRef keys
;
505 computerNameKey
= SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault
);
507 store
= SCDynamicStoreCreate(kCFAllocatorDefault
, CFSTR("Network watcher"),
510 errx(1, "SCDynamicStoreCreate");
512 keys
= CFArrayCreateMutable(kCFAllocatorDefault
, 2, &kCFTypeArrayCallBacks
);
514 errx(1, "CFArrayCreateMutable");
516 CFArrayAppendValue(keys
, computerNameKey
);
517 CFArrayAppendValue(keys
, NetworkChangedKey_BackToMyMac
);
519 if (SCDynamicStoreSetNotificationKeys(store
, keys
, NULL
) == false)
520 errx(1, "SCDynamicStoreSetNotificationKeys");
522 CFRelease(computerNameKey
);
525 if (!SCDynamicStoreSetDispatchQueue(store
, g_queue
))
526 errx(1, "SCDynamicStoreSetDispatchQueue");
533 bonjour_announce(krb5_context context
, krb5_kdc_configuration
*config
)
535 #if defined(__APPLE__) && defined(HAVE_GCD)
536 g_queue
= dispatch_queue_create("com.apple.kdc_announce", NULL
);
538 errx(1, "dispatch_queue_create");
540 g_store
= register_notification();
541 announce_config
= config
;
542 announce_context
= context
;