Mercurial > pidgin.yaz
comparison libgaim/dnssrv.c @ 14192:60b1bc8dbf37
[gaim-migrate @ 16863]
Renamed 'core' to 'libgaim'
committer: Tailor Script <tailor@pidgin.im>
author | Evan Schoenberg <evan.s@dreskin.net> |
---|---|
date | Sat, 19 Aug 2006 01:50:10 +0000 |
parents | |
children | 9ad313800b19 |
comparison
equal
deleted
inserted
replaced
14191:009db0b357b5 | 14192:60b1bc8dbf37 |
---|---|
1 /** | |
2 * @file dnssrv.c | |
3 * | |
4 * gaim | |
5 * | |
6 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de> | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU General Public License | |
19 * along with this program; if not, write to the Free Software | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 */ | |
22 | |
23 #include "internal.h" | |
24 | |
25 #ifndef _WIN32 | |
26 #include <resolv.h> | |
27 #include <arpa/nameser.h> | |
28 #ifdef HAVE_ARPA_NAMESER_COMPAT_H | |
29 #include <arpa/nameser_compat.h> | |
30 #endif | |
31 #ifndef T_SRV | |
32 #define T_SRV 33 | |
33 #endif | |
34 #else | |
35 #include <windns.h> | |
36 /* Missing from the mingw headers */ | |
37 #ifndef DNS_TYPE_SRV | |
38 # define DNS_TYPE_SRV 33 | |
39 #endif | |
40 #endif | |
41 | |
42 #include "dnssrv.h" | |
43 #include "eventloop.h" | |
44 #include "debug.h" | |
45 | |
46 #ifndef _WIN32 | |
47 typedef union { | |
48 HEADER hdr; | |
49 u_char buf[1024]; | |
50 } queryans; | |
51 #else | |
52 static DNS_STATUS WINAPI (*MyDnsQuery_UTF8) ( | |
53 PCSTR lpstrName, WORD wType, DWORD fOptions, | |
54 PIP4_ARRAY aipServers, PDNS_RECORD* ppQueryResultsSet, | |
55 PVOID* pReserved) = NULL; | |
56 static void WINAPI (*MyDnsRecordListFree) (PDNS_RECORD pRecordList, | |
57 DNS_FREE_TYPE FreeType) = NULL; | |
58 #endif | |
59 | |
60 struct resdata { | |
61 GaimSRVCallback cb; | |
62 gpointer extradata; | |
63 #ifndef _WIN32 | |
64 guint handle; | |
65 #else | |
66 char *query; | |
67 char *errmsg; | |
68 GSList *results; | |
69 #endif | |
70 }; | |
71 | |
72 static gint responsecompare(gconstpointer ar, gconstpointer br) { | |
73 GaimSrvResponse *a = (GaimSrvResponse*)ar; | |
74 GaimSrvResponse *b = (GaimSrvResponse*)br; | |
75 | |
76 if(a->pref == b->pref) { | |
77 if(a->weight == b->weight) | |
78 return 0; | |
79 if(a->weight < b->weight) | |
80 return -1; | |
81 return 1; | |
82 } | |
83 if(a->pref < b->pref) | |
84 return -1; | |
85 return 1; | |
86 } | |
87 #ifndef _WIN32 | |
88 static void resolve(int in, int out) { | |
89 GList *ret = NULL; | |
90 GaimSrvResponse *srvres; | |
91 queryans answer; | |
92 int size; | |
93 int qdcount; | |
94 int ancount; | |
95 guchar *end; | |
96 guchar *cp; | |
97 gchar name[256]; | |
98 guint16 type, dlen, pref, weight, port; | |
99 gchar query[256]; | |
100 | |
101 if(read(in, query, 256) <= 0) { | |
102 _exit(0); | |
103 } | |
104 size = res_query( query, C_IN, T_SRV, (u_char*)&answer, sizeof( answer)); | |
105 | |
106 qdcount = ntohs(answer.hdr.qdcount); | |
107 ancount = ntohs(answer.hdr.ancount); | |
108 | |
109 | |
110 cp = (guchar*)&answer + sizeof(HEADER); | |
111 end = (guchar*)&answer + size; | |
112 | |
113 /* skip over unwanted stuff */ | |
114 while (qdcount-- > 0 && cp < end) { | |
115 size = dn_expand( (unsigned char*)&answer, end, cp, name, 256); | |
116 if(size < 0) goto end; | |
117 cp += size + QFIXEDSZ; | |
118 } | |
119 | |
120 while (ancount-- > 0 && cp < end) { | |
121 size = dn_expand((unsigned char*)&answer, end, cp, name, 256); | |
122 if(size < 0) | |
123 goto end; | |
124 | |
125 cp += size; | |
126 | |
127 GETSHORT(type,cp); | |
128 | |
129 /* skip ttl and class since we already know it */ | |
130 cp += 6; | |
131 | |
132 GETSHORT(dlen,cp); | |
133 | |
134 if (type == T_SRV) { | |
135 GETSHORT(pref,cp); | |
136 | |
137 GETSHORT(weight,cp); | |
138 | |
139 GETSHORT(port,cp); | |
140 | |
141 size = dn_expand( (unsigned char*)&answer, end, cp, name, 256); | |
142 if(size < 0 ) | |
143 goto end; | |
144 | |
145 cp += size; | |
146 | |
147 srvres = g_new0(GaimSrvResponse, 1); | |
148 strcpy(srvres->hostname, name); | |
149 srvres->pref = pref; | |
150 srvres->port = port; | |
151 srvres->weight = weight; | |
152 | |
153 ret = g_list_insert_sorted(ret, srvres, responsecompare); | |
154 } else { | |
155 cp += dlen; | |
156 } | |
157 } | |
158 end: size = g_list_length(ret); | |
159 write(out, &size, sizeof(int)); | |
160 while(g_list_first(ret)) { | |
161 write(out, g_list_first(ret)->data, sizeof(GaimSrvResponse)); | |
162 g_free(g_list_first(ret)->data); | |
163 ret = g_list_remove(ret, g_list_first(ret)->data); | |
164 } | |
165 | |
166 /* Should the resolver be reused? | |
167 * There is most likely only 1 SRV queries per prpl... | |
168 */ | |
169 _exit(0); | |
170 } | |
171 | |
172 static void resolved(gpointer data, gint source, GaimInputCondition cond) { | |
173 int size; | |
174 struct resdata *rdata = (struct resdata*)data; | |
175 GaimSrvResponse *res; | |
176 GaimSrvResponse *tmp; | |
177 int i; | |
178 GaimSRVCallback cb = rdata->cb; | |
179 | |
180 read(source, &size, sizeof(int)); | |
181 gaim_debug_info("srv","found %d SRV entries\n", size); | |
182 tmp = res = g_new0(GaimSrvResponse, size); | |
183 i = size; | |
184 while(i) { | |
185 read(source, tmp++, sizeof(GaimSrvResponse)); | |
186 i--; | |
187 } | |
188 cb(res, size, rdata->extradata); | |
189 gaim_input_remove(rdata->handle); | |
190 g_free(rdata); | |
191 } | |
192 | |
193 #else /* _WIN32 */ | |
194 | |
195 /** The Jabber Server code was inspiration for parts of this. */ | |
196 | |
197 static gboolean res_main_thread_cb(gpointer data) { | |
198 GaimSrvResponse *srvres = NULL; | |
199 int size = 0; | |
200 struct resdata *rdata = data; | |
201 | |
202 if (rdata->errmsg != NULL) { | |
203 gaim_debug_error("srv", rdata->errmsg); | |
204 g_free(rdata->errmsg); | |
205 } else { | |
206 GaimSrvResponse *srvres_tmp; | |
207 GSList *lst = rdata->results; | |
208 | |
209 size = g_slist_length(rdata->results); | |
210 | |
211 srvres_tmp = srvres = g_new0(GaimSrvResponse, size); | |
212 while (lst) { | |
213 memcpy(srvres_tmp++, lst->data, sizeof(GaimSrvResponse)); | |
214 g_free(lst->data); | |
215 lst = g_slist_remove(lst, lst->data); | |
216 } | |
217 | |
218 rdata->results = lst; | |
219 } | |
220 | |
221 gaim_debug_info("srv", "found %d SRV entries\n", size); | |
222 | |
223 rdata->cb(srvres, size, rdata->extradata); | |
224 | |
225 g_free(rdata->query); | |
226 g_free(rdata); | |
227 | |
228 return FALSE; | |
229 } | |
230 | |
231 static gpointer res_thread(gpointer data) { | |
232 PDNS_RECORD dr = NULL; | |
233 int type = DNS_TYPE_SRV; | |
234 DNS_STATUS ds; | |
235 struct resdata *rdata = data; | |
236 | |
237 ds = MyDnsQuery_UTF8(rdata->query, type, DNS_QUERY_STANDARD, NULL, &dr, NULL); | |
238 if (ds != ERROR_SUCCESS) { | |
239 gchar *msg = g_win32_error_message(ds); | |
240 rdata->errmsg = g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg, ds); | |
241 g_free(msg); | |
242 } else { | |
243 PDNS_RECORD dr_tmp; | |
244 GSList *lst = NULL; | |
245 DNS_SRV_DATA *srv_data; | |
246 GaimSrvResponse *srvres; | |
247 | |
248 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { | |
249 /* Discard any incorrect entries. I'm not sure if this is necessary */ | |
250 if (dr_tmp->wType != type || strcmp(dr_tmp->pName, rdata->query) != 0) { | |
251 continue; | |
252 } | |
253 | |
254 srv_data = &dr_tmp->Data.SRV; | |
255 srvres = g_new0(GaimSrvResponse, 1); | |
256 strncpy(srvres->hostname, srv_data->pNameTarget, 255); | |
257 srvres->hostname[255] = '\0'; | |
258 srvres->pref = srv_data->wPriority; | |
259 srvres->port = srv_data->wPort; | |
260 srvres->weight = srv_data->wWeight; | |
261 | |
262 lst = g_slist_insert_sorted(lst, srvres, responsecompare); | |
263 } | |
264 | |
265 MyDnsRecordListFree(dr, DnsFreeRecordList); | |
266 rdata->results = lst; | |
267 } | |
268 | |
269 /* back to main thread */ | |
270 g_idle_add(res_main_thread_cb, rdata); | |
271 | |
272 g_thread_exit(NULL); | |
273 return NULL; | |
274 } | |
275 | |
276 #endif | |
277 | |
278 /* | |
279 * TODO: It would be really good if this returned some sort of handle | |
280 * that we could use to cancel the DNS query. As it is now, | |
281 * each callback has to check to make sure gc is still valid. | |
282 * And that is ugly. | |
283 */ | |
284 void gaim_srv_resolve(const char *protocol, const char *transport, const char *domain, GaimSRVCallback cb, gpointer extradata) { | |
285 char *query = g_strdup_printf("_%s._%s.%s",protocol, transport, domain); | |
286 struct resdata *rdata; | |
287 #ifndef _WIN32 | |
288 int in[2], out[2]; | |
289 int pid; | |
290 gaim_debug_info("srv","querying SRV record for %s\n", query); | |
291 if(pipe(in) || pipe(out)) { | |
292 gaim_debug_error("srv", "Could not create pipe\n"); | |
293 g_free(query); | |
294 cb(NULL, 0, extradata); | |
295 return; | |
296 } | |
297 | |
298 pid = fork(); | |
299 | |
300 if(pid == -1) { | |
301 gaim_debug_error("srv","Could not create process!\n"); | |
302 cb(NULL, 0, extradata); | |
303 g_free(query); | |
304 return; | |
305 } | |
306 /* Child */ | |
307 if( pid == 0 ) { | |
308 close(out[0]); | |
309 close(in[1]); | |
310 resolve(in[0], out[1]); | |
311 } | |
312 | |
313 close(out[1]); | |
314 close(in[0]); | |
315 | |
316 if(write(in[1], query, strlen(query)+1)<0) { | |
317 gaim_debug_error("srv", "Could not write to SRV resolver\n"); | |
318 } | |
319 rdata = g_new0(struct resdata,1); | |
320 rdata->cb = cb; | |
321 rdata->extradata = extradata; | |
322 rdata->handle = gaim_input_add(out[0], GAIM_INPUT_READ, resolved, rdata); | |
323 | |
324 g_free(query); | |
325 #else | |
326 GError* err = NULL; | |
327 | |
328 static gboolean initialized = FALSE; | |
329 | |
330 gaim_debug_info("srv","querying SRV record for %s\n", query); | |
331 | |
332 if (!initialized) { | |
333 MyDnsQuery_UTF8 = (void*) wgaim_find_and_loadproc("dnsapi.dll", "DnsQuery_UTF8"); | |
334 MyDnsRecordListFree = (void*) wgaim_find_and_loadproc( | |
335 "dnsapi.dll", "DnsRecordListFree"); | |
336 initialized = TRUE; | |
337 } | |
338 | |
339 if (!MyDnsQuery_UTF8 || !MyDnsRecordListFree) { | |
340 gaim_debug_error("srv", "System missing DNS API (Requires W2K+)\n"); | |
341 g_free(query); | |
342 cb(NULL, 0, extradata); | |
343 return; | |
344 } | |
345 | |
346 rdata = g_new0(struct resdata, 1); | |
347 rdata->cb = cb; | |
348 rdata->query = query; | |
349 rdata->extradata = extradata; | |
350 | |
351 if (!g_thread_create(res_thread, rdata, FALSE, &err)) { | |
352 rdata->errmsg = g_strdup_printf("SRV thread create failure: %s\n", err ? err->message : ""); | |
353 g_error_free(err); | |
354 res_main_thread_cb(rdata); | |
355 } | |
356 #endif | |
357 } | |
358 |