Mercurial > pidgin.yaz
changeset 21987:71d18f5adbba
Detect the mdnsresponder crashing in the DnsSD implementation and disconnect the account instead of pegging the CPU.
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Fri, 04 Jan 2008 20:41:08 +0000 |
parents | 035cfd9080a6 |
children | f314129eaaf5 |
files | libpurple/protocols/bonjour/mdns_win32.c |
diffstat | 1 files changed, 95 insertions(+), 60 deletions(-) [+] |
line wrap: on
line diff
--- a/libpurple/protocols/bonjour/mdns_win32.c Fri Jan 04 20:34:23 2008 +0000 +++ b/libpurple/protocols/bonjour/mdns_win32.c Fri Jan 04 20:41:08 2008 +0000 @@ -30,19 +30,21 @@ static GSList *pending_buddies = NULL; +typedef struct _dnssd_service_ref_handler { + DNSServiceRef sdRef; + PurpleAccount *account; + guint input_handler; +} DnsSDServiceRefHandlerData; + /* data used by win32 bonjour implementation */ typedef struct _win32_session_impl_data { - DNSServiceRef presence_svc; - DNSServiceRef browser_svc; + DnsSDServiceRefHandlerData *presence_query; + DnsSDServiceRefHandlerData *browser_query; DNSRecordRef buddy_icon_rec; - - guint presence_handler; - guint browser_handler; } Win32SessionImplData; typedef struct _win32_buddy_service_resolver_data { - DNSServiceRef txt_query; - guint txt_query_handler; + DnsSDServiceRefHandlerData *txt_query; uint32_t if_idx; gchar *name; gchar *type; @@ -53,21 +55,20 @@ typedef struct _win32_buddy_impl_data { GSList *resolvers; - DNSServiceRef null_query; - guint null_query_handler; + DnsSDServiceRefHandlerData *null_query; } Win32BuddyImplData; /* data structure for the resolve callback */ typedef struct _ResolveCallbackArgs { - DNSServiceRef resolver; - guint resolver_handler; + DnsSDServiceRefHandlerData *resolver_query; PurpleAccount *account; BonjourBuddy *bb; Win32SvcResolverData *res_data; gchar *full_service_name; - PurpleDnsQueryData *query; + PurpleDnsQueryData *dns_query; } ResolveCallbackArgs; + static gint _find_resolver_data(gconstpointer a, gconstpointer b) { const Win32SvcResolverData *rd_a = a; @@ -87,8 +88,9 @@ static void _cleanup_resolver_data(Win32SvcResolverData *rd) { if (rd->txt_query != NULL) { - purple_input_remove(rd->txt_query_handler); - DNSServiceRefDeallocate(rd->txt_query); + purple_input_remove(rd->txt_query->input_handler); + DNSServiceRefDeallocate(rd->txt_query->sdRef); + g_free(rd->txt_query); } g_free(rd->name); g_free(rd->type); @@ -98,7 +100,16 @@ static void _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) { - DNSServiceProcessResult((DNSServiceRef) data); + DnsSDServiceRefHandlerData *srh = data; + DNSServiceErrorType errorCode = DNSServiceProcessResult(srh->sdRef); + if (errorCode != kDNSServiceErr_NoError) { + purple_debug_error("bonjour", "Error (%d) handling mDNS response.\n", errorCode); + /* This happens when the mDNSResponder goes down, I haven't seen it happen any other time (in my limited testing) */ + if (errorCode == kDNSServiceErr_Unknown) { + purple_connection_error_reason(srh->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Error communicating with local mDNSResponder.")); + } + } } static void @@ -142,9 +153,9 @@ bonjour_buddy_got_buddy_icon(bb, rdata, rdlen); /* We've got what we need; stop listening */ - purple_input_remove(idata->null_query_handler); - idata->null_query_handler = 0; - DNSServiceRefDeallocate(idata->null_query); + purple_input_remove(idata->null_query->input_handler); + DNSServiceRefDeallocate(idata->null_query->sdRef); + g_free(idata->null_query); idata->null_query = NULL; } } @@ -153,7 +164,7 @@ static void _mdns_resolve_host_callback(GSList *hosts, gpointer data, const char *error_message) { - ResolveCallbackArgs* args = (ResolveCallbackArgs*) data; + ResolveCallbackArgs *args = (ResolveCallbackArgs*) data; Win32BuddyImplData *idata = args->bb->mdns_impl_data; gboolean delete_buddy = FALSE; PurpleBuddy *pb; @@ -169,10 +180,10 @@ } else { struct sockaddr_in *addr = g_slist_nth_data(hosts, 1); DNSServiceErrorType errorCode; + DNSServiceRef txt_query_sr; /* finally, set up the continuous txt record watcher, and add the buddy to purple */ - - errorCode = DNSServiceQueryRecord(&args->res_data->txt_query, kDNSServiceFlagsLongLivedQuery, + errorCode = DNSServiceQueryRecord(&txt_query_sr, kDNSServiceFlagsLongLivedQuery, kDNSServiceInterfaceIndexAny, args->full_service_name, kDNSServiceType_TXT, kDNSServiceClass_IN, _mdns_record_query_callback, args->bb); if (errorCode == kDNSServiceErr_NoError) { @@ -180,11 +191,14 @@ purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", args->bb->name, ip, args->bb->port_p2pj); - args->bb->ips = g_slist_prepend(args->bb->ips, g_strdup(ip)); args->res_data->ip = args->bb->ips->data; - args->res_data->txt_query_handler = purple_input_add(DNSServiceRefSockFD(args->res_data->txt_query), + args->res_data->txt_query = g_new(DnsSDServiceRefHandlerData, 1); + args->res_data->txt_query->sdRef = txt_query_sr; + args->res_data->txt_query->account = args->account; + + args->res_data->txt_query->input_handler = purple_input_add(DNSServiceRefSockFD(txt_query_sr), PURPLE_INPUT_READ, _mdns_handle_event, args->res_data->txt_query); bonjour_buddy_add_to_purple(args->bb, NULL); @@ -231,21 +245,21 @@ _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 char *txtRecord, void *context) { - ResolveCallbackArgs *args = (ResolveCallbackArgs*)context; + ResolveCallbackArgs *args = (ResolveCallbackArgs*) context; Win32BuddyImplData *idata = args->bb->mdns_impl_data; /* remove the input fd and destroy the service ref */ - purple_input_remove(args->resolver_handler); - args->resolver_handler = 0; - DNSServiceRefDeallocate(args->resolver); - args->resolver = NULL; + purple_input_remove(args->resolver_query->input_handler); + DNSServiceRefDeallocate(args->resolver_query->sdRef); + g_free(args->resolver_query); + args->resolver_query = NULL; if (errorCode != kDNSServiceErr_NoError) purple_debug_error("bonjour", "service resolver - callback error.\n"); else { /* set more arguments, and start the host resolver */ - if ((args->query = + if ((args->dns_query = purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args)) != NULL) { args->full_service_name = g_strdup(fullname); @@ -307,12 +321,13 @@ DNSServiceErrorType resErrorCode; /* OK, lets go ahead and resolve it to add to the buddy list */ ResolveCallbackArgs *args = g_new0(ResolveCallbackArgs, 1); + DNSServiceRef resolver_sr; purple_debug_info("bonjour", "Received new record for '%s' on iface %u (%s, %s)\n", serviceName, interfaceIndex, regtype ? regtype : "", replyDomain ? replyDomain : ""); - resErrorCode = DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype, + resErrorCode = DNSServiceResolve(&resolver_sr, 0, 0, serviceName, regtype, replyDomain, _mdns_service_resolve_callback, args); if (resErrorCode == kDNSServiceErr_NoError) { GSList *tmp = pending_buddies; @@ -320,7 +335,6 @@ BonjourBuddy* bb = NULL; Win32SvcResolverData *rd; Win32BuddyImplData *idata; - gint fd; /* Is there an existing buddy? */ if ((pb = purple_find_buddy(account, serviceName))) @@ -347,7 +361,6 @@ pb->proto_data = bb; } - rd = g_new0(Win32SvcResolverData, 1); rd->if_idx = interfaceIndex; rd->name = g_strdup(serviceName); @@ -361,9 +374,12 @@ args->res_data = rd; args->account = account; + args->resolver_query = g_new(DnsSDServiceRefHandlerData, 1); + args->resolver_query->sdRef = resolver_sr; + args->resolver_query->account = account; /* get a file descriptor for this service ref, and add it to the input list */ - fd = DNSServiceRefSockFD(args->resolver); - args->resolver_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver); + args->resolver_query->input_handler = purple_input_add(DNSServiceRefSockFD(resolver_sr), + PURPLE_INPUT_READ, _mdns_handle_event, args->resolver_query); } else { purple_debug_error("bonjour", "service browser - failed to resolve service. (%d)\n", resErrorCode); g_free(args); @@ -455,18 +471,19 @@ ret = FALSE; } else { /* OK, we're done constructing the text record, (re)publish the service */ + DNSServiceRef presence_sr; switch (type) { case PUBLISH_START: purple_debug_info("bonjour", "Registering presence on port %d\n", data->port_p2pj); - errorCode = DNSServiceRegister(&idata->presence_svc, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE, + errorCode = DNSServiceRegister(&presence_sr, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL, NULL, htons(data->port_p2pj), TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), _mdns_service_register_callback, NULL); break; case PUBLISH_UPDATE: purple_debug_info("bonjour", "Updating presence.\n"); - errorCode = DNSServiceUpdateRecord(idata->presence_svc, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0); + errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0); break; } @@ -478,8 +495,11 @@ * "the client is responsible for ensuring that DNSServiceProcessResult() is called * whenever there is a reply from the daemon - the daemon may terminate its connection * with a client that does not process the daemon's responses */ - idata->presence_handler = purple_input_add(DNSServiceRefSockFD(idata->presence_svc), - PURPLE_INPUT_READ, _mdns_handle_event, idata->presence_svc); + idata->presence_query = g_new(DnsSDServiceRefHandlerData, 1); + idata->presence_query->sdRef = presence_sr; + idata->presence_query->account = data->account; + idata->presence_query->input_handler = purple_input_add(DNSServiceRefSockFD(presence_sr), + PURPLE_INPUT_READ, _mdns_handle_event, idata->presence_query); } } @@ -491,14 +511,18 @@ gboolean _mdns_browse(BonjourDnsSd *data) { DNSServiceErrorType errorCode; Win32SessionImplData *idata = data->mdns_impl_data; + DNSServiceRef browser_sr; g_return_val_if_fail(idata != NULL, FALSE); - errorCode = DNSServiceBrowse(&idata->browser_svc, 0, 0, ICHAT_SERVICE, NULL, - _mdns_service_browse_callback, data->account); + errorCode = DNSServiceBrowse(&browser_sr, 0, 0, ICHAT_SERVICE, NULL, + _mdns_service_browse_callback, data->account); if (errorCode == kDNSServiceErr_NoError) { - idata->browser_handler = purple_input_add(DNSServiceRefSockFD(idata->browser_svc), - PURPLE_INPUT_READ, _mdns_handle_event, idata->browser_svc); + idata->browser_query = g_new(DnsSDServiceRefHandlerData, 1); + idata->browser_query->sdRef = browser_sr; + idata->browser_query->account = data->account; + idata->browser_query->input_handler = purple_input_add(DNSServiceRefSockFD(browser_sr), + PURPLE_INPUT_READ, _mdns_handle_event, idata->browser_query); return TRUE; } else purple_debug_error("bonjour", "Error registering Local Link presence browser. (%d)\n", errorCode); @@ -513,14 +537,16 @@ if (idata == NULL) return; - if (idata->presence_svc != NULL) { - purple_input_remove(idata->presence_handler); - DNSServiceRefDeallocate(idata->presence_svc); + if (idata->presence_query != NULL) { + purple_input_remove(idata->presence_query->input_handler); + DNSServiceRefDeallocate(idata->presence_query->sdRef); + g_free(idata->presence_query); } - if (idata->browser_svc != NULL) { - purple_input_remove(idata->browser_handler); - DNSServiceRefDeallocate(idata->browser_svc); + if (idata->browser_query != NULL) { + purple_input_remove(idata->browser_query->input_handler); + DNSServiceRefDeallocate(idata->browser_query->sdRef); + g_free(idata->browser_query); } g_free(idata); @@ -536,15 +562,15 @@ if (avatar_data != NULL && idata->buddy_icon_rec == NULL) { purple_debug_info("bonjour", "Setting new buddy icon.\n"); - errorCode = DNSServiceAddRecord(idata->presence_svc, &idata->buddy_icon_rec, + errorCode = DNSServiceAddRecord(idata->presence_query->sdRef, &idata->buddy_icon_rec, 0, kDNSServiceType_NULL, avatar_len, avatar_data, 0); } else if (avatar_data != NULL) { purple_debug_info("bonjour", "Updating existing buddy icon.\n"); - errorCode = DNSServiceUpdateRecord(idata->presence_svc, idata->buddy_icon_rec, + errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, idata->buddy_icon_rec, 0, avatar_len, avatar_data, 0); } else if (idata->buddy_icon_rec != NULL) { purple_debug_info("bonjour", "Removing existing buddy icon.\n"); - errorCode = DNSServiceRemoveRecord(idata->presence_svc, idata->buddy_icon_rec, 0); + errorCode = DNSServiceRemoveRecord(idata->presence_query->sdRef, idata->buddy_icon_rec, 0); idata->buddy_icon_rec = NULL; } @@ -572,8 +598,9 @@ } if (idata->null_query != NULL) { - purple_input_remove(idata->null_query_handler); - DNSServiceRefDeallocate(idata->null_query); + purple_input_remove(idata->null_query->input_handler); + DNSServiceRefDeallocate(idata->null_query->sdRef); + g_free(idata->null_query); } g_free(idata); @@ -589,21 +616,29 @@ /* Cancel any existing query */ if (idata->null_query != NULL) { - purple_input_remove(idata->null_query_handler); - idata->null_query_handler = 0; - DNSServiceRefDeallocate(idata->null_query); + purple_input_remove(idata->null_query->input_handler); + DNSServiceRefDeallocate(idata->null_query->sdRef); + g_free(idata->null_query); idata->null_query = NULL; } if (DNSServiceConstructFullName(svc_name, buddy->name, ICHAT_SERVICE, "local") != 0) purple_debug_error("bonjour", "Unable to construct full name to retrieve buddy icon for %s.\n", buddy->name); else { - DNSServiceErrorType errorCode = DNSServiceQueryRecord(&idata->null_query, 0, kDNSServiceInterfaceIndexAny, + DNSServiceRef null_query_sr; + + DNSServiceErrorType errorCode = DNSServiceQueryRecord(&null_query_sr, 0, kDNSServiceInterfaceIndexAny, svc_name, kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_record_query_callback, buddy); - if (errorCode == kDNSServiceErr_NoError) - idata->null_query_handler = purple_input_add(DNSServiceRefSockFD(idata->null_query), + + if (errorCode == kDNSServiceErr_NoError) { + idata->null_query = g_new(DnsSDServiceRefHandlerData, 1); + + idata->null_query->sdRef = null_query_sr; + idata->null_query->account = buddy->account; + + idata->null_query->input_handler = purple_input_add(DNSServiceRefSockFD(null_query_sr), PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query); - else + } else purple_debug_error("bonjour", "Unable to query buddy icon record for %s. (%d)\n", buddy->name, errorCode); }