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
|
14238
|
92 static gpointer
|
|
93 dns_thread(gpointer data)
|
|
94 {
|
|
95 GaimDnsQueryData *query_data;
|
14192
|
96 #ifdef HAVE_GETADDRINFO
|
|
97 int rc;
|
|
98 struct addrinfo hints, *res, *tmp;
|
|
99 char servname[20];
|
|
100 #else
|
|
101 struct sockaddr_in sin;
|
|
102 struct hostent *hp;
|
|
103 #endif
|
14238
|
104
|
|
105 query_data = data;
|
14192
|
106
|
|
107 #ifdef HAVE_GETADDRINFO
|
14238
|
108 g_snprintf(servname, sizeof(servname), "%d", query_data->port);
|
14192
|
109 memset(&hints,0,sizeof(hints));
|
|
110
|
14238
|
111 /*
|
|
112 * This is only used to convert a service
|
14192
|
113 * name to a port number. As we know we are
|
|
114 * passing a number already, we know this
|
|
115 * value will not be really used by the C
|
|
116 * library.
|
|
117 */
|
|
118 hints.ai_socktype = SOCK_STREAM;
|
14238
|
119 if ((rc = getaddrinfo(query_data->hostname, servname, &hints, &res)) == 0) {
|
14192
|
120 tmp = res;
|
|
121 while(res) {
|
14238
|
122 query_data->hosts = g_slist_append(query_data->hosts,
|
14192
|
123 GSIZE_TO_POINTER(res->ai_addrlen));
|
14238
|
124 query_data->hosts = g_slist_append(query_data->hosts,
|
14192
|
125 g_memdup(res->ai_addr, res->ai_addrlen));
|
|
126 res = res->ai_next;
|
|
127 }
|
|
128 freeaddrinfo(tmp);
|
|
129 } else {
|
14238
|
130 query_data->error_message = g_strdup_printf(_("Error resolving %s: %s"), query_data->hostname, gai_strerror(rc));
|
14192
|
131 }
|
|
132 #else
|
14238
|
133 if ((hp = gethostbyname(query_data->hostname))) {
|
14192
|
134 memset(&sin, 0, sizeof(struct sockaddr_in));
|
|
135 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
|
|
136 sin.sin_family = hp->h_addrtype;
|
14238
|
137 sin.sin_port = htons(query_data->port);
|
14192
|
138
|
14238
|
139 query_data->hosts = g_slist_append(query_data->hosts,
|
14192
|
140 GSIZE_TO_POINTER(sizeof(sin)));
|
14238
|
141 query_data->hosts = g_slist_append(query_data->hosts,
|
14192
|
142 g_memdup(&sin, sizeof(sin)));
|
|
143 } else {
|
14238
|
144 query_data->error_message = g_strdup_printf(_("Error resolving %s: %d"), query_data->hostname, h_errno);
|
14192
|
145 }
|
|
146 #endif
|
14238
|
147
|
14192
|
148 /* back to main thread */
|
14238
|
149 g_idle_add(dns_main_thread_cb, query_data);
|
|
150
|
14192
|
151 return 0;
|
|
152 }
|
|
153
|
14238
|
154 static gboolean
|
|
155 resolve_host(gpointer data)
|
14192
|
156 {
|
14238
|
157 GaimDnsQueryData *query_data;
|
14192
|
158 struct sockaddr_in sin;
|
14238
|
159 GError *err = NULL;
|
14192
|
160
|
14238
|
161 query_data = data;
|
|
162 query_data->timeout = 0;
|
|
163
|
|
164 if (inet_aton(query_data->hostname, &sin.sin_addr))
|
|
165 {
|
14244
|
166 /*
|
|
167 * The given "hostname" is actually an IP address, so we
|
|
168 * don't need to do anything.
|
|
169 */
|
14192
|
170 GSList *hosts = NULL;
|
|
171 sin.sin_family = AF_INET;
|
14238
|
172 sin.sin_port = htons(query_data->port);
|
14192
|
173 hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin)));
|
|
174 hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin)));
|
14238
|
175 gaim_dnsquery_resolved(query_data, hosts);
|
|
176 }
|
|
177 else
|
|
178 {
|
14244
|
179 /*
|
|
180 * Spin off a separate thread to perform the DNS lookup so
|
|
181 * that we don't block the UI.
|
|
182 */
|
14238
|
183 query_data->resolver = g_thread_create(dns_thread,
|
|
184 query_data, FALSE, &err);
|
|
185 if (query_data->resolver == NULL)
|
|
186 {
|
|
187 char message[1024];
|
|
188 g_snprintf(message, sizeof(message), _("Thread creation failure: %s"),
|
|
189 err ? err->message : _("Unknown reason"));
|
|
190 g_error_free(err);
|
|
191 gaim_dnsquery_failed(query_data, message);
|
|
192 }
|
14192
|
193 }
|
|
194
|
|
195 return FALSE;
|
|
196 }
|
|
197
|
|
198 GaimDnsQueryData *
|
|
199 gaim_dnsquery_a(const char *hostname, int port,
|
14238
|
200 GaimDnsQueryConnectFunction callback, gpointer data)
|
14192
|
201 {
|
14238
|
202 GaimDnsQueryData *query_data;
|
|
203
|
|
204 g_return_val_if_fail(hostname != NULL, NULL);
|
|
205 g_return_val_if_fail(port != 0, NULL);
|
|
206
|
14244
|
207 gaim_debug_info("dnsquery", "Performing DNS lookup for %s\n", hostname);
|
|
208
|
14238
|
209 query_data = g_new(GaimDnsQueryData, 1);
|
|
210 query_data->hostname = g_strdup(hostname);
|
|
211 g_strstrip(query_data->hostname);
|
|
212 query_data->port = port;
|
|
213 query_data->callback = callback;
|
|
214 query_data->data = data;
|
|
215 query_data->error_message = NULL;
|
|
216 query_data->hosts = NULL;
|
|
217
|
|
218 /* Don't call the callback before returning */
|
|
219 query_data->timeout = gaim_timeout_add(0, resolve_host, query_data);
|
|
220
|
|
221 return query_data;
|
|
222 }
|
|
223
|
|
224 void
|
|
225 gaim_dnsquery_destroy(GaimDnsQueryData *query_data)
|
|
226 {
|
|
227 if (query_data->resolver != NULL)
|
|
228 {
|
|
229 /*
|
|
230 * It's not really possible to kill a thread. So instead we
|
|
231 * just set the callback to NULL and let the DNS lookup
|
|
232 * finish.
|
|
233 */
|
|
234 query_data->callback = NULL;
|
|
235 return;
|
|
236 }
|
|
237
|
|
238 while (query_data->hosts != NULL)
|
|
239 {
|
|
240 /* Discard the length... */
|
|
241 query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data);
|
|
242 /* Free the address... */
|
|
243 g_free(query_data->hosts->data);
|
|
244 query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data);
|
|
245 }
|
|
246 g_free(query_data->error_message);
|
|
247
|
|
248 if (query_data->timeout > 0)
|
|
249 gaim_timeout_remove(query_data->timeout);
|
|
250
|
|
251 g_free(query_data->hostname);
|
|
252 g_free(query_data);
|
|
253 }
|
|
254
|
|
255 void
|
|
256 gaim_dnsquery_init(void)
|
|
257 {
|
|
258 if (!g_thread_supported())
|
|
259 g_thread_init(NULL);
|
|
260 }
|
|
261
|
|
262 void
|
|
263 gaim_dnsquery_uninit(void)
|
|
264 {
|
|
265 }
|