14192
|
1 /**
|
|
2 * @file dnsquery.c DNS query API
|
|
3 * @ingroup core
|
|
4 *
|
|
5 * gaim
|
|
6 *
|
|
7 * Gaim is the legal property of its developers, whose names are too numerous
|
|
8 * to list here. Please refer to the COPYRIGHT file distributed with this
|
|
9 * source distribution.
|
|
10 *
|
|
11 * This program is free software; you can redistribute it and/or modify
|
|
12 * it under the terms of the GNU General Public License as published by
|
|
13 * the Free Software Foundation; either version 2 of the License, or
|
|
14 * (at your option) any later version.
|
|
15 *
|
|
16 * This program is distributed in the hope that it will be useful,
|
|
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19 * GNU General Public License for more details.
|
|
20 *
|
|
21 * You should have received a copy of the GNU General Public License
|
|
22 * along with this program; if not, write to the Free Software
|
|
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
24 *
|
|
25 */
|
|
26
|
|
27 #include "internal.h"
|
|
28 #include "debug.h"
|
14238
|
29 #include "dnsquery.h"
|
14192
|
30 #include "notify.h"
|
|
31 #include "prefs.h"
|
|
32 #include "util.h"
|
|
33
|
|
34 /**************************************************************************
|
|
35 * DNS query API
|
|
36 **************************************************************************/
|
|
37
|
14238
|
38 typedef struct _GaimDnsQueryResolverProcess GaimDnsQueryResolverProcess;
|
|
39
|
14192
|
40 struct _GaimDnsQueryData {
|
14238
|
41 char *hostname;
|
|
42 int port;
|
|
43 GaimDnsQueryConnectFunction callback;
|
|
44 gpointer data;
|
|
45 guint timeout;
|
|
46
|
|
47 GThread *resolver;
|
|
48 GSList *hosts;
|
|
49 gchar *error_message;
|
14192
|
50 };
|
|
51
|
14238
|
52 static void
|
|
53 gaim_dnsquery_resolved(GaimDnsQueryData *query_data, GSList *hosts)
|
|
54 {
|
14244
|
55 gaim_debug_info("dnsquery", "IP resolved for %s\n", query_data->hostname);
|
14238
|
56 if (query_data->callback != NULL)
|
|
57 query_data->callback(hosts, query_data->data, NULL);
|
|
58 gaim_dnsquery_destroy(query_data);
|
|
59 }
|
|
60
|
|
61 static void
|
|
62 gaim_dnsquery_failed(GaimDnsQueryData *query_data, const gchar *error_message)
|
|
63 {
|
|
64 gaim_debug_info("dnsquery", "%s\n", error_message);
|
|
65 if (query_data->callback != NULL)
|
|
66 query_data->callback(NULL, query_data->data, error_message);
|
|
67 gaim_dnsquery_destroy(query_data);
|
|
68 }
|
|
69
|
|
70 static gboolean
|
|
71 dns_main_thread_cb(gpointer data)
|
|
72 {
|
|
73 GaimDnsQueryData *query_data;
|
|
74
|
|
75 query_data = data;
|
14192
|
76
|
14238
|
77 if (query_data->error_message != NULL)
|
|
78 gaim_dnsquery_failed(query_data, query_data->error_message);
|
|
79 else
|
|
80 {
|
|
81 GSList *hosts;
|
14244
|
82
|
|
83 /* We don't want gaim_dns_query_resolved() to free hosts */
|
14238
|
84 hosts = query_data->hosts;
|
|
85 query_data->hosts = NULL;
|
|
86 gaim_dnsquery_resolved(query_data, hosts);
|
14192
|
87 }
|
14238
|
88
|
14192
|
89 return FALSE;
|
|
90 }
|
|
91
|
14263
|
92 #ifdef HAVE_GETADDRINFO
|
14238
|
93 static gpointer
|
|
94 dns_thread(gpointer data)
|
|
95 {
|
|
96 GaimDnsQueryData *query_data;
|
14192
|
97 int rc;
|
|
98 struct addrinfo hints, *res, *tmp;
|
|
99 char servname[20];
|
14238
|
100
|
|
101 query_data = data;
|
14192
|
102
|
14238
|
103 g_snprintf(servname, sizeof(servname), "%d", query_data->port);
|
14192
|
104 memset(&hints,0,sizeof(hints));
|
|
105
|
14238
|
106 /*
|
|
107 * This is only used to convert a service
|
14192
|
108 * name to a port number. As we know we are
|
|
109 * passing a number already, we know this
|
|
110 * value will not be really used by the C
|
|
111 * library.
|
|
112 */
|
|
113 hints.ai_socktype = SOCK_STREAM;
|
14238
|
114 if ((rc = getaddrinfo(query_data->hostname, servname, &hints, &res)) == 0) {
|
14192
|
115 tmp = res;
|
|
116 while(res) {
|
14238
|
117 query_data->hosts = g_slist_append(query_data->hosts,
|
14192
|
118 GSIZE_TO_POINTER(res->ai_addrlen));
|
14238
|
119 query_data->hosts = g_slist_append(query_data->hosts,
|
14192
|
120 g_memdup(res->ai_addr, res->ai_addrlen));
|
|
121 res = res->ai_next;
|
|
122 }
|
|
123 freeaddrinfo(tmp);
|
|
124 } else {
|
14238
|
125 query_data->error_message = g_strdup_printf(_("Error resolving %s: %s"), query_data->hostname, gai_strerror(rc));
|
14192
|
126 }
|
|
127
|
14263
|
128 /* We're done, tell the main thread to look at our results */
|
14238
|
129 g_idle_add(dns_main_thread_cb, query_data);
|
|
130
|
14192
|
131 return 0;
|
|
132 }
|
14263
|
133 #endif
|
14192
|
134
|
14238
|
135 static gboolean
|
|
136 resolve_host(gpointer data)
|
14192
|
137 {
|
14238
|
138 GaimDnsQueryData *query_data;
|
14192
|
139 struct sockaddr_in sin;
|
14238
|
140 GError *err = NULL;
|
14192
|
141
|
14238
|
142 query_data = data;
|
|
143 query_data->timeout = 0;
|
|
144
|
|
145 if (inet_aton(query_data->hostname, &sin.sin_addr))
|
|
146 {
|
14244
|
147 /*
|
|
148 * The given "hostname" is actually an IP address, so we
|
|
149 * don't need to do anything.
|
|
150 */
|
14192
|
151 GSList *hosts = NULL;
|
|
152 sin.sin_family = AF_INET;
|
14238
|
153 sin.sin_port = htons(query_data->port);
|
14192
|
154 hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin)));
|
|
155 hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin)));
|
14238
|
156 gaim_dnsquery_resolved(query_data, hosts);
|
|
157 }
|
|
158 else
|
|
159 {
|
14263
|
160 #ifdef HAVE_GETADDRINFO
|
14244
|
161 /*
|
|
162 * Spin off a separate thread to perform the DNS lookup so
|
|
163 * that we don't block the UI.
|
|
164 */
|
14238
|
165 query_data->resolver = g_thread_create(dns_thread,
|
|
166 query_data, FALSE, &err);
|
|
167 if (query_data->resolver == NULL)
|
|
168 {
|
|
169 char message[1024];
|
|
170 g_snprintf(message, sizeof(message), _("Thread creation failure: %s"),
|
|
171 err ? err->message : _("Unknown reason"));
|
|
172 g_error_free(err);
|
|
173 gaim_dnsquery_failed(query_data, message);
|
|
174 }
|
14263
|
175 #else
|
|
176 struct sockaddr_in sin;
|
|
177 struct hostent *hp;
|
|
178
|
|
179 /*
|
|
180 * gethostbyname() is not threadsafe, but gethostbyname_r() is a GNU
|
|
181 * extension. Unfortunately this means that we'll have to do a
|
|
182 * blocking DNS query for systems without GETADDRINFO. Fortunately
|
|
183 * this should be a very small number of systems.
|
|
184 */
|
|
185
|
|
186 if ((hp = gethostbyname(query_data->hostname))) {
|
|
187 memset(&sin, 0, sizeof(struct sockaddr_in));
|
|
188 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
|
|
189 sin.sin_family = hp->h_addrtype;
|
|
190 sin.sin_port = htons(query_data->port);
|
|
191
|
|
192 query_data->hosts = g_slist_append(query_data->hosts,
|
|
193 GSIZE_TO_POINTER(sizeof(sin)));
|
|
194 query_data->hosts = g_slist_append(query_data->hosts,
|
|
195 g_memdup(&sin, sizeof(sin)));
|
|
196 } else {
|
|
197 query_data->error_message = g_strdup_printf(_("Error resolving %s: %d"), query_data->hostname, h_errno);
|
|
198 }
|
|
199
|
|
200 /* We're done! */
|
|
201 dns_main_thread(query_data);
|
|
202 #endif
|
14192
|
203 }
|
|
204
|
|
205 return FALSE;
|
|
206 }
|
|
207
|
|
208 GaimDnsQueryData *
|
|
209 gaim_dnsquery_a(const char *hostname, int port,
|
14238
|
210 GaimDnsQueryConnectFunction callback, gpointer data)
|
14192
|
211 {
|
14238
|
212 GaimDnsQueryData *query_data;
|
|
213
|
|
214 g_return_val_if_fail(hostname != NULL, NULL);
|
|
215 g_return_val_if_fail(port != 0, NULL);
|
|
216
|
14244
|
217 gaim_debug_info("dnsquery", "Performing DNS lookup for %s\n", hostname);
|
|
218
|
14238
|
219 query_data = g_new(GaimDnsQueryData, 1);
|
|
220 query_data->hostname = g_strdup(hostname);
|
|
221 g_strstrip(query_data->hostname);
|
|
222 query_data->port = port;
|
|
223 query_data->callback = callback;
|
|
224 query_data->data = data;
|
|
225 query_data->error_message = NULL;
|
|
226 query_data->hosts = NULL;
|
|
227
|
|
228 /* Don't call the callback before returning */
|
|
229 query_data->timeout = gaim_timeout_add(0, resolve_host, query_data);
|
|
230
|
|
231 return query_data;
|
|
232 }
|
|
233
|
|
234 void
|
|
235 gaim_dnsquery_destroy(GaimDnsQueryData *query_data)
|
|
236 {
|
|
237 if (query_data->resolver != NULL)
|
|
238 {
|
|
239 /*
|
|
240 * It's not really possible to kill a thread. So instead we
|
|
241 * just set the callback to NULL and let the DNS lookup
|
|
242 * finish.
|
|
243 */
|
|
244 query_data->callback = NULL;
|
|
245 return;
|
|
246 }
|
|
247
|
|
248 while (query_data->hosts != NULL)
|
|
249 {
|
|
250 /* Discard the length... */
|
|
251 query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data);
|
|
252 /* Free the address... */
|
|
253 g_free(query_data->hosts->data);
|
|
254 query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data);
|
|
255 }
|
|
256 g_free(query_data->error_message);
|
|
257
|
|
258 if (query_data->timeout > 0)
|
|
259 gaim_timeout_remove(query_data->timeout);
|
|
260
|
|
261 g_free(query_data->hostname);
|
|
262 g_free(query_data);
|
|
263 }
|
|
264
|
|
265 void
|
|
266 gaim_dnsquery_init(void)
|
|
267 {
|
|
268 if (!g_thread_supported())
|
|
269 g_thread_init(NULL);
|
|
270 }
|
|
271
|
|
272 void
|
|
273 gaim_dnsquery_uninit(void)
|
|
274 {
|
|
275 }
|