xref: /freebsd/crypto/heimdal/kdc/announce.c (revision ae771770)
1*ae771770SStanislav Sedov /*
2*ae771770SStanislav Sedov  * Copyright (c) 2008 Apple Inc.  All Rights Reserved.
3*ae771770SStanislav Sedov  *
4*ae771770SStanislav Sedov  * Export of this software from the United States of America may require
5*ae771770SStanislav Sedov  * a specific license from the United States Government.  It is the
6*ae771770SStanislav Sedov  * responsibility of any person or organization contemplating export to
7*ae771770SStanislav Sedov  * obtain such a license before exporting.
8*ae771770SStanislav Sedov  *
9*ae771770SStanislav Sedov  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10*ae771770SStanislav Sedov  * distribute this software and its documentation for any purpose and
11*ae771770SStanislav Sedov  * without fee is hereby granted, provided that the above copyright
12*ae771770SStanislav Sedov  * notice appear in all copies and that both that copyright notice and
13*ae771770SStanislav Sedov  * this permission notice appear in supporting documentation, and that
14*ae771770SStanislav Sedov  * the name of Apple Inc. not be used in advertising or publicity pertaining
15*ae771770SStanislav Sedov  * to distribution of the software without specific, written prior
16*ae771770SStanislav Sedov  * permission.  Apple Inc. makes no representations about the suitability of
17*ae771770SStanislav Sedov  * this software for any purpose.  It is provided "as is" without express
18*ae771770SStanislav Sedov  * or implied warranty.
19*ae771770SStanislav Sedov  *
20*ae771770SStanislav Sedov  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
21*ae771770SStanislav Sedov  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
22*ae771770SStanislav Sedov  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23*ae771770SStanislav Sedov  *
24*ae771770SStanislav Sedov  */
25*ae771770SStanislav Sedov 
26*ae771770SStanislav Sedov #include "kdc_locl.h"
27*ae771770SStanislav Sedov 
28*ae771770SStanislav Sedov #if defined(__APPLE__) && defined(HAVE_GCD)
29*ae771770SStanislav Sedov 
30*ae771770SStanislav Sedov #include <CoreFoundation/CoreFoundation.h>
31*ae771770SStanislav Sedov #include <SystemConfiguration/SCDynamicStore.h>
32*ae771770SStanislav Sedov #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
33*ae771770SStanislav Sedov #include <SystemConfiguration/SCDynamicStoreKey.h>
34*ae771770SStanislav Sedov 
35*ae771770SStanislav Sedov #include <dispatch/dispatch.h>
36*ae771770SStanislav Sedov 
37*ae771770SStanislav Sedov #include <asl.h>
38*ae771770SStanislav Sedov #include <resolv.h>
39*ae771770SStanislav Sedov 
40*ae771770SStanislav Sedov #include <dns_sd.h>
41*ae771770SStanislav Sedov #include <err.h>
42*ae771770SStanislav Sedov 
43*ae771770SStanislav Sedov static krb5_kdc_configuration *announce_config;
44*ae771770SStanislav Sedov static krb5_context announce_context;
45*ae771770SStanislav Sedov 
46*ae771770SStanislav Sedov struct entry {
47*ae771770SStanislav Sedov     DNSRecordRef recordRef;
48*ae771770SStanislav Sedov     char *domain;
49*ae771770SStanislav Sedov     char *realm;
50*ae771770SStanislav Sedov #define F_EXISTS 1
51*ae771770SStanislav Sedov #define F_PUSH 2
52*ae771770SStanislav Sedov     int flags;
53*ae771770SStanislav Sedov     struct entry *next;
54*ae771770SStanislav Sedov };
55*ae771770SStanislav Sedov 
56*ae771770SStanislav Sedov /* #define REGISTER_SRV_RR */
57*ae771770SStanislav Sedov 
58*ae771770SStanislav Sedov static struct entry *g_entries = NULL;
59*ae771770SStanislav Sedov static CFStringRef g_hostname = NULL;
60*ae771770SStanislav Sedov static DNSServiceRef g_dnsRef = NULL;
61*ae771770SStanislav Sedov static SCDynamicStoreRef g_store = NULL;
62*ae771770SStanislav Sedov static dispatch_queue_t g_queue = NULL;
63*ae771770SStanislav Sedov 
64*ae771770SStanislav Sedov #define LOG(...) asl_log(NULL, NULL, ASL_LEVEL_INFO, __VA_ARGS__)
65*ae771770SStanislav Sedov 
66*ae771770SStanislav Sedov static void create_dns_sd(void);
67*ae771770SStanislav Sedov static void destroy_dns_sd(void);
68*ae771770SStanislav Sedov static void update_all(SCDynamicStoreRef, CFArrayRef, void *);
69*ae771770SStanislav Sedov 
70*ae771770SStanislav Sedov 
71*ae771770SStanislav Sedov /* parameters */
72*ae771770SStanislav Sedov static CFStringRef NetworkChangedKey_BackToMyMac = CFSTR("Setup:/Network/BackToMyMac");
73*ae771770SStanislav Sedov 
74*ae771770SStanislav Sedov 
75*ae771770SStanislav Sedov static char *
CFString2utf8(CFStringRef string)76*ae771770SStanislav Sedov CFString2utf8(CFStringRef string)
77*ae771770SStanislav Sedov {
78*ae771770SStanislav Sedov     size_t size;
79*ae771770SStanislav Sedov     char *str;
80*ae771770SStanislav Sedov 
81*ae771770SStanislav Sedov     size = 1 + CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8);
82*ae771770SStanislav Sedov     str = malloc(size);
83*ae771770SStanislav Sedov     if (str == NULL)
84*ae771770SStanislav Sedov 	return NULL;
85*ae771770SStanislav Sedov 
86*ae771770SStanislav Sedov     if (CFStringGetCString(string, str, size, kCFStringEncodingUTF8) == false) {
87*ae771770SStanislav Sedov 	free(str);
88*ae771770SStanislav Sedov 	return NULL;
89*ae771770SStanislav Sedov     }
90*ae771770SStanislav Sedov     return str;
91*ae771770SStanislav Sedov }
92*ae771770SStanislav Sedov 
93*ae771770SStanislav Sedov /*
94*ae771770SStanislav Sedov  *
95*ae771770SStanislav Sedov  */
96*ae771770SStanislav Sedov 
97*ae771770SStanislav Sedov static void
retry_timer(void)98*ae771770SStanislav Sedov retry_timer(void)
99*ae771770SStanislav Sedov {
100*ae771770SStanislav Sedov     dispatch_source_t s;
101*ae771770SStanislav Sedov     dispatch_time_t t;
102*ae771770SStanislav Sedov 
103*ae771770SStanislav Sedov     s = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
104*ae771770SStanislav Sedov 			       0, 0, g_queue);
105*ae771770SStanislav Sedov     t = dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC);
106*ae771770SStanislav Sedov     dispatch_source_set_timer(s, t, 0, NSEC_PER_SEC);
107*ae771770SStanislav Sedov     dispatch_source_set_event_handler(s, ^{
108*ae771770SStanislav Sedov 	    create_dns_sd();
109*ae771770SStanislav Sedov 	    dispatch_release(s);
110*ae771770SStanislav Sedov 	});
111*ae771770SStanislav Sedov     dispatch_resume(s);
112*ae771770SStanislav Sedov }
113*ae771770SStanislav Sedov 
114*ae771770SStanislav Sedov /*
115*ae771770SStanislav Sedov  *
116*ae771770SStanislav Sedov  */
117*ae771770SStanislav Sedov 
118*ae771770SStanislav Sedov static void
create_dns_sd(void)119*ae771770SStanislav Sedov create_dns_sd(void)
120*ae771770SStanislav Sedov {
121*ae771770SStanislav Sedov     DNSServiceErrorType error;
122*ae771770SStanislav Sedov     dispatch_source_t s;
123*ae771770SStanislav Sedov 
124*ae771770SStanislav Sedov     error = DNSServiceCreateConnection(&g_dnsRef);
125*ae771770SStanislav Sedov     if (error) {
126*ae771770SStanislav Sedov 	retry_timer();
127*ae771770SStanislav Sedov 	return;
128*ae771770SStanislav Sedov     }
129*ae771770SStanislav Sedov 
130*ae771770SStanislav Sedov     dispatch_suspend(g_queue);
131*ae771770SStanislav Sedov 
132*ae771770SStanislav Sedov     s = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
133*ae771770SStanislav Sedov 			       DNSServiceRefSockFD(g_dnsRef),
134*ae771770SStanislav Sedov 			       0, g_queue);
135*ae771770SStanislav Sedov 
136*ae771770SStanislav Sedov     dispatch_source_set_event_handler(s, ^{
137*ae771770SStanislav Sedov 	    DNSServiceErrorType ret = DNSServiceProcessResult(g_dnsRef);
138*ae771770SStanislav Sedov 	    /* on error tear down and set timer to recreate */
139*ae771770SStanislav Sedov 	    if (ret != kDNSServiceErr_NoError && ret != kDNSServiceErr_Transient) {
140*ae771770SStanislav Sedov 		dispatch_source_cancel(s);
141*ae771770SStanislav Sedov 	    }
142*ae771770SStanislav Sedov 	});
143*ae771770SStanislav Sedov 
144*ae771770SStanislav Sedov     dispatch_source_set_cancel_handler(s, ^{
145*ae771770SStanislav Sedov 	    destroy_dns_sd();
146*ae771770SStanislav Sedov 	    retry_timer();
147*ae771770SStanislav Sedov 	    dispatch_release(s);
148*ae771770SStanislav Sedov 	});
149*ae771770SStanislav Sedov 
150*ae771770SStanislav Sedov     dispatch_resume(s);
151*ae771770SStanislav Sedov 
152*ae771770SStanislav Sedov     /* Do the first update ourself */
153*ae771770SStanislav Sedov     update_all(g_store, NULL, NULL);
154*ae771770SStanislav Sedov     dispatch_resume(g_queue);
155*ae771770SStanislav Sedov }
156*ae771770SStanislav Sedov 
157*ae771770SStanislav Sedov static void
domain_add(const char * domain,const char * realm,int flag)158*ae771770SStanislav Sedov domain_add(const char *domain, const char *realm, int flag)
159*ae771770SStanislav Sedov {
160*ae771770SStanislav Sedov     struct entry *e;
161*ae771770SStanislav Sedov 
162*ae771770SStanislav Sedov     for (e = g_entries; e != NULL; e = e->next) {
163*ae771770SStanislav Sedov 	if (strcmp(domain, e->domain) == 0 && strcmp(realm, e->realm) == 0) {
164*ae771770SStanislav Sedov 	    e->flags |= flag;
165*ae771770SStanislav Sedov 	    return;
166*ae771770SStanislav Sedov 	}
167*ae771770SStanislav Sedov     }
168*ae771770SStanislav Sedov 
169*ae771770SStanislav Sedov     LOG("Adding realm %s to domain %s", realm, domain);
170*ae771770SStanislav Sedov 
171*ae771770SStanislav Sedov     e = calloc(1, sizeof(*e));
172*ae771770SStanislav Sedov     if (e == NULL)
173*ae771770SStanislav Sedov 	return;
174*ae771770SStanislav Sedov     e->domain = strdup(domain);
175*ae771770SStanislav Sedov     e->realm = strdup(realm);
176*ae771770SStanislav Sedov     if (e->domain == NULL || e->realm == NULL) {
177*ae771770SStanislav Sedov 	free(e->domain);
178*ae771770SStanislav Sedov 	free(e->realm);
179*ae771770SStanislav Sedov 	free(e);
180*ae771770SStanislav Sedov 	return;
181*ae771770SStanislav Sedov     }
182*ae771770SStanislav Sedov     e->flags = flag | F_PUSH; /* if we allocate, we push */
183*ae771770SStanislav Sedov     e->next = g_entries;
184*ae771770SStanislav Sedov     g_entries = e;
185*ae771770SStanislav Sedov }
186*ae771770SStanislav Sedov 
187*ae771770SStanislav Sedov struct addctx {
188*ae771770SStanislav Sedov     int flags;
189*ae771770SStanislav Sedov     const char *realm;
190*ae771770SStanislav Sedov };
191*ae771770SStanislav Sedov 
192*ae771770SStanislav Sedov static void
domains_add(const void * key,const void * value,void * context)193*ae771770SStanislav Sedov domains_add(const void *key, const void *value, void *context)
194*ae771770SStanislav Sedov {
195*ae771770SStanislav Sedov     char *str = CFString2utf8((CFStringRef)value);
196*ae771770SStanislav Sedov     struct addctx *ctx = context;
197*ae771770SStanislav Sedov 
198*ae771770SStanislav Sedov     if (str == NULL)
199*ae771770SStanislav Sedov 	return;
200*ae771770SStanislav Sedov     if (str[0] != '\0')
201*ae771770SStanislav Sedov 	domain_add(str, ctx->realm, F_EXISTS | ctx->flags);
202*ae771770SStanislav Sedov     free(str);
203*ae771770SStanislav Sedov }
204*ae771770SStanislav Sedov 
205*ae771770SStanislav Sedov 
206*ae771770SStanislav Sedov static void
dnsCallback(DNSServiceRef sdRef,DNSRecordRef RecordRef,DNSServiceFlags flags,DNSServiceErrorType errorCode,void * context)207*ae771770SStanislav Sedov dnsCallback(DNSServiceRef sdRef __attribute__((unused)),
208*ae771770SStanislav Sedov 	    DNSRecordRef RecordRef __attribute__((unused)),
209*ae771770SStanislav Sedov 	    DNSServiceFlags flags __attribute__((unused)),
210*ae771770SStanislav Sedov 	    DNSServiceErrorType errorCode __attribute__((unused)),
211*ae771770SStanislav Sedov 	    void *context __attribute__((unused)))
212*ae771770SStanislav Sedov {
213*ae771770SStanislav Sedov }
214*ae771770SStanislav Sedov 
215*ae771770SStanislav Sedov #ifdef REGISTER_SRV_RR
216*ae771770SStanislav Sedov 
217*ae771770SStanislav Sedov /*
218*ae771770SStanislav Sedov  * Register DNS SRV rr for the realm.
219*ae771770SStanislav Sedov  */
220*ae771770SStanislav Sedov 
221*ae771770SStanislav Sedov static const char *register_names[2] = {
222*ae771770SStanislav Sedov     "_kerberos._tcp",
223*ae771770SStanislav Sedov     "_kerberos._udp"
224*ae771770SStanislav Sedov };
225*ae771770SStanislav Sedov 
226*ae771770SStanislav Sedov static struct {
227*ae771770SStanislav Sedov     DNSRecordRef *val;
228*ae771770SStanislav Sedov     size_t len;
229*ae771770SStanislav Sedov } srvRefs = { NULL, 0 };
230*ae771770SStanislav Sedov 
231*ae771770SStanislav Sedov static void
register_srv(const char * realm,const char * hostname,int port)232*ae771770SStanislav Sedov register_srv(const char *realm, const char *hostname, int port)
233*ae771770SStanislav Sedov {
234*ae771770SStanislav Sedov     unsigned char target[1024];
235*ae771770SStanislav Sedov     int i;
236*ae771770SStanislav Sedov     int size;
237*ae771770SStanislav Sedov 
238*ae771770SStanislav Sedov     /* skip registering LKDC realms */
239*ae771770SStanislav Sedov     if (strncmp(realm, "LKDC:", 5) == 0)
240*ae771770SStanislav Sedov 	return;
241*ae771770SStanislav Sedov 
242*ae771770SStanislav Sedov     /* encode SRV-RR */
243*ae771770SStanislav Sedov     target[0] = 0; /* priority */
244*ae771770SStanislav Sedov     target[1] = 0; /* priority */
245*ae771770SStanislav Sedov     target[2] = 0; /* weight */
246*ae771770SStanislav Sedov     target[3] = 0; /* weigth */
247*ae771770SStanislav Sedov     target[4] = (port >> 8) & 0xff; /* port */
248*ae771770SStanislav Sedov     target[5] = (port >> 0) & 0xff; /* port */
249*ae771770SStanislav Sedov 
250*ae771770SStanislav Sedov     size = dn_comp(hostname, target + 6, sizeof(target) - 6, NULL, NULL);
251*ae771770SStanislav Sedov     if (size < 0)
252*ae771770SStanislav Sedov 	return;
253*ae771770SStanislav Sedov 
254*ae771770SStanislav Sedov     size += 6;
255*ae771770SStanislav Sedov 
256*ae771770SStanislav Sedov     LOG("register SRV rr for realm %s hostname %s:%d", realm, hostname, port);
257*ae771770SStanislav Sedov 
258*ae771770SStanislav Sedov     for (i = 0; i < sizeof(register_names)/sizeof(register_names[0]); i++) {
259*ae771770SStanislav Sedov 	char name[kDNSServiceMaxDomainName];
260*ae771770SStanislav Sedov 	DNSServiceErrorType error;
261*ae771770SStanislav Sedov 	void *ptr;
262*ae771770SStanislav Sedov 
263*ae771770SStanislav Sedov 	ptr = realloc(srvRefs.val, sizeof(srvRefs.val[0]) * (srvRefs.len + 1));
264*ae771770SStanislav Sedov 	if (ptr == NULL)
265*ae771770SStanislav Sedov 	    errx(1, "malloc: out of memory");
266*ae771770SStanislav Sedov 	srvRefs.val = ptr;
267*ae771770SStanislav Sedov 
268*ae771770SStanislav Sedov 	DNSServiceConstructFullName(name, NULL, register_names[i], realm);
269*ae771770SStanislav Sedov 
270*ae771770SStanislav Sedov 	error = DNSServiceRegisterRecord(g_dnsRef,
271*ae771770SStanislav Sedov 					 &srvRefs.val[srvRefs.len],
272*ae771770SStanislav Sedov 					 kDNSServiceFlagsUnique | kDNSServiceFlagsShareConnection,
273*ae771770SStanislav Sedov 					 0,
274*ae771770SStanislav Sedov 					 name,
275*ae771770SStanislav Sedov 					 kDNSServiceType_SRV,
276*ae771770SStanislav Sedov 					 kDNSServiceClass_IN,
277*ae771770SStanislav Sedov 					 size,
278*ae771770SStanislav Sedov 					 target,
279*ae771770SStanislav Sedov 					 0,
280*ae771770SStanislav Sedov 					 dnsCallback,
281*ae771770SStanislav Sedov 					 NULL);
282*ae771770SStanislav Sedov 	if (error) {
283*ae771770SStanislav Sedov 	    LOG("Failed to register SRV rr for realm %s: %d", realm, error);
284*ae771770SStanislav Sedov 	} else
285*ae771770SStanislav Sedov 	    srvRefs.len++;
286*ae771770SStanislav Sedov     }
287*ae771770SStanislav Sedov }
288*ae771770SStanislav Sedov 
289*ae771770SStanislav Sedov static void
unregister_srv_realms(void)290*ae771770SStanislav Sedov unregister_srv_realms(void)
291*ae771770SStanislav Sedov {
292*ae771770SStanislav Sedov     if (g_dnsRef) {
293*ae771770SStanislav Sedov 	for (i = 0; i < srvRefs.len; i++)
294*ae771770SStanislav Sedov 	    DNSServiceRemoveRecord(g_dnsRef, srvRefs.val[i], 0);
295*ae771770SStanislav Sedov     }
296*ae771770SStanislav Sedov     free(srvRefs.val);
297*ae771770SStanislav Sedov     srvRefs.len = 0;
298*ae771770SStanislav Sedov     srvRefs.val = NULL;
299*ae771770SStanislav Sedov }
300*ae771770SStanislav Sedov 
301*ae771770SStanislav Sedov static void
register_srv_realms(CFStringRef host)302*ae771770SStanislav Sedov register_srv_realms(CFStringRef host)
303*ae771770SStanislav Sedov {
304*ae771770SStanislav Sedov     krb5_error_code ret;
305*ae771770SStanislav Sedov     char *hostname;
306*ae771770SStanislav Sedov     size_t i;
307*ae771770SStanislav Sedov 
308*ae771770SStanislav Sedov     /* first unregister old names */
309*ae771770SStanislav Sedov 
310*ae771770SStanislav Sedov     hostname = CFString2utf8(host);
311*ae771770SStanislav Sedov     if (hostname == NULL)
312*ae771770SStanislav Sedov 	return;
313*ae771770SStanislav Sedov 
314*ae771770SStanislav Sedov     for(i = 0; i < announce_config->num_db; i++) {
315*ae771770SStanislav Sedov 	char **realms, **r;
316*ae771770SStanislav Sedov 
317*ae771770SStanislav Sedov 	if (announce_config->db[i]->hdb_get_realms == NULL)
318*ae771770SStanislav Sedov 	    continue;
319*ae771770SStanislav Sedov 
320*ae771770SStanislav Sedov 	ret = (announce_config->db[i]->hdb_get_realms)(announce_context, &realms);
321*ae771770SStanislav Sedov 	if (ret == 0) {
322*ae771770SStanislav Sedov 	    for (r = realms; r && *r; r++)
323*ae771770SStanislav Sedov 		register_srv(*r, hostname, 88);
324*ae771770SStanislav Sedov 	    krb5_free_host_realm(announce_context, realms);
325*ae771770SStanislav Sedov 	}
326*ae771770SStanislav Sedov     }
327*ae771770SStanislav Sedov 
328*ae771770SStanislav Sedov     free(hostname);
329*ae771770SStanislav Sedov }
330*ae771770SStanislav Sedov #endif /* REGISTER_SRV_RR */
331*ae771770SStanislav Sedov 
332*ae771770SStanislav Sedov static void
update_dns(void)333*ae771770SStanislav Sedov update_dns(void)
334*ae771770SStanislav Sedov {
335*ae771770SStanislav Sedov     DNSServiceErrorType error;
336*ae771770SStanislav Sedov     struct entry **e = &g_entries;
337*ae771770SStanislav Sedov     char *hostname;
338*ae771770SStanislav Sedov 
339*ae771770SStanislav Sedov     hostname = CFString2utf8(g_hostname);
340*ae771770SStanislav Sedov     if (hostname == NULL)
341*ae771770SStanislav Sedov 	return;
342*ae771770SStanislav Sedov 
343*ae771770SStanislav Sedov     while (*e != NULL) {
344*ae771770SStanislav Sedov 	/* remove if this wasn't updated */
345*ae771770SStanislav Sedov 	if (((*e)->flags & F_EXISTS) == 0) {
346*ae771770SStanislav Sedov 	    struct entry *drop = *e;
347*ae771770SStanislav Sedov 	    *e = (*e)->next;
348*ae771770SStanislav Sedov 
349*ae771770SStanislav Sedov 	    LOG("Deleting realm %s from domain %s",
350*ae771770SStanislav Sedov 		drop->realm, drop->domain);
351*ae771770SStanislav Sedov 
352*ae771770SStanislav Sedov 	    if (drop->recordRef && g_dnsRef)
353*ae771770SStanislav Sedov 		DNSServiceRemoveRecord(g_dnsRef, drop->recordRef, 0);
354*ae771770SStanislav Sedov 	    free(drop->domain);
355*ae771770SStanislav Sedov 	    free(drop->realm);
356*ae771770SStanislav Sedov 	    free(drop);
357*ae771770SStanislav Sedov 	    continue;
358*ae771770SStanislav Sedov 	}
359*ae771770SStanislav Sedov 	if ((*e)->flags & F_PUSH) {
360*ae771770SStanislav Sedov 	    struct entry *update = *e;
361*ae771770SStanislav Sedov 	    char *dnsdata, *name;
362*ae771770SStanislav Sedov 	    size_t len;
363*ae771770SStanislav Sedov 
364*ae771770SStanislav Sedov 	    len = strlen(update->realm);
365*ae771770SStanislav Sedov 	    asprintf(&dnsdata, "%c%s", (int)len, update->realm);
366*ae771770SStanislav Sedov 	    if (dnsdata == NULL)
367*ae771770SStanislav Sedov 		errx(1, "malloc");
368*ae771770SStanislav Sedov 
369*ae771770SStanislav Sedov 	    asprintf(&name, "_kerberos.%s.%s", hostname, update->domain);
370*ae771770SStanislav Sedov 	    if (name == NULL)
371*ae771770SStanislav Sedov 		errx(1, "malloc");
372*ae771770SStanislav Sedov 
373*ae771770SStanislav Sedov 	    if (update->recordRef)
374*ae771770SStanislav Sedov 		DNSServiceRemoveRecord(g_dnsRef, update->recordRef, 0);
375*ae771770SStanislav Sedov 
376*ae771770SStanislav Sedov 	    error = DNSServiceRegisterRecord(g_dnsRef,
377*ae771770SStanislav Sedov 					     &update->recordRef,
378*ae771770SStanislav Sedov 					     kDNSServiceFlagsShared | kDNSServiceFlagsAllowRemoteQuery,
379*ae771770SStanislav Sedov 					     0,
380*ae771770SStanislav Sedov 					     name,
381*ae771770SStanislav Sedov 					     kDNSServiceType_TXT,
382*ae771770SStanislav Sedov 					     kDNSServiceClass_IN,
383*ae771770SStanislav Sedov 					     len+1,
384*ae771770SStanislav Sedov 					     dnsdata,
385*ae771770SStanislav Sedov 					     0,
386*ae771770SStanislav Sedov 					     dnsCallback,
387*ae771770SStanislav Sedov 					     NULL);
388*ae771770SStanislav Sedov 	    free(name);
389*ae771770SStanislav Sedov 	    free(dnsdata);
390*ae771770SStanislav Sedov 	    if (error)
391*ae771770SStanislav Sedov 		errx(1, "failure to update entry for %s/%s",
392*ae771770SStanislav Sedov 		     update->domain, update->realm);
393*ae771770SStanislav Sedov 	}
394*ae771770SStanislav Sedov 	e = &(*e)->next;
395*ae771770SStanislav Sedov     }
396*ae771770SStanislav Sedov     free(hostname);
397*ae771770SStanislav Sedov }
398*ae771770SStanislav Sedov 
399*ae771770SStanislav Sedov static void
update_entries(SCDynamicStoreRef store,const char * realm,int flags)400*ae771770SStanislav Sedov update_entries(SCDynamicStoreRef store, const char *realm, int flags)
401*ae771770SStanislav Sedov {
402*ae771770SStanislav Sedov     CFDictionaryRef btmm;
403*ae771770SStanislav Sedov 
404*ae771770SStanislav Sedov     /* we always announce in the local domain */
405*ae771770SStanislav Sedov     domain_add("local", realm, F_EXISTS | flags);
406*ae771770SStanislav Sedov 
407*ae771770SStanislav Sedov     /* announce btmm */
408*ae771770SStanislav Sedov     btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac);
409*ae771770SStanislav Sedov     if (btmm) {
410*ae771770SStanislav Sedov 	struct addctx addctx;
411*ae771770SStanislav Sedov 
412*ae771770SStanislav Sedov 	addctx.flags = flags;
413*ae771770SStanislav Sedov 	addctx.realm = realm;
414*ae771770SStanislav Sedov 
415*ae771770SStanislav Sedov 	CFDictionaryApplyFunction(btmm, domains_add, &addctx);
416*ae771770SStanislav Sedov 	CFRelease(btmm);
417*ae771770SStanislav Sedov     }
418*ae771770SStanislav Sedov }
419*ae771770SStanislav Sedov 
420*ae771770SStanislav Sedov static void
update_all(SCDynamicStoreRef store,CFArrayRef changedKeys,void * info)421*ae771770SStanislav Sedov update_all(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
422*ae771770SStanislav Sedov {
423*ae771770SStanislav Sedov     struct entry *e;
424*ae771770SStanislav Sedov     CFStringRef host;
425*ae771770SStanislav Sedov     int i, flags = 0;
426*ae771770SStanislav Sedov 
427*ae771770SStanislav Sedov     LOG("something changed, running update");
428*ae771770SStanislav Sedov 
429*ae771770SStanislav Sedov     host = SCDynamicStoreCopyLocalHostName(store);
430*ae771770SStanislav Sedov     if (host == NULL)
431*ae771770SStanislav Sedov 	return;
432*ae771770SStanislav Sedov 
433*ae771770SStanislav Sedov     if (g_hostname == NULL || CFStringCompare(host, g_hostname, 0) != kCFCompareEqualTo) {
434*ae771770SStanislav Sedov 	if (g_hostname)
435*ae771770SStanislav Sedov 	    CFRelease(g_hostname);
436*ae771770SStanislav Sedov 	g_hostname = CFRetain(host);
437*ae771770SStanislav Sedov 	flags = F_PUSH; /* if hostname has changed, force push */
438*ae771770SStanislav Sedov 
439*ae771770SStanislav Sedov #ifdef REGISTER_SRV_RR
440*ae771770SStanislav Sedov 	register_srv_realms(g_hostname);
441*ae771770SStanislav Sedov #endif
442*ae771770SStanislav Sedov     }
443*ae771770SStanislav Sedov 
444*ae771770SStanislav Sedov     for (e = g_entries; e != NULL; e = e->next)
445*ae771770SStanislav Sedov 	e->flags &= ~(F_EXISTS|F_PUSH);
446*ae771770SStanislav Sedov 
447*ae771770SStanislav Sedov     for(i = 0; i < announce_config->num_db; i++) {
448*ae771770SStanislav Sedov 	krb5_error_code ret;
449*ae771770SStanislav Sedov 	char **realms, **r;
450*ae771770SStanislav Sedov 
451*ae771770SStanislav Sedov 	if (announce_config->db[i]->hdb_get_realms == NULL)
452*ae771770SStanislav Sedov 	    continue;
453*ae771770SStanislav Sedov 
454*ae771770SStanislav Sedov 	ret = (announce_config->db[i]->hdb_get_realms)(announce_context, announce_config->db[i], &realms);
455*ae771770SStanislav Sedov 	if (ret == 0) {
456*ae771770SStanislav Sedov 	    for (r = realms; r && *r; r++)
457*ae771770SStanislav Sedov 		update_entries(store, *r, flags);
458*ae771770SStanislav Sedov 	    krb5_free_host_realm(announce_context, realms);
459*ae771770SStanislav Sedov 	}
460*ae771770SStanislav Sedov     }
461*ae771770SStanislav Sedov 
462*ae771770SStanislav Sedov     update_dns();
463*ae771770SStanislav Sedov 
464*ae771770SStanislav Sedov     CFRelease(host);
465*ae771770SStanislav Sedov }
466*ae771770SStanislav Sedov 
467*ae771770SStanislav Sedov static void
delete_all(void)468*ae771770SStanislav Sedov delete_all(void)
469*ae771770SStanislav Sedov {
470*ae771770SStanislav Sedov     struct entry *e;
471*ae771770SStanislav Sedov 
472*ae771770SStanislav Sedov     for (e = g_entries; e != NULL; e = e->next)
473*ae771770SStanislav Sedov 	e->flags &= ~(F_EXISTS|F_PUSH);
474*ae771770SStanislav Sedov 
475*ae771770SStanislav Sedov     update_dns();
476*ae771770SStanislav Sedov     if (g_entries != NULL)
477*ae771770SStanislav Sedov 	errx(1, "Failed to remove all bonjour entries");
478*ae771770SStanislav Sedov }
479*ae771770SStanislav Sedov 
480*ae771770SStanislav Sedov static void
destroy_dns_sd(void)481*ae771770SStanislav Sedov destroy_dns_sd(void)
482*ae771770SStanislav Sedov {
483*ae771770SStanislav Sedov     if (g_dnsRef == NULL)
484*ae771770SStanislav Sedov 	return;
485*ae771770SStanislav Sedov 
486*ae771770SStanislav Sedov     delete_all();
487*ae771770SStanislav Sedov #ifdef REGISTER_SRV_RR
488*ae771770SStanislav Sedov     unregister_srv_realms();
489*ae771770SStanislav Sedov #endif
490*ae771770SStanislav Sedov 
491*ae771770SStanislav Sedov     DNSServiceRefDeallocate(g_dnsRef);
492*ae771770SStanislav Sedov     g_dnsRef = NULL;
493*ae771770SStanislav Sedov }
494*ae771770SStanislav Sedov 
495*ae771770SStanislav Sedov 
496*ae771770SStanislav Sedov static SCDynamicStoreRef
register_notification(void)497*ae771770SStanislav Sedov register_notification(void)
498*ae771770SStanislav Sedov {
499*ae771770SStanislav Sedov     SCDynamicStoreRef store;
500*ae771770SStanislav Sedov     CFStringRef computerNameKey;
501*ae771770SStanislav Sedov     CFMutableArrayRef keys;
502*ae771770SStanislav Sedov 
503*ae771770SStanislav Sedov     computerNameKey = SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault);
504*ae771770SStanislav Sedov 
505*ae771770SStanislav Sedov     store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Network watcher"),
506*ae771770SStanislav Sedov 				 update_all, NULL);
507*ae771770SStanislav Sedov     if (store == NULL)
508*ae771770SStanislav Sedov 	errx(1, "SCDynamicStoreCreate");
509*ae771770SStanislav Sedov 
510*ae771770SStanislav Sedov     keys = CFArrayCreateMutable(kCFAllocatorDefault, 2, &kCFTypeArrayCallBacks);
511*ae771770SStanislav Sedov     if (keys == NULL)
512*ae771770SStanislav Sedov 	errx(1, "CFArrayCreateMutable");
513*ae771770SStanislav Sedov 
514*ae771770SStanislav Sedov     CFArrayAppendValue(keys, computerNameKey);
515*ae771770SStanislav Sedov     CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac);
516*ae771770SStanislav Sedov 
517*ae771770SStanislav Sedov     if (SCDynamicStoreSetNotificationKeys(store, keys, NULL) == false)
518*ae771770SStanislav Sedov 	errx(1, "SCDynamicStoreSetNotificationKeys");
519*ae771770SStanislav Sedov 
520*ae771770SStanislav Sedov     CFRelease(computerNameKey);
521*ae771770SStanislav Sedov     CFRelease(keys);
522*ae771770SStanislav Sedov 
523*ae771770SStanislav Sedov     if (!SCDynamicStoreSetDispatchQueue(store, g_queue))
524*ae771770SStanislav Sedov 	errx(1, "SCDynamicStoreSetDispatchQueue");
525*ae771770SStanislav Sedov 
526*ae771770SStanislav Sedov     return store;
527*ae771770SStanislav Sedov }
528*ae771770SStanislav Sedov #endif
529*ae771770SStanislav Sedov 
530*ae771770SStanislav Sedov void
bonjour_announce(krb5_context context,krb5_kdc_configuration * config)531*ae771770SStanislav Sedov bonjour_announce(krb5_context context, krb5_kdc_configuration *config)
532*ae771770SStanislav Sedov {
533*ae771770SStanislav Sedov #if defined(__APPLE__) && defined(HAVE_GCD)
534*ae771770SStanislav Sedov     g_queue = dispatch_queue_create("com.apple.kdc_announce", NULL);
535*ae771770SStanislav Sedov     if (!g_queue)
536*ae771770SStanislav Sedov 	errx(1, "dispatch_queue_create");
537*ae771770SStanislav Sedov 
538*ae771770SStanislav Sedov     g_store = register_notification();
539*ae771770SStanislav Sedov     announce_config = config;
540*ae771770SStanislav Sedov     announce_context = context;
541*ae771770SStanislav Sedov 
542*ae771770SStanislav Sedov     create_dns_sd();
543*ae771770SStanislav Sedov #endif
544*ae771770SStanislav Sedov }
545