| 153 ret = g_list_insert_sorted(ret, srvres, responsecompare); |
159 ret = g_list_insert_sorted(ret, srvres, responsecompare); |
| 154 } else { |
160 } else { |
| 155 cp += dlen; |
161 cp += dlen; |
| 156 } |
162 } |
| 157 } |
163 } |
| 158 end: size = g_list_length(ret); |
164 |
| |
165 end: |
| |
166 size = g_list_length(ret); |
| 159 write(out, &size, sizeof(int)); |
167 write(out, &size, sizeof(int)); |
| 160 while(g_list_first(ret)) { |
168 while (ret != NULL) |
| 161 write(out, g_list_first(ret)->data, sizeof(GaimSrvResponse)); |
169 { |
| 162 g_free(g_list_first(ret)->data); |
170 write(out, ret->data, sizeof(GaimSrvResponse)); |
| 163 ret = g_list_remove(ret, g_list_first(ret)->data); |
171 g_free(ret->data); |
| 164 } |
172 ret = g_list_remove(ret, ret->data); |
| 165 |
173 } |
| 166 /* Should the resolver be reused? |
174 |
| 167 * There is most likely only 1 SRV queries per prpl... |
|
| 168 */ |
|
| 169 _exit(0); |
175 _exit(0); |
| 170 } |
176 } |
| 171 |
177 |
| 172 static void resolved(gpointer data, gint source, GaimInputCondition cond) { |
178 static void |
| |
179 resolved(gpointer data, gint source, GaimInputCondition cond) |
| |
180 { |
| 173 int size; |
181 int size; |
| 174 struct resdata *rdata = (struct resdata*)data; |
182 GaimSrvQueryData *query_data = (GaimSrvQueryData*)data; |
| 175 GaimSrvResponse *res; |
183 GaimSrvResponse *res; |
| 176 GaimSrvResponse *tmp; |
184 GaimSrvResponse *tmp; |
| 177 int i; |
185 int i; |
| 178 GaimSRVCallback cb = rdata->cb; |
186 GaimSrvCallback cb = query_data->cb; |
| 179 |
187 |
| 180 read(source, &size, sizeof(int)); |
188 read(source, &size, sizeof(int)); |
| 181 gaim_debug_info("srv","found %d SRV entries\n", size); |
189 gaim_debug_info("dnssrv","found %d SRV entries\n", size); |
| 182 tmp = res = g_new0(GaimSrvResponse, size); |
190 tmp = res = g_new0(GaimSrvResponse, size); |
| 183 i = size; |
191 for (i = 0; i < size; i++) { |
| 184 while(i) { |
|
| 185 read(source, tmp++, sizeof(GaimSrvResponse)); |
192 read(source, tmp++, sizeof(GaimSrvResponse)); |
| 186 i--; |
193 } |
| 187 } |
194 |
| 188 cb(res, size, rdata->extradata); |
195 cb(res, size, query_data->extradata); |
| 189 gaim_input_remove(rdata->handle); |
196 |
| 190 g_free(rdata); |
197 gaim_srv_cancel(query_data); |
| 191 } |
198 } |
| 192 |
199 |
| 193 #else /* _WIN32 */ |
200 #else /* _WIN32 */ |
| 194 |
201 |
| 195 /** The Jabber Server code was inspiration for parts of this. */ |
202 /** The Jabber Server code was inspiration for parts of this. */ |
| 196 |
203 |
| 197 static gboolean res_main_thread_cb(gpointer data) { |
204 static gboolean |
| |
205 res_main_thread_cb(gpointer data) |
| |
206 { |
| 198 GaimSrvResponse *srvres = NULL; |
207 GaimSrvResponse *srvres = NULL; |
| 199 int size = 0; |
208 int size = 0; |
| 200 struct resdata *rdata = data; |
209 GaimSrvQueryData *query_data = data; |
| 201 |
210 |
| 202 if (rdata->errmsg != NULL) { |
211 if (query_data->error_message != NULL) { |
| 203 gaim_debug_error("srv", rdata->errmsg); |
212 gaim_debug_error("dnssrv", query_data->error_message); |
| 204 g_free(rdata->errmsg); |
|
| 205 } else { |
213 } else { |
| 206 GaimSrvResponse *srvres_tmp; |
214 GaimSrvResponse *srvres_tmp; |
| 207 GSList *lst = rdata->results; |
215 GSList *lst = query_data->results; |
| 208 |
216 |
| 209 size = g_slist_length(rdata->results); |
217 size = g_slist_length(query_data->results); |
| 210 |
218 |
| 211 srvres_tmp = srvres = g_new0(GaimSrvResponse, size); |
219 srvres_tmp = srvres = g_new0(GaimSrvResponse, size); |
| 212 while (lst) { |
220 while (lst) { |
| 213 memcpy(srvres_tmp++, lst->data, sizeof(GaimSrvResponse)); |
221 memcpy(srvres_tmp++, lst->data, sizeof(GaimSrvResponse)); |
| 214 g_free(lst->data); |
222 g_free(lst->data); |
| 215 lst = g_slist_remove(lst, lst->data); |
223 lst = g_slist_remove(lst, lst->data); |
| 216 } |
224 } |
| 217 |
225 |
| 218 rdata->results = lst; |
226 query_data->results = lst; |
| 219 } |
227 } |
| 220 |
228 |
| 221 gaim_debug_info("srv", "found %d SRV entries\n", size); |
229 gaim_debug_info("dnssrv", "found %d SRV entries\n", size); |
| 222 |
230 |
| 223 rdata->cb(srvres, size, rdata->extradata); |
231 query_data->cb(srvres, size, query_data->extradata); |
| 224 |
232 |
| 225 g_free(rdata->query); |
233 gaim_srv_cancel(query_data); |
| 226 g_free(rdata); |
|
| 227 |
234 |
| 228 return FALSE; |
235 return FALSE; |
| 229 } |
236 } |
| 230 |
237 |
| 231 static gpointer res_thread(gpointer data) { |
238 static gpointer |
| |
239 res_thread(gpointer data) |
| |
240 { |
| 232 PDNS_RECORD dr = NULL; |
241 PDNS_RECORD dr = NULL; |
| 233 int type = DNS_TYPE_SRV; |
242 int type = DNS_TYPE_SRV; |
| 234 DNS_STATUS ds; |
243 DNS_STATUS ds; |
| 235 struct resdata *rdata = data; |
244 GaimSrvQueryData *query_data = data; |
| 236 |
245 |
| 237 ds = MyDnsQuery_UTF8(rdata->query, type, DNS_QUERY_STANDARD, NULL, &dr, NULL); |
246 ds = MyDnsQuery_UTF8(query_data->query, type, DNS_QUERY_STANDARD, NULL, &dr, NULL); |
| 238 if (ds != ERROR_SUCCESS) { |
247 if (ds != ERROR_SUCCESS) { |
| 239 gchar *msg = g_win32_error_message(ds); |
248 gchar *msg = g_win32_error_message(ds); |
| 240 rdata->errmsg = g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg, ds); |
249 query_data->error_message = g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg, ds); |
| 241 g_free(msg); |
250 g_free(msg); |
| 242 } else { |
251 } else { |
| 243 PDNS_RECORD dr_tmp; |
252 PDNS_RECORD dr_tmp; |
| 244 GSList *lst = NULL; |
253 GSList *lst = NULL; |
| 245 DNS_SRV_DATA *srv_data; |
254 DNS_SRV_DATA *srv_data; |
| 246 GaimSrvResponse *srvres; |
255 GaimSrvResponse *srvres; |
| 247 |
256 |
| 248 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { |
257 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 */ |
258 /* 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) { |
259 if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { |
| 251 continue; |
260 continue; |
| 252 } |
261 } |
| 253 |
262 |
| 254 srv_data = &dr_tmp->Data.SRV; |
263 srv_data = &dr_tmp->Data.SRV; |
| 255 srvres = g_new0(GaimSrvResponse, 1); |
264 srvres = g_new0(GaimSrvResponse, 1); |
| 261 |
270 |
| 262 lst = g_slist_insert_sorted(lst, srvres, responsecompare); |
271 lst = g_slist_insert_sorted(lst, srvres, responsecompare); |
| 263 } |
272 } |
| 264 |
273 |
| 265 MyDnsRecordListFree(dr, DnsFreeRecordList); |
274 MyDnsRecordListFree(dr, DnsFreeRecordList); |
| 266 rdata->results = lst; |
275 query_data->results = lst; |
| 267 } |
276 } |
| 268 |
277 |
| 269 /* back to main thread */ |
278 /* back to main thread */ |
| 270 g_idle_add(res_main_thread_cb, rdata); |
279 g_idle_add(res_main_thread_cb, query_data); |
| 271 |
280 |
| 272 g_thread_exit(NULL); |
281 g_thread_exit(NULL); |
| 273 return NULL; |
282 return NULL; |
| 274 } |
283 } |
| 275 |
284 |
| 276 #endif |
285 #endif |
| 277 |
286 |
| 278 /* |
287 GaimSrvQueryData * |
| 279 * TODO: It would be really good if this returned some sort of handle |
288 gaim_srv_resolve(const char *protocol, const char *transport, const char *domain, GaimSrvCallback cb, gpointer extradata) |
| 280 * that we could use to cancel the DNS query. As it is now, |
289 { |
| 281 * each callback has to check to make sure gc is still valid. |
290 char *query; |
| 282 * And that is ugly. |
291 GaimSrvQueryData *query_data; |
| 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 |
292 #ifndef _WIN32 |
| 288 int in[2], out[2]; |
293 int in[2], out[2]; |
| 289 int pid; |
294 int pid; |
| 290 gaim_debug_info("srv","querying SRV record for %s\n", query); |
295 #else |
| |
296 GError* err = NULL; |
| |
297 static gboolean initialized = FALSE; |
| |
298 #endif |
| |
299 |
| |
300 #ifndef _WIN32 |
| |
301 query = g_strdup_printf("_%s._%s.%s", protocol, transport, domain); |
| |
302 gaim_debug_info("dnssrv","querying SRV record for %s\n", query); |
| |
303 |
| 291 if(pipe(in) || pipe(out)) { |
304 if(pipe(in) || pipe(out)) { |
| 292 gaim_debug_error("srv", "Could not create pipe\n"); |
305 gaim_debug_error("dnssrv", "Could not create pipe\n"); |
| 293 g_free(query); |
306 g_free(query); |
| 294 cb(NULL, 0, extradata); |
307 cb(NULL, 0, extradata); |
| 295 return; |
308 return NULL; |
| 296 } |
309 } |
| 297 |
310 |
| 298 pid = fork(); |
311 pid = fork(); |
| 299 |
312 if (pid == -1) { |
| 300 if(pid == -1) { |
313 gaim_debug_error("dnssrv", "Could not create process!\n"); |
| 301 gaim_debug_error("srv","Could not create process!\n"); |
|
| 302 cb(NULL, 0, extradata); |
314 cb(NULL, 0, extradata); |
| 303 g_free(query); |
315 g_free(query); |
| 304 return; |
316 return NULL; |
| 305 } |
317 } |
| |
318 |
| 306 /* Child */ |
319 /* Child */ |
| 307 if( pid == 0 ) { |
320 if (pid == 0) |
| |
321 { |
| 308 close(out[0]); |
322 close(out[0]); |
| 309 close(in[1]); |
323 close(in[1]); |
| 310 resolve(in[0], out[1]); |
324 resolve(in[0], out[1]); |
| 311 } |
325 } |
| 312 |
326 |
| 313 close(out[1]); |
327 close(out[1]); |
| 314 close(in[0]); |
328 close(in[0]); |
| 315 |
329 |
| 316 if(write(in[1], query, strlen(query)+1)<0) { |
330 if (write(in[1], query, strlen(query)+1) < 0) |
| 317 gaim_debug_error("srv", "Could not write to SRV resolver\n"); |
331 gaim_debug_error("dnssrv", "Could not write to SRV resolver\n"); |
| 318 } |
332 |
| 319 rdata = g_new0(struct resdata,1); |
333 query_data = g_new0(GaimSrvQueryData, 1); |
| 320 rdata->cb = cb; |
334 query_data->cb = cb; |
| 321 rdata->extradata = extradata; |
335 query_data->extradata = extradata; |
| 322 rdata->handle = gaim_input_add(out[0], GAIM_INPUT_READ, resolved, rdata); |
336 query_data->handle = gaim_input_add(out[0], GAIM_INPUT_READ, resolved, query_data); |
| 323 |
337 |
| 324 g_free(query); |
338 g_free(query); |
| 325 #else |
339 |
| 326 GError* err = NULL; |
340 return query_data; |
| 327 |
341 #else |
| 328 static gboolean initialized = FALSE; |
|
| 329 |
|
| 330 gaim_debug_info("srv","querying SRV record for %s\n", query); |
|
| 331 |
|
| 332 if (!initialized) { |
342 if (!initialized) { |
| 333 MyDnsQuery_UTF8 = (void*) wgaim_find_and_loadproc("dnsapi.dll", "DnsQuery_UTF8"); |
343 MyDnsQuery_UTF8 = (void*) wgaim_find_and_loadproc("dnsapi.dll", "DnsQuery_UTF8"); |
| 334 MyDnsRecordListFree = (void*) wgaim_find_and_loadproc( |
344 MyDnsRecordListFree = (void*) wgaim_find_and_loadproc( |
| 335 "dnsapi.dll", "DnsRecordListFree"); |
345 "dnsapi.dll", "DnsRecordListFree"); |
| 336 initialized = TRUE; |
346 initialized = TRUE; |
| 337 } |
347 } |
| 338 |
348 |
| 339 if (!MyDnsQuery_UTF8 || !MyDnsRecordListFree) { |
349 if (!MyDnsQuery_UTF8 || !MyDnsRecordListFree) { |
| 340 gaim_debug_error("srv", "System missing DNS API (Requires W2K+)\n"); |
350 gaim_debug_error("dnssrv", "System missing DNS API (Requires W2K+)\n"); |
| 341 g_free(query); |
351 g_free(query); |
| 342 cb(NULL, 0, extradata); |
352 cb(NULL, 0, extradata); |
| |
353 return NULL; |
| |
354 } |
| |
355 |
| |
356 query_data = g_new0(GaimSrvQueryData, 1); |
| |
357 query_data->cb = cb; |
| |
358 query_data->query = query; |
| |
359 query_data->extradata = extradata; |
| |
360 |
| |
361 query_data->resolver = g_thread_create(res_thread, query_data, FALSE, &err); |
| |
362 if (query_data->resolver == NULL) |
| |
363 { |
| |
364 query_data->error_message = g_strdup_printf("SRV thread create failure: %s\n", err ? err->message : ""); |
| |
365 g_error_free(err); |
| |
366 res_main_thread_cb(query_data); |
| |
367 return NULL; |
| |
368 } |
| |
369 |
| |
370 return query_data; |
| |
371 #endif |
| |
372 } |
| |
373 |
| |
374 void |
| |
375 gaim_srv_cancel(GaimSrvQueryData *query_data) |
| |
376 { |
| |
377 #ifndef _WIN32 |
| |
378 if (query_data->handle > 0) |
| |
379 gaim_input_remove(query_data->handle); |
| |
380 #else |
| |
381 if (query_data->resolver != NULL) |
| |
382 { |
| |
383 /* |
| |
384 * It's not really possible to kill a thread. So instead we |
| |
385 * just set the callback to NULL and let the DNS lookup |
| |
386 * finish. |
| |
387 */ |
| |
388 query_data->callback = NULL; |
| 343 return; |
389 return; |
| 344 } |
390 } |
| 345 |
391 g_free(query_data->query); |
| 346 rdata = g_new0(struct resdata, 1); |
392 g_free(query_data->error_message); |
| 347 rdata->cb = cb; |
393 #endif |
| 348 rdata->query = query; |
394 g_free(query_data); |
| 349 rdata->extradata = extradata; |
395 } |
| 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 |
|