1 /*
2  *
3  * Purple is the legal property of its developers, whose names are too numerous
4  * to list here.  Please refer to the COPYRIGHT file distributed with this
5  * source distribution.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
20  */
21 
22 #include "internal.h"
23 #include "debug.h"
24 
25 #include "buddy.h"
26 #include "mdns_interface.h"
27 #ifdef __APPLE_CC__
28 #include <dns_sd.h>
29 #else
30 #include "dns_sd_proxy.h"
31 #endif
32 #include "mdns_common.h"
33 #include "bonjour.h"
34 
35 static GSList *pending_buddies = NULL;
36 
37 typedef struct _dnssd_service_ref_handler {
38 	DNSServiceRef sdRef;
39 	PurpleAccount *account;
40 	guint input_handler;
41 } DnsSDServiceRefHandlerData;
42 
43 /* data used by win32 bonjour implementation */
44 typedef struct _win32_session_impl_data {
45 	DnsSDServiceRefHandlerData *presence_query;
46 	DnsSDServiceRefHandlerData *browser_query;
47 	DNSRecordRef buddy_icon_rec;
48 } Win32SessionImplData;
49 
50 typedef struct _win32_buddy_service_resolver_data {
51 	DnsSDServiceRefHandlerData *txt_query;
52 	uint32_t if_idx;
53 	gchar *name;
54 	gchar *type;
55 	gchar *domain;
56 	/* This is a reference to the entry in BonjourBuddy->ips */
57 	const char *ip;
58 } Win32SvcResolverData;
59 
60 typedef struct _win32_buddy_impl_data {
61 	GSList *resolvers;
62 	DnsSDServiceRefHandlerData *null_query;
63 } Win32BuddyImplData;
64 
65 /* data structure for the resolve callback */
66 typedef struct _ResolveCallbackArgs {
67 	DnsSDServiceRefHandlerData *resolver_query;
68 	PurpleAccount *account;
69 	BonjourBuddy *bb;
70 	Win32SvcResolverData *res_data;
71 	gchar *full_service_name;
72 } ResolveCallbackArgs;
73 
74 
75 static gint
_find_resolver_data(gconstpointer a,gconstpointer b)76 _find_resolver_data(gconstpointer a, gconstpointer b) {
77 	const Win32SvcResolverData *rd_a = a;
78 	const Win32SvcResolverData *rd_b = b;
79 	gint ret = 1;
80 
81 	if(rd_a->if_idx == rd_b->if_idx
82 			&& purple_strequal(rd_a->name, rd_b->name)
83 			&& purple_strequal(rd_a->type, rd_b->type)
84 			&& purple_strequal(rd_a->domain, rd_b->domain)) {
85 		ret = 0;
86 	}
87 
88 	return ret;
89 }
90 
91 static void
_cleanup_resolver_data(Win32SvcResolverData * rd)92 _cleanup_resolver_data(Win32SvcResolverData *rd) {
93 	if (rd->txt_query != NULL) {
94 		purple_input_remove(rd->txt_query->input_handler);
95 		DNSServiceRefDeallocate(rd->txt_query->sdRef);
96 		g_free(rd->txt_query);
97 	}
98 	g_free(rd->name);
99 	g_free(rd->type);
100 	g_free(rd->domain);
101 	g_free(rd);
102 }
103 
104 static void
_mdns_handle_event(gpointer data,gint source,PurpleInputCondition condition)105 _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) {
106 	DnsSDServiceRefHandlerData *srh = data;
107 	DNSServiceErrorType errorCode = DNSServiceProcessResult(srh->sdRef);
108 	if (errorCode != kDNSServiceErr_NoError) {
109 		purple_debug_error("bonjour", "Error (%d) handling mDNS response.\n", errorCode);
110 		/* This happens when the mDNSResponder goes down, I haven't seen it happen any other time (in my limited testing) */
111 		if (errorCode == kDNSServiceErr_Unknown) {
112 			purple_connection_error_reason(srh->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
113 				_("Error communicating with local mDNSResponder."));
114 		}
115 	}
116 }
117 
118 static void
_mdns_parse_text_record(BonjourBuddy * buddy,const char * record,uint16_t record_len)119 _mdns_parse_text_record(BonjourBuddy *buddy, const char *record, uint16_t record_len)
120 {
121 	const char *txt_entry;
122 	uint8_t txt_len;
123 	int i;
124 
125 	clear_bonjour_buddy_values(buddy);
126 	for (i = 0; buddy_TXT_records[i] != NULL; i++) {
127 		txt_entry = TXTRecordGetValuePtr(record_len, record, buddy_TXT_records[i], &txt_len);
128 		if (txt_entry != NULL)
129 			set_bonjour_buddy_value(buddy, buddy_TXT_records[i], txt_entry, txt_len);
130 	}
131 }
132 
133 static void DNSSD_API
_mdns_record_query_callback(DNSServiceRef DNSServiceRef,DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * fullname,uint16_t rrtype,uint16_t rrclass,uint16_t rdlen,const void * rdata,uint32_t ttl,void * context)134 _mdns_record_query_callback(DNSServiceRef DNSServiceRef, DNSServiceFlags flags,
135 	uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname,
136 	uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata,
137 	uint32_t ttl, void *context)
138 {
139 
140 	if (errorCode != kDNSServiceErr_NoError) {
141 		purple_debug_error("bonjour", "record query - callback error (%d).\n", errorCode);
142 		/* TODO: Probably should remove the buddy when this happens */
143 	} else if (flags & kDNSServiceFlagsAdd) {
144 		if (rrtype == kDNSServiceType_TXT) {
145 			/* New Buddy */
146 			BonjourBuddy *bb = (BonjourBuddy*) context;
147 			_mdns_parse_text_record(bb, rdata, rdlen);
148 			bonjour_buddy_add_to_purple(bb, NULL);
149 		} else if (rrtype == kDNSServiceType_NULL) {
150 			/* Buddy Icon response */
151 			BonjourBuddy *bb = (BonjourBuddy*) context;
152 			Win32BuddyImplData *idata = bb->mdns_impl_data;
153 
154 			g_return_if_fail(idata != NULL);
155 
156 			bonjour_buddy_got_buddy_icon(bb, rdata, rdlen);
157 
158 			/* We've got what we need; stop listening */
159 			purple_input_remove(idata->null_query->input_handler);
160 			DNSServiceRefDeallocate(idata->null_query->sdRef);
161 			g_free(idata->null_query);
162 			idata->null_query = NULL;
163 		}
164 	}
165 }
166 
167 static void DNSSD_API
_mdns_resolve_host_callback(DNSServiceRef sdRef,DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * hostname,const struct sockaddr * address,uint32_t ttl,void * context)168 _mdns_resolve_host_callback(DNSServiceRef sdRef, DNSServiceFlags flags,
169 	uint32_t interfaceIndex, DNSServiceErrorType errorCode,
170 	const char *hostname, const struct sockaddr *address,
171 	uint32_t ttl, void *context)
172 {
173 	ResolveCallbackArgs *args = (ResolveCallbackArgs*) context;
174 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
175 	gboolean delete_buddy = FALSE;
176 	PurpleBuddy *pb = NULL;
177 
178 	purple_input_remove(args->resolver_query->input_handler);
179 	DNSServiceRefDeallocate(args->resolver_query->sdRef);
180 	g_free(args->resolver_query);
181 	args->resolver_query = NULL;
182 
183 	if ((pb = purple_find_buddy(args->account, args->res_data->name))) {
184 		if (pb->proto_data != args->bb) {
185 			purple_debug_error("bonjour", "Found purple buddy for %s not matching bonjour buddy record.",
186 				args->res_data->name);
187 			goto cleanup;
188 		}
189 	/* Make sure that the BonjourBuddy associated with this request is still around */
190 	} else if (g_slist_find(pending_buddies, args->bb) == NULL) {
191 		purple_debug_error("bonjour", "host resolution - complete, but buddy no longer pending.\n");
192 		goto cleanup;
193 	}
194 
195 	if (errorCode != kDNSServiceErr_NoError) {
196 		purple_debug_error("bonjour", "host resolution - callback error (%d).\n", errorCode);
197 		delete_buddy = TRUE;
198 	} else {
199 		DNSServiceRef txt_query_sr;
200 
201 		/* finally, set up the continuous txt record watcher, and add the buddy to purple */
202 		errorCode = DNSServiceQueryRecord(&txt_query_sr, kDNSServiceFlagsLongLivedQuery,
203 				kDNSServiceInterfaceIndexAny, args->full_service_name, kDNSServiceType_TXT,
204 				kDNSServiceClass_IN, _mdns_record_query_callback, args->bb);
205 		if (errorCode == kDNSServiceErr_NoError) {
206 			const char *ip = inet_ntoa(((struct sockaddr_in *) address)->sin_addr);
207 
208 			purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", args->bb->name, ip, args->bb->port_p2pj);
209 
210 			args->bb->ips = g_slist_prepend(args->bb->ips, g_strdup(ip));
211 			args->res_data->ip = args->bb->ips->data;
212 
213 			args->res_data->txt_query = g_new(DnsSDServiceRefHandlerData, 1);
214 			args->res_data->txt_query->sdRef = txt_query_sr;
215 			args->res_data->txt_query->account = args->account;
216 
217 			args->res_data->txt_query->input_handler = purple_input_add(DNSServiceRefSockFD(txt_query_sr),
218 				PURPLE_INPUT_READ, _mdns_handle_event, args->res_data->txt_query);
219 
220 			bonjour_buddy_add_to_purple(args->bb, NULL);
221 		} else {
222 			purple_debug_error("bonjour", "Unable to set up record watcher for buddy %s (%d)\n", args->bb->name, errorCode);
223 			delete_buddy = TRUE;
224 		}
225 
226 	}
227 
228 	cleanup:
229 
230 	if (delete_buddy) {
231 		idata->resolvers = g_slist_remove(idata->resolvers, args->res_data);
232 		_cleanup_resolver_data(args->res_data);
233 
234 		/* If this was the last resolver, remove the buddy */
235 		if (idata->resolvers == NULL) {
236 			if (pb)
237 				bonjour_buddy_signed_off(pb);
238 			else
239 				bonjour_buddy_delete(args->bb);
240 
241 			/* Remove from the pending list */
242 			pending_buddies = g_slist_remove(pending_buddies, args->bb);
243 		}
244 	} else {
245 		/* Remove from the pending list */
246 		pending_buddies = g_slist_remove(pending_buddies, args->bb);
247 	}
248 
249 	/* free the remaining args memory */
250 	g_free(args->full_service_name);
251 	g_free(args);
252 }
253 
254 static void DNSSD_API
_mdns_service_resolve_callback(DNSServiceRef sdRef,DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * fullname,const char * hosttarget,uint16_t port,uint16_t txtLen,const unsigned char * txtRecord,void * context)255 _mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode,
256     const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context)
257 {
258 	ResolveCallbackArgs *args = (ResolveCallbackArgs*) context;
259 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
260 
261 	/* remove the input fd and destroy the service ref */
262 	purple_input_remove(args->resolver_query->input_handler);
263 	DNSServiceRefDeallocate(args->resolver_query->sdRef);
264 
265 	if (errorCode != kDNSServiceErr_NoError)
266 		purple_debug_error("bonjour", "service resolver - callback error. (%d)\n", errorCode);
267 	else {
268 		DNSServiceRef getaddrinfo_sr;
269 		/* set more arguments, and start the host resolver */
270 		errorCode = DNSServiceGetAddrInfo(&getaddrinfo_sr, 0, interfaceIndex,
271 			kDNSServiceProtocol_IPv4, hosttarget, _mdns_resolve_host_callback, args);
272 		if (errorCode != kDNSServiceErr_NoError)
273 			purple_debug_error("bonjour", "service resolver - host resolution failed.\n");
274 		else {
275 			args->resolver_query->sdRef = getaddrinfo_sr;
276 			args->resolver_query->input_handler = purple_input_add(DNSServiceRefSockFD(getaddrinfo_sr),
277 				PURPLE_INPUT_READ, _mdns_handle_event, args->resolver_query);
278 			args->full_service_name = g_strdup(fullname);
279 
280 			/* TODO: Should this be per resolver? */
281 			args->bb->port_p2pj = ntohs(port);
282 
283 			/* We don't want to hit the cleanup code */
284 			return;
285 		}
286 	}
287 
288 	/* If we get this far, clean up */
289 
290 	g_free(args->resolver_query);
291 	args->resolver_query = NULL;
292 
293 	idata->resolvers = g_slist_remove(idata->resolvers, args->res_data);
294 	_cleanup_resolver_data(args->res_data);
295 
296 	/* If this was the last resolver, remove the buddy */
297 	if (idata->resolvers == NULL) {
298 		PurpleBuddy *pb;
299 		/* See if this is now attached to a PurpleBuddy */
300 		if ((pb = purple_find_buddy(args->account, args->bb->name)))
301 			bonjour_buddy_signed_off(pb);
302 		else {
303 			/* Remove from the pending list */
304 			pending_buddies = g_slist_remove(pending_buddies, args->bb);
305 			bonjour_buddy_delete(args->bb);
306 		}
307 	}
308 
309 	g_free(args);
310 
311 }
312 
313 static void DNSSD_API
_mdns_service_register_callback(DNSServiceRef sdRef,DNSServiceFlags flags,DNSServiceErrorType errorCode,const char * name,const char * regtype,const char * domain,void * context)314 _mdns_service_register_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
315 				const char *name, const char *regtype, const char *domain, void *context) {
316 
317 	/* TODO: deal with collision */
318 	if (errorCode != kDNSServiceErr_NoError)
319 		purple_debug_error("bonjour", "service advertisement - callback error (%d).\n", errorCode);
320 	else
321 		purple_debug_info("bonjour", "service advertisement - callback.\n");
322 }
323 
324 static void DNSSD_API
_mdns_service_browse_callback(DNSServiceRef sdRef,DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * serviceName,const char * regtype,const char * replyDomain,void * context)325 _mdns_service_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
326     DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context)
327 {
328 	PurpleAccount *account = (PurpleAccount*)context;
329 
330 	if (errorCode != kDNSServiceErr_NoError)
331 		purple_debug_error("bonjour", "service browser - callback error (%d)\n", errorCode);
332 	else if (flags & kDNSServiceFlagsAdd) {
333 		/* A presence service instance has been discovered... check it isn't us! */
334 		if (purple_utf8_strcasecmp(serviceName, bonjour_get_jid(account)) != 0) {
335 			DNSServiceErrorType resErrorCode;
336 			/* OK, lets go ahead and resolve it to add to the buddy list */
337 			ResolveCallbackArgs *args = g_new0(ResolveCallbackArgs, 1);
338 			DNSServiceRef resolver_sr;
339 
340 			purple_debug_info("bonjour", "Received new record for '%s' on iface %u (%s, %s)\n",
341 							  serviceName, interfaceIndex, regtype ? regtype : "",
342 							  replyDomain ? replyDomain : "");
343 
344 			resErrorCode = DNSServiceResolve(&resolver_sr, 0, interfaceIndex, serviceName, regtype,
345 					replyDomain, _mdns_service_resolve_callback, args);
346 			if (resErrorCode == kDNSServiceErr_NoError) {
347 				GSList *tmp = pending_buddies;
348 				PurpleBuddy *pb;
349 				BonjourBuddy* bb = NULL;
350 				Win32SvcResolverData *rd;
351 				Win32BuddyImplData *idata;
352 
353 				/* Is there an existing buddy? */
354 				if ((pb = purple_find_buddy(account, serviceName)))
355 					bb = pb->proto_data;
356 				/* Is there a pending buddy? */
357 				else {
358 					while (tmp) {
359 						BonjourBuddy *bb_tmp = tmp->data;
360 						if (purple_strequal(bb_tmp->name, serviceName)) {
361 							bb = bb_tmp;
362 							break;
363 						}
364 						tmp = tmp->next;
365 					}
366 				}
367 
368 				if (bb == NULL) {
369 					bb = bonjour_buddy_new(serviceName, account);
370 
371 					/* This is only necessary for the wacky case where someone previously manually added a buddy. */
372 					if (pb == NULL)
373 						pending_buddies = g_slist_prepend(pending_buddies, bb);
374 					else
375 						pb->proto_data = bb;
376 				}
377 
378 				rd = g_new0(Win32SvcResolverData, 1);
379 				rd->if_idx = interfaceIndex;
380 				rd->name = g_strdup(serviceName);
381 				rd->type = g_strdup(regtype);
382 				rd->domain = g_strdup(replyDomain);
383 
384 				idata = bb->mdns_impl_data;
385 				idata->resolvers = g_slist_prepend(idata->resolvers, rd);
386 
387 				args->bb = bb;
388 				args->res_data = rd;
389 				args->account = account;
390 
391 				args->resolver_query = g_new(DnsSDServiceRefHandlerData, 1);
392 				args->resolver_query->sdRef = resolver_sr;
393 				args->resolver_query->account = account;
394 				/* get a file descriptor for this service ref, and add it to the input list */
395 				args->resolver_query->input_handler = purple_input_add(DNSServiceRefSockFD(resolver_sr),
396 					PURPLE_INPUT_READ, _mdns_handle_event, args->resolver_query);
397 			} else {
398 				purple_debug_error("bonjour", "service browser - failed to resolve service. (%d)\n", resErrorCode);
399 				g_free(args);
400 			}
401 		}
402 	} else {
403 		PurpleBuddy *pb = NULL;
404 
405 		/* A peer has sent a goodbye packet, remove them from the buddy list */
406 		purple_debug_info("bonjour", "Received remove notification for '%s' on iface %u (%s, %s)\n",
407 						  serviceName, interfaceIndex, regtype ? regtype : "",
408 						  replyDomain ? replyDomain : "");
409 
410 		pb = purple_find_buddy(account, serviceName);
411 		if (pb != NULL) {
412 			GSList *l;
413 			/* There may be multiple presences, we should only get rid of this one */
414 			Win32SvcResolverData *rd_search;
415 			BonjourBuddy *bb = pb->proto_data;
416 			Win32BuddyImplData *idata;
417 
418 			g_return_if_fail(bb != NULL);
419 
420 			idata = bb->mdns_impl_data;
421 
422 			rd_search = g_new0(Win32SvcResolverData, 1);
423 			rd_search->if_idx = interfaceIndex;
424 			rd_search->name = (gchar *) serviceName;
425 			rd_search->type = (gchar *) regtype;
426 			rd_search->domain = (gchar *) replyDomain;
427 
428 			l = g_slist_find_custom(idata->resolvers, rd_search, _find_resolver_data);
429 
430 			g_free(rd_search);
431 
432 			if (l != NULL) {
433 				Win32SvcResolverData *rd = l->data;
434 				idata->resolvers = g_slist_delete_link(idata->resolvers, l);
435 				/* This IP is no longer available */
436 				if (rd->ip != NULL) {
437 					bb->ips = g_slist_remove(bb->ips, rd->ip);
438 					g_free((gchar *) rd->ip);
439 				}
440 				_cleanup_resolver_data(rd);
441 
442 				/* If this was the last resolver, remove the buddy */
443 				if (idata->resolvers == NULL) {
444 					purple_debug_info("bonjour", "Removed last presence for buddy '%s'; signing off buddy.\n",
445 							  serviceName);
446 					bonjour_buddy_signed_off(pb);
447 				}
448 			}
449 		} else {
450 			purple_debug_warning("bonjour", "Unable to find buddy (%s) to remove\n", serviceName ? serviceName : "(null)");
451 			/* TODO: Should we look in the pending buddies list? */
452 		}
453 	}
454 }
455 
456 /****************************
457  * mdns_interface functions *
458  ****************************/
459 
_mdns_init_session(BonjourDnsSd * data)460 gboolean _mdns_init_session(BonjourDnsSd *data) {
461 	data->mdns_impl_data = g_new0(Win32SessionImplData, 1);
462 
463 	bonjour_dns_sd_set_jid(data->account, purple_get_host_name());
464 
465 	return TRUE;
466 }
467 
_mdns_publish(BonjourDnsSd * data,PublishType type,GSList * records)468 gboolean _mdns_publish(BonjourDnsSd *data, PublishType type, GSList *records) {
469 	TXTRecordRef dns_data;
470 	gboolean ret = TRUE;
471 	DNSServiceErrorType errorCode = kDNSServiceErr_NoError;
472 	Win32SessionImplData *idata = data->mdns_impl_data;
473 
474 	g_return_val_if_fail(idata != NULL, FALSE);
475 
476 	TXTRecordCreate(&dns_data, 256, NULL);
477 
478 	while (records) {
479 		PurpleKeyValuePair *kvp = records->data;
480 		errorCode = TXTRecordSetValue(&dns_data, kvp->key, strlen(kvp->value), kvp->value);
481 		if (errorCode != kDNSServiceErr_NoError)
482 			break;
483 		records = records->next;
484 	}
485 
486 	if (errorCode != kDNSServiceErr_NoError) {
487 		purple_debug_error("bonjour", "Unable to allocate memory for text record.(%d)\n", errorCode);
488 		ret = FALSE;
489 	} else {
490 		/* OK, we're done constructing the text record, (re)publish the service */
491 		DNSServiceRef presence_sr;
492 
493 		switch (type) {
494 			case PUBLISH_START:
495 				purple_debug_info("bonjour", "Registering presence on port %d\n", data->port_p2pj);
496 				errorCode = DNSServiceRegister(&presence_sr, kDNSServiceInterfaceIndexAny,
497 					0, bonjour_get_jid(data->account), LINK_LOCAL_RECORD_NAME,
498 					NULL, NULL, htons(data->port_p2pj), TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data),
499 					_mdns_service_register_callback, NULL);
500 				break;
501 
502 			case PUBLISH_UPDATE:
503 				purple_debug_info("bonjour", "Updating presence.\n");
504 				errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0);
505 				break;
506 		}
507 
508 		if (errorCode != kDNSServiceErr_NoError) {
509 			purple_debug_error("bonjour", "Failed to publish presence service.(%d)\n", errorCode);
510 			ret = FALSE;
511 		} else if (type == PUBLISH_START) {
512 			/* We need to do this because according to the Apple docs:
513 			 * "the client is responsible for ensuring that DNSServiceProcessResult() is called
514 			 * whenever there is a reply from the daemon - the daemon may terminate its connection
515 			 * with a client that does not process the daemon's responses */
516 			idata->presence_query = g_new(DnsSDServiceRefHandlerData, 1);
517 			idata->presence_query->sdRef = presence_sr;
518 			idata->presence_query->account = data->account;
519 			idata->presence_query->input_handler = purple_input_add(DNSServiceRefSockFD(presence_sr),
520 				PURPLE_INPUT_READ, _mdns_handle_event, idata->presence_query);
521 		}
522 	}
523 
524 	/* Free the memory used by temp data */
525 	TXTRecordDeallocate(&dns_data);
526 	return ret;
527 }
528 
_mdns_browse(BonjourDnsSd * data)529 gboolean _mdns_browse(BonjourDnsSd *data) {
530 	DNSServiceErrorType errorCode;
531 	Win32SessionImplData *idata = data->mdns_impl_data;
532 	DNSServiceRef browser_sr;
533 
534 	g_return_val_if_fail(idata != NULL, FALSE);
535 
536 	errorCode = DNSServiceBrowse(&browser_sr, 0, kDNSServiceInterfaceIndexAny,
537 			LINK_LOCAL_RECORD_NAME, NULL,_mdns_service_browse_callback,
538 			data->account);
539 	if (errorCode == kDNSServiceErr_NoError) {
540 		idata->browser_query = g_new(DnsSDServiceRefHandlerData, 1);
541 		idata->browser_query->sdRef = browser_sr;
542 		idata->browser_query->account = data->account;
543 		idata->browser_query->input_handler = purple_input_add(DNSServiceRefSockFD(browser_sr),
544 			PURPLE_INPUT_READ, _mdns_handle_event, idata->browser_query);
545 		return TRUE;
546 	} else
547 		purple_debug_error("bonjour", "Error registering Local Link presence browser. (%d)\n", errorCode);
548 
549 
550 	return FALSE;
551 }
552 
_mdns_stop(BonjourDnsSd * data)553 void _mdns_stop(BonjourDnsSd *data) {
554 	Win32SessionImplData *idata = data->mdns_impl_data;
555 
556 	if (idata == NULL)
557 		return;
558 
559 	if (idata->presence_query != NULL) {
560 		purple_input_remove(idata->presence_query->input_handler);
561 		DNSServiceRefDeallocate(idata->presence_query->sdRef);
562 		g_free(idata->presence_query);
563 	}
564 
565 	if (idata->browser_query != NULL) {
566 		purple_input_remove(idata->browser_query->input_handler);
567 		DNSServiceRefDeallocate(idata->browser_query->sdRef);
568 		g_free(idata->browser_query);
569 	}
570 
571 	g_free(idata);
572 
573 	data->mdns_impl_data = NULL;
574 }
575 
_mdns_set_buddy_icon_data(BonjourDnsSd * data,gconstpointer avatar_data,gsize avatar_len)576 gboolean _mdns_set_buddy_icon_data(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len) {
577 	Win32SessionImplData *idata = data->mdns_impl_data;
578 	DNSServiceErrorType errorCode = kDNSServiceErr_NoError;
579 
580 	g_return_val_if_fail(idata != NULL, FALSE);
581 
582 	if (avatar_data != NULL && idata->buddy_icon_rec == NULL) {
583 		purple_debug_info("bonjour", "Setting new buddy icon.\n");
584 		errorCode = DNSServiceAddRecord(idata->presence_query->sdRef, &idata->buddy_icon_rec,
585 			0, kDNSServiceType_NULL, avatar_len, avatar_data, 0);
586 	} else if (avatar_data != NULL) {
587 		purple_debug_info("bonjour", "Updating existing buddy icon.\n");
588 		errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, idata->buddy_icon_rec,
589 			0, avatar_len, avatar_data, 0);
590 	} else if (idata->buddy_icon_rec != NULL) {
591 		purple_debug_info("bonjour", "Removing existing buddy icon.\n");
592 		errorCode = DNSServiceRemoveRecord(idata->presence_query->sdRef, idata->buddy_icon_rec, 0);
593 		idata->buddy_icon_rec = NULL;
594 	}
595 
596 	if (errorCode != kDNSServiceErr_NoError) {
597 		purple_debug_error("bonjour", "Error (%d) setting buddy icon record.\n", errorCode);
598 		return FALSE;
599 	}
600 
601 	return TRUE;
602 }
603 
_mdns_init_buddy(BonjourBuddy * buddy)604 void _mdns_init_buddy(BonjourBuddy *buddy) {
605 	buddy->mdns_impl_data = g_new0(Win32BuddyImplData, 1);
606 }
607 
_mdns_delete_buddy(BonjourBuddy * buddy)608 void _mdns_delete_buddy(BonjourBuddy *buddy) {
609 	Win32BuddyImplData *idata = buddy->mdns_impl_data;
610 
611 	g_return_if_fail(idata != NULL);
612 
613 	while (idata->resolvers) {
614 		Win32SvcResolverData *rd = idata->resolvers->data;
615 		_cleanup_resolver_data(rd);
616 		idata->resolvers = g_slist_delete_link(idata->resolvers, idata->resolvers);
617 	}
618 
619 	if (idata->null_query != NULL) {
620 		purple_input_remove(idata->null_query->input_handler);
621 		DNSServiceRefDeallocate(idata->null_query->sdRef);
622 		g_free(idata->null_query);
623 	}
624 
625 	g_free(idata);
626 
627 	buddy->mdns_impl_data = NULL;
628 }
629 
_mdns_retrieve_buddy_icon(BonjourBuddy * buddy)630 void _mdns_retrieve_buddy_icon(BonjourBuddy* buddy) {
631 	Win32BuddyImplData *idata = buddy->mdns_impl_data;
632 	char svc_name[kDNSServiceMaxDomainName];
633 
634 	g_return_if_fail(idata != NULL);
635 
636 	/* Cancel any existing query */
637 	if (idata->null_query != NULL) {
638 		purple_input_remove(idata->null_query->input_handler);
639 		DNSServiceRefDeallocate(idata->null_query->sdRef);
640 		g_free(idata->null_query);
641 		idata->null_query = NULL;
642 	}
643 
644 	if (DNSServiceConstructFullName(svc_name, buddy->name, LINK_LOCAL_RECORD_NAME, "local") != 0)
645 		purple_debug_error("bonjour", "Unable to construct full name to retrieve buddy icon for %s.\n", buddy->name);
646 	else {
647 		DNSServiceRef null_query_sr;
648 
649 		DNSServiceErrorType errorCode = DNSServiceQueryRecord(&null_query_sr, 0, kDNSServiceInterfaceIndexAny,
650 			svc_name, kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_record_query_callback, buddy);
651 
652 		if (errorCode == kDNSServiceErr_NoError) {
653 			idata->null_query = g_new(DnsSDServiceRefHandlerData, 1);
654 
655 			idata->null_query->sdRef = null_query_sr;
656 			idata->null_query->account = buddy->account;
657 
658 			idata->null_query->input_handler = purple_input_add(DNSServiceRefSockFD(null_query_sr),
659 				PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query);
660 		} else
661 			purple_debug_error("bonjour", "Unable to query buddy icon record for %s. (%d)\n", buddy->name, errorCode);
662 	}
663 
664 }
665 
666