/** * @return TRUE if the request was sent succesfully. FALSE * if the request could not be sent. This isn't * necessarily an error. If the child has expired, * for example, we won't be able to send the message. */ static gboolean send_dns_request_to_child(PurpleDnsQueryData *query_data, PurpleDnsQueryResolverProcess *resolver) { pid_t pid; dns_params_t dns_params; ssize_t rc; /* This waitpid might return the child's PID if it has recently * exited, or it might return an error if it exited "long * enough" ago that it has already been reaped; in either * instance, we can't use it. */ pid = waitpid(resolver->dns_pid, NULL, WNOHANG); if (pid > 0) { purple_debug_warning("dns", "DNS child %d no longer exists\n", resolver->dns_pid); purple_dnsquery_resolver_destroy(resolver); return FALSE; } else if (pid < 0) { purple_debug_warning("dns", "Wait for DNS child %d failed: %s\n", resolver->dns_pid, g_strerror(errno)); purple_dnsquery_resolver_destroy(resolver); return FALSE; } /* Copy the hostname and port into a single data structure */ strncpy(dns_params.hostname, query_data->hostname, sizeof(dns_params.hostname) - 1); dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; dns_params.port = query_data->port; /* Send the data structure to the child */ rc = write(resolver->fd_in, &dns_params, sizeof(dns_params)); if (rc < 0) { purple_debug_error("dns", "Unable to write to DNS child %d: %s\n", resolver->dns_pid, g_strerror(errno)); purple_dnsquery_resolver_destroy(resolver); return FALSE; } if ((gsize)rc < sizeof(dns_params)) { purple_debug_error("dns", "Tried to write %" G_GSSIZE_FORMAT " bytes to child but only wrote %" G_GSSIZE_FORMAT "\n", sizeof(dns_params), rc); purple_dnsquery_resolver_destroy(resolver); return FALSE; } purple_debug_info("dns", "Successfully sent DNS request to child %d\n", resolver->dns_pid); query_data->resolver = resolver; return TRUE; }
void purple_dnsquery_uninit(void) { #if defined(__unix__) || defined(__APPLE__) while (free_dns_children != NULL) { purple_dnsquery_resolver_destroy(free_dns_children->data); free_dns_children = g_slist_remove(free_dns_children, free_dns_children->data); } #endif }
void purple_dnsquery_uninit(void) { #if defined(PURPLE_DNSQUERY_USE_FORK) while (free_dns_children != NULL) { purple_dnsquery_resolver_destroy(free_dns_children->data); free_dns_children = g_slist_remove(free_dns_children, free_dns_children->data); } #endif /* end PURPLE_DNSQUERY_USE_FORK */ }
static PurpleDnsQueryResolverProcess * purple_dnsquery_resolver_new(gboolean show_debug) { PurpleDnsQueryResolverProcess *resolver; int child_out[2], child_in[2]; /* Create pipes for communicating with the child process */ if (pipe(child_out) || pipe(child_in)) { purple_debug_error("dns", "Could not create pipes: %s\n", strerror(errno)); return NULL; } resolver = g_new(PurpleDnsQueryResolverProcess, 1); resolver->inpa = 0; cope_with_gdb_brokenness(); /* "Go fork and multiply." --Tommy Caldwell (Emily's dad, not the climber) */ resolver->dns_pid = fork(); /* If we are the child process... */ if (resolver->dns_pid == 0) { /* We should not access the parent's side of the pipes, so close them */ close(child_out[0]); close(child_in[1]); purple_dnsquery_resolver_run(child_out[1], child_in[0], show_debug); /* The thread calls _exit() rather than returning, so we never get here */ } /* We should not access the child's side of the pipes, so close them */ close(child_out[1]); close(child_in[0]); if (resolver->dns_pid == -1) { purple_debug_error("dns", "Could not create child process for DNS: %s\n", strerror(errno)); purple_dnsquery_resolver_destroy(resolver); return NULL; } resolver->fd_out = child_out[0]; resolver->fd_in = child_in[1]; number_of_dns_children++; purple_debug_info("dns", "Created new DNS child %d, there are now %d children.\n", resolver->dns_pid, number_of_dns_children); return resolver; }
void purple_dnsquery_destroy(PurpleDnsQueryData *query_data) { PurpleDnsQueryUiOps *ops = purple_dnsquery_get_ui_ops(); if (ops && ops->destroy) ops->destroy(query_data); #if defined(PURPLE_DNSQUERY_USE_FORK) queued_requests = g_slist_remove(queued_requests, query_data); if (query_data->resolver != NULL) /* * This is only non-NULL when we're cancelling an in-progress * query. Ideally we would tell our resolver child to stop * resolving shit and then we would add it back to the * free_dns_children linked list. However, it's hard to tell * children stuff, they just don't listen. So we'll just * kill the process and allow a new child to be started if we * have more stuff to resolve. */ purple_dnsquery_resolver_destroy(query_data->resolver); #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */ if (query_data->resolver != NULL) { /* * It's not really possible to kill a thread. So instead we * just set the callback to NULL and let the DNS lookup * finish. */ query_data->callback = NULL; return; } while (query_data->hosts != NULL) { /* Discard the length... */ query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data); /* Free the address... */ g_free(query_data->hosts->data); query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data); } g_free(query_data->error_message); #endif /* end _WIN32 */ if (query_data->timeout > 0) purple_timeout_remove(query_data->timeout); g_free(query_data->hostname); g_free(query_data); }
void purple_dnsquery_destroy(PurpleDnsQueryData *query_data) { PurpleDnsQueryUiOps *ops = purple_dnsquery_get_ui_ops(); if (ops && ops->destroy) ops->destroy(query_data); #if defined(__unix__) || defined(__APPLE__) queued_requests = g_slist_remove(queued_requests, query_data); if (query_data->resolver != NULL) /* * Ideally we would tell our resolver child to stop resolving * shit and then we would add it back to the free_dns_children * linked list. However, it's hard to tell children stuff, * they just don't listen. */ purple_dnsquery_resolver_destroy(query_data->resolver); #elif defined _WIN32 /* end __unix__ || __APPLE__ */ if (query_data->resolver != NULL) { /* * It's not really possible to kill a thread. So instead we * just set the callback to NULL and let the DNS lookup * finish. */ query_data->callback = NULL; return; } while (query_data->hosts != NULL) { /* Discard the length... */ query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data); /* Free the address... */ g_free(query_data->hosts->data); query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data); } g_free(query_data->error_message); #endif if (query_data->timeout > 0) purple_timeout_remove(query_data->timeout); g_free(query_data->hostname); g_free(query_data); }
/** * @return TRUE if the request was sent succesfully. FALSE * if the request could not be sent. This isn't * necessarily an error. If the child has expired, * for example, we won't be able to send the message. */ static gboolean send_dns_request_to_child(PurpleDnsQueryData *query_data, PurpleDnsQueryResolverProcess *resolver) { pid_t pid; dns_params_t dns_params; int rc; char ch; /* This waitpid might return the child's PID if it has recently * exited, or it might return an error if it exited "long * enough" ago that it has already been reaped; in either * instance, we can't use it. */ pid = waitpid(resolver->dns_pid, NULL, WNOHANG); if (pid > 0) { purple_debug_warning("dns", "DNS child %d no longer exists\n", resolver->dns_pid); purple_dnsquery_resolver_destroy(resolver); return FALSE; } else if (pid < 0) { purple_debug_warning("dns", "Wait for DNS child %d failed: %s\n", resolver->dns_pid, strerror(errno)); purple_dnsquery_resolver_destroy(resolver); return FALSE; } /* Copy the hostname and port into a single data structure */ strncpy(dns_params.hostname, query_data->hostname, sizeof(dns_params.hostname) - 1); dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; dns_params.port = query_data->port; /* Send the data structure to the child */ rc = write(resolver->fd_in, &dns_params, sizeof(dns_params)); if (rc < 0) { purple_debug_error("dns", "Unable to write to DNS child %d: %d\n", resolver->dns_pid, strerror(errno)); purple_dnsquery_resolver_destroy(resolver); return FALSE; } g_return_val_if_fail(rc == sizeof(dns_params), -1); /* Did you hear me? (This avoids some race conditions) */ rc = read(resolver->fd_out, &ch, sizeof(ch)); if (rc != 1 || ch != 'Y') { purple_debug_warning("dns", "DNS child %d not responding. Killing it!\n", resolver->dns_pid); purple_dnsquery_resolver_destroy(resolver); return FALSE; } purple_debug_info("dns", "Successfully sent DNS request to child %d\n", resolver->dns_pid); query_data->resolver = resolver; return TRUE; }