| 1 /* purple |
|
| 2 * |
|
| 3 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de> |
|
| 4 * |
|
| 5 * This program is free software; you can redistribute it and/or modify |
|
| 6 * it under the terms of the GNU General Public License as published by |
|
| 7 * the Free Software Foundation; either version 2 of the License, or |
|
| 8 * (at your option) any later version. |
|
| 9 * |
|
| 10 * This program is distributed in the hope that it will be useful, |
|
| 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 13 * GNU General Public License for more details. |
|
| 14 * |
|
| 15 * You should have received a copy of the GNU General Public License |
|
| 16 * along with this program; if not, write to the Free Software |
|
| 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
|
| 18 */ |
|
| 19 #define _PURPLE_DNSSRV_C_ |
|
| 20 |
|
| 21 #include "internal.h" |
|
| 22 #include "util.h" |
|
| 23 |
|
| 24 #ifndef _WIN32 |
|
| 25 #include <arpa/nameser.h> |
|
| 26 #include <resolv.h> |
|
| 27 #ifdef HAVE_ARPA_NAMESER_COMPAT_H |
|
| 28 #include <arpa/nameser_compat.h> |
|
| 29 #endif |
|
| 30 #else /* WIN32 */ |
|
| 31 #include <windns.h> |
|
| 32 /* Missing from the mingw headers */ |
|
| 33 #ifndef DNS_TYPE_SRV |
|
| 34 # define DNS_TYPE_SRV PurpleDnsTypeSrv |
|
| 35 #endif |
|
| 36 #ifndef DNS_TYPE_TXT |
|
| 37 # define DNS_TYPE_TXT PurpleDnsTypeTxt |
|
| 38 #endif |
|
| 39 #endif |
|
| 40 |
|
| 41 #ifndef T_SRV |
|
| 42 #define T_SRV PurpleDnsTypeSrv |
|
| 43 #endif |
|
| 44 #ifndef T_TXT |
|
| 45 #define T_TXT PurpleDnsTypeTxt |
|
| 46 #endif |
|
| 47 |
|
| 48 #define MAX_ADDR_RESPONSE_LEN 1048576 |
|
| 49 |
|
| 50 #include "debug.h" |
|
| 51 #include "dnssrv.h" |
|
| 52 #include "eventloop.h" |
|
| 53 #include "network.h" |
|
| 54 |
|
| 55 static PurpleSrvTxtQueryUiOps *srv_txt_query_ui_ops = NULL; |
|
| 56 |
|
| 57 #ifndef _WIN32 |
|
| 58 typedef union { |
|
| 59 HEADER hdr; |
|
| 60 u_char buf[1024]; |
|
| 61 } queryans; |
|
| 62 #endif |
|
| 63 |
|
| 64 enum PurpleDnsType { |
|
| 65 PurpleDnsTypeTxt = 16, |
|
| 66 PurpleDnsTypeSrv = 33 |
|
| 67 }; |
|
| 68 |
|
| 69 struct _PurpleSrvTxtQueryData { |
|
| 70 union { |
|
| 71 PurpleSrvCallback srv; |
|
| 72 PurpleTxtCallback txt; |
|
| 73 } cb; |
|
| 74 |
|
| 75 gpointer extradata; |
|
| 76 guint handle; |
|
| 77 int type; |
|
| 78 char *query; |
|
| 79 #ifdef _WIN32 |
|
| 80 GThread *resolver; |
|
| 81 char *error_message; |
|
| 82 GList *results; |
|
| 83 #else |
|
| 84 int fd_in, fd_out; |
|
| 85 pid_t pid; |
|
| 86 #endif |
|
| 87 }; |
|
| 88 |
|
| 89 typedef struct _PurpleSrvInternalQuery { |
|
| 90 int type; |
|
| 91 char query[256]; |
|
| 92 } PurpleSrvInternalQuery; |
|
| 93 |
|
| 94 typedef struct _PurpleSrvResponseContainer { |
|
| 95 PurpleSrvResponse *response; |
|
| 96 int sum; |
|
| 97 } PurpleSrvResponseContainer; |
|
| 98 |
|
| 99 static gboolean purple_srv_txt_query_ui_resolve(PurpleSrvTxtQueryData *query_data); |
|
| 100 |
|
| 101 /* |
|
| 102 * Sort by priority, then by weight. Strictly numerically--no |
|
| 103 * randomness. Technically we only need to sort by pref and then |
|
| 104 * make sure any records with weight 0 are at the beginning of |
|
| 105 * their group, but it's just as easy to sort by weight. |
|
| 106 */ |
|
| 107 static gint |
|
| 108 responsecompare(gconstpointer ar, gconstpointer br) |
|
| 109 { |
|
| 110 PurpleSrvResponse *a = (PurpleSrvResponse*)ar; |
|
| 111 PurpleSrvResponse *b = (PurpleSrvResponse*)br; |
|
| 112 |
|
| 113 if(a->pref == b->pref) { |
|
| 114 if(a->weight == b->weight) |
|
| 115 return 0; |
|
| 116 if(a->weight < b->weight) |
|
| 117 return -1; |
|
| 118 return 1; |
|
| 119 } |
|
| 120 if(a->pref < b->pref) |
|
| 121 return -1; |
|
| 122 return 1; |
|
| 123 } |
|
| 124 |
|
| 125 /* |
|
| 126 * select_random_response: |
|
| 127 * @list: The list of PurpleSrvResponseContainer. This function |
|
| 128 * removes a node from this list and returns the new list. |
|
| 129 * @container_ptr: The PurpleSrvResponseContainer that was chosen |
|
| 130 * will be returned here. |
|
| 131 * |
|
| 132 * Iterate over a list of PurpleSrvResponseContainer making the sum |
|
| 133 * the running total of the sums. Select a random integer in the range |
|
| 134 * (1, sum+1), then find the first element greater than or equal to the |
|
| 135 * number selected. From RFC 2782. |
|
| 136 */ |
|
| 137 static GList * |
|
| 138 select_random_response(GList *list, PurpleSrvResponseContainer **container_ptr) |
|
| 139 { |
|
| 140 GList *cur; |
|
| 141 size_t runningtotal; |
|
| 142 int r; |
|
| 143 |
|
| 144 g_return_val_if_fail(list != NULL, NULL); |
|
| 145 |
|
| 146 runningtotal = 0; |
|
| 147 cur = list; |
|
| 148 |
|
| 149 while (cur) { |
|
| 150 PurpleSrvResponseContainer *container = cur->data; |
|
| 151 runningtotal += container->response->weight; |
|
| 152 container->sum = runningtotal; |
|
| 153 cur = cur->next; |
|
| 154 } |
|
| 155 |
|
| 156 /* |
|
| 157 * If the running total is greater than 0, pick a number between |
|
| 158 * 1 and the runningtotal inclusive. (This is not precisely what |
|
| 159 * the RFC algorithm describes, but we wish to deal with integers |
|
| 160 * and avoid floats. This is functionally equivalent.) |
|
| 161 * If running total is 0, then choose r = 0. |
|
| 162 */ |
|
| 163 r = runningtotal ? g_random_int_range(1, runningtotal + 1) : 0; |
|
| 164 cur = list; |
|
| 165 while (r > ((PurpleSrvResponseContainer *)cur->data)->sum) { |
|
| 166 if (G_UNLIKELY(!cur->next)) |
|
| 167 break; |
|
| 168 cur = cur->next; |
|
| 169 } |
|
| 170 |
|
| 171 /* Set the return parameter and remove cur from the list */ |
|
| 172 *container_ptr = cur->data; |
|
| 173 return g_list_delete_link(list, cur); |
|
| 174 } |
|
| 175 |
|
| 176 /* |
|
| 177 * Reorder a GList of PurpleSrvResponses that have the same priority |
|
| 178 * (aka "pref"). |
|
| 179 */ |
|
| 180 static void |
|
| 181 srv_reorder(GList *list, int num) |
|
| 182 { |
|
| 183 int i; |
|
| 184 GList *cur, *container_list = NULL; |
|
| 185 PurpleSrvResponseContainer *container; |
|
| 186 |
|
| 187 if (num < 2) |
|
| 188 /* Nothing to sort */ |
|
| 189 return; |
|
| 190 |
|
| 191 g_return_if_fail(list != NULL); |
|
| 192 |
|
| 193 /* First build a list of container structs */ |
|
| 194 for (i = 0, cur = list; i < num; i++, cur = cur->next) { |
|
| 195 container = g_new(PurpleSrvResponseContainer, 1); |
|
| 196 container->response = cur->data; |
|
| 197 container_list = g_list_prepend(container_list, container); |
|
| 198 } |
|
| 199 container_list = g_list_reverse(container_list); |
|
| 200 |
|
| 201 /* |
|
| 202 * Re-order the list that was passed in as a parameter. We leave |
|
| 203 * the list nodes in place, but replace their data pointers. |
|
| 204 */ |
|
| 205 cur = list; |
|
| 206 while (container_list) { |
|
| 207 g_return_if_fail(cur); |
|
| 208 container_list = select_random_response(container_list, &container); |
|
| 209 cur->data = container->response; |
|
| 210 g_free(container); |
|
| 211 cur = cur->next; |
|
| 212 } |
|
| 213 } |
|
| 214 |
|
| 215 /* |
|
| 216 * purple_srv_sort: |
|
| 217 * @list: The original list, resorted |
|
| 218 * |
|
| 219 * Sorts a GList of PurpleSrvResponses according to the |
|
| 220 * algorithm described in RFC 2782. |
|
| 221 * |
|
| 222 * Returns: GList of PurpleSrvResponse's |
|
| 223 */ |
|
| 224 static GList * |
|
| 225 purple_srv_sort(GList *list) |
|
| 226 { |
|
| 227 int pref, count; |
|
| 228 GList *cur, *start; |
|
| 229 |
|
| 230 if (!list || !list->next) { |
|
| 231 /* Nothing to sort */ |
|
| 232 return list; |
|
| 233 } |
|
| 234 |
|
| 235 list = g_list_sort(list, responsecompare); |
|
| 236 |
|
| 237 start = cur = list; |
|
| 238 count = 1; |
|
| 239 while (cur) { |
|
| 240 PurpleSrvResponse *next_response; |
|
| 241 |
|
| 242 g_return_val_if_fail(cur->data, list); |
|
| 243 |
|
| 244 pref = ((PurpleSrvResponse *)cur->data)->pref; |
|
| 245 next_response = cur->next ? cur->next->data : NULL; |
|
| 246 if (!next_response || next_response->pref != pref) { |
|
| 247 /* |
|
| 248 * The 'count' records starting at 'start' all have the same |
|
| 249 * priority. Sort them by weight. |
|
| 250 */ |
|
| 251 srv_reorder(start, count); |
|
| 252 start = cur->next; |
|
| 253 count = 0; |
|
| 254 } |
|
| 255 count++; |
|
| 256 cur = cur->next; |
|
| 257 } |
|
| 258 |
|
| 259 return list; |
|
| 260 } |
|
| 261 |
|
| 262 static PurpleSrvTxtQueryData * |
|
| 263 query_data_new(int type, gchar *query, gpointer extradata) |
|
| 264 { |
|
| 265 PurpleSrvTxtQueryData *query_data = g_new0(PurpleSrvTxtQueryData, 1); |
|
| 266 query_data->type = type; |
|
| 267 query_data->extradata = extradata; |
|
| 268 query_data->query = query; |
|
| 269 #ifndef _WIN32 |
|
| 270 query_data->fd_in = -1; |
|
| 271 query_data->fd_out = -1; |
|
| 272 #endif |
|
| 273 return query_data; |
|
| 274 } |
|
| 275 |
|
| 276 void |
|
| 277 purple_srv_txt_query_destroy(PurpleSrvTxtQueryData *query_data) |
|
| 278 { |
|
| 279 PurpleSrvTxtQueryUiOps *ops = purple_srv_txt_query_get_ui_ops(); |
|
| 280 |
|
| 281 if (ops && ops->destroy) |
|
| 282 ops->destroy(query_data); |
|
| 283 |
|
| 284 if (query_data->handle > 0) |
|
| 285 purple_input_remove(query_data->handle); |
|
| 286 #ifdef _WIN32 |
|
| 287 if (query_data->resolver != NULL) |
|
| 288 { |
|
| 289 /* |
|
| 290 * It's not really possible to kill a thread. So instead we |
|
| 291 * just set the callback to NULL and let the DNS lookup |
|
| 292 * finish. |
|
| 293 */ |
|
| 294 query_data->cb.srv = NULL; |
|
| 295 return; |
|
| 296 } |
|
| 297 g_free(query_data->error_message); |
|
| 298 #else |
|
| 299 if (query_data->fd_out != -1) |
|
| 300 close(query_data->fd_out); |
|
| 301 if (query_data->fd_in != -1) |
|
| 302 close(query_data->fd_in); |
|
| 303 #endif |
|
| 304 g_free(query_data->query); |
|
| 305 g_free(query_data); |
|
| 306 } |
|
| 307 |
|
| 308 #ifdef USE_IDN |
|
| 309 static gboolean |
|
| 310 dns_str_is_ascii(const char *name) |
|
| 311 { |
|
| 312 guchar *c; |
|
| 313 for (c = (guchar *)name; c && *c; ++c) { |
|
| 314 if (*c > 0x7f) |
|
| 315 return FALSE; |
|
| 316 } |
|
| 317 |
|
| 318 return TRUE; |
|
| 319 } |
|
| 320 #endif |
|
| 321 |
|
| 322 #ifndef _WIN32 |
|
| 323 static void |
|
| 324 write_to_parent(int in, int out, gconstpointer data, gsize size) |
|
| 325 { |
|
| 326 const guchar *buf = data; |
|
| 327 gssize w; |
|
| 328 |
|
| 329 do { |
|
| 330 w = write(out, buf, size); |
|
| 331 if (w > 0) { |
|
| 332 buf += w; |
|
| 333 size -= w; |
|
| 334 } else if (w < 0 && errno == EINTR) { |
|
| 335 /* Let's try some more; */ |
|
| 336 w = 1; |
|
| 337 } |
|
| 338 } while (size > 0 && w > 0); |
|
| 339 |
|
| 340 if (size != 0) { |
|
| 341 /* An error occurred */ |
|
| 342 close(out); |
|
| 343 close(in); |
|
| 344 _exit(0); |
|
| 345 } |
|
| 346 } |
|
| 347 |
|
| 348 /* Read size bytes to data. Dies if an error occurs. */ |
|
| 349 static void |
|
| 350 read_from_parent(int in, int out, gpointer data, gsize size) |
|
| 351 { |
|
| 352 guchar *buf = data; |
|
| 353 gssize r; |
|
| 354 |
|
| 355 do { |
|
| 356 r = read(in, data, size); |
|
| 357 if (r > 0) { |
|
| 358 buf += r; |
|
| 359 size -= r; |
|
| 360 } else if (r < 0 && errno == EINTR) { |
|
| 361 /* Let's try some more; */ |
|
| 362 r = 1; |
|
| 363 } |
|
| 364 } while (size > 0 && r > 0); |
|
| 365 |
|
| 366 if (size != 0) { |
|
| 367 /* An error occurred */ |
|
| 368 close(out); |
|
| 369 close(in); |
|
| 370 _exit(0); |
|
| 371 } |
|
| 372 } |
|
| 373 |
|
| 374 |
|
| 375 G_GNUC_NORETURN static void |
|
| 376 resolve(int in, int out) |
|
| 377 { |
|
| 378 GList *ret = NULL; |
|
| 379 PurpleSrvResponse *srvres; |
|
| 380 PurpleTxtResponse *txtres; |
|
| 381 queryans answer; |
|
| 382 int size, qdcount, ancount; |
|
| 383 guchar *end, *cp; |
|
| 384 gchar name[256]; |
|
| 385 guint16 type, dlen, pref, weight, port; |
|
| 386 PurpleSrvInternalQuery query; |
|
| 387 |
|
| 388 #ifdef HAVE_SIGNAL_H |
|
| 389 purple_restore_default_signal_handlers(); |
|
| 390 #endif |
|
| 391 |
|
| 392 read_from_parent(in, out, &query, sizeof(query)); |
|
| 393 |
|
| 394 size = res_query( query.query, C_IN, query.type, (u_char*)&answer, sizeof( answer)); |
|
| 395 if (size == -1) { |
|
| 396 write_to_parent(in, out, &(query.type), sizeof(query.type)); |
|
| 397 write_to_parent(in, out, &size, sizeof(size)); |
|
| 398 close(out); |
|
| 399 close(in); |
|
| 400 _exit(0); |
|
| 401 } |
|
| 402 |
|
| 403 qdcount = ntohs(answer.hdr.qdcount); |
|
| 404 ancount = ntohs(answer.hdr.ancount); |
|
| 405 cp = (guchar*)&answer + sizeof(HEADER); |
|
| 406 end = (guchar*)&answer + size; |
|
| 407 |
|
| 408 /* skip over unwanted stuff */ |
|
| 409 while (qdcount-- > 0 && cp < end) { |
|
| 410 size = dn_expand( (unsigned char*)&answer, end, cp, name, 256); |
|
| 411 if(size < 0) goto end; |
|
| 412 cp += size + QFIXEDSZ; |
|
| 413 } |
|
| 414 |
|
| 415 while (ancount-- > 0 && cp < end) { |
|
| 416 size = dn_expand((unsigned char*)&answer, end, cp, name, 256); |
|
| 417 if(size < 0) |
|
| 418 goto end; |
|
| 419 cp += size; |
|
| 420 GETSHORT(type,cp); |
|
| 421 |
|
| 422 /* skip ttl and class since we already know it */ |
|
| 423 cp += 6; |
|
| 424 |
|
| 425 GETSHORT(dlen,cp); |
|
| 426 if (type == T_SRV) { |
|
| 427 GETSHORT(pref,cp); |
|
| 428 |
|
| 429 GETSHORT(weight,cp); |
|
| 430 |
|
| 431 GETSHORT(port,cp); |
|
| 432 |
|
| 433 size = dn_expand( (unsigned char*)&answer, end, cp, name, 256); |
|
| 434 if(size < 0 ) |
|
| 435 goto end; |
|
| 436 |
|
| 437 cp += size; |
|
| 438 |
|
| 439 srvres = g_new0(PurpleSrvResponse, 1); |
|
| 440 if (strlen(name) > sizeof(srvres->hostname) - 1) { |
|
| 441 purple_debug_error("dnssrv", "hostname is " |
|
| 442 "longer than available buffer ('%s', %" |
|
| 443 G_GSIZE_FORMAT " bytes)!", |
|
| 444 name, strlen(name)); |
|
| 445 } |
|
| 446 g_strlcpy(srvres->hostname, name, sizeof(srvres->hostname)); |
|
| 447 srvres->pref = pref; |
|
| 448 srvres->port = port; |
|
| 449 srvres->weight = weight; |
|
| 450 |
|
| 451 ret = g_list_prepend(ret, srvres); |
|
| 452 } else if (type == T_TXT) { |
|
| 453 txtres = g_new0(PurpleTxtResponse, 1); |
|
| 454 txtres->content = g_strndup((gchar*)(++cp), dlen-1); |
|
| 455 ret = g_list_append(ret, txtres); |
|
| 456 cp += dlen - 1; |
|
| 457 } else { |
|
| 458 cp += dlen; |
|
| 459 } |
|
| 460 } |
|
| 461 |
|
| 462 end: |
|
| 463 size = g_list_length(ret); |
|
| 464 |
|
| 465 if (query.type == T_SRV) |
|
| 466 ret = purple_srv_sort(ret); |
|
| 467 |
|
| 468 write_to_parent(in, out, &(query.type), sizeof(query.type)); |
|
| 469 write_to_parent(in, out, &size, sizeof(size)); |
|
| 470 while (ret != NULL) |
|
| 471 { |
|
| 472 if (query.type == T_SRV) |
|
| 473 write_to_parent(in, out, ret->data, sizeof(PurpleSrvResponse)); |
|
| 474 if (query.type == T_TXT) { |
|
| 475 PurpleTxtResponse *response = ret->data; |
|
| 476 gsize l = strlen(response->content) + 1 /* null byte */; |
|
| 477 write_to_parent(in, out, &l, sizeof(l)); |
|
| 478 write_to_parent(in, out, response->content, l); |
|
| 479 } |
|
| 480 |
|
| 481 g_free(ret->data); |
|
| 482 ret = g_list_remove(ret, ret->data); |
|
| 483 } |
|
| 484 |
|
| 485 close(out); |
|
| 486 close(in); |
|
| 487 |
|
| 488 _exit(0); |
|
| 489 } |
|
| 490 |
|
| 491 static void |
|
| 492 resolved(gpointer data, gint source, PurpleInputCondition cond) |
|
| 493 { |
|
| 494 int size; |
|
| 495 int type; |
|
| 496 PurpleSrvTxtQueryData *query_data = (PurpleSrvTxtQueryData*)data; |
|
| 497 int i; |
|
| 498 int status; |
|
| 499 |
|
| 500 if (read(source, &type, sizeof(type)) == sizeof(type)) { |
|
| 501 if (read(source, &size, sizeof(size)) == sizeof(size)) { |
|
| 502 if (size < -1 || size > MAX_ADDR_RESPONSE_LEN) { |
|
| 503 purple_debug_warning("dnssrv", "res_query returned invalid number\n"); |
|
| 504 size = 0; |
|
| 505 } |
|
| 506 if (size == -1 || size == 0) { |
|
| 507 if (size == -1) { |
|
| 508 purple_debug_warning("dnssrv", "res_query returned an error\n"); |
|
| 509 /* Re-read resolv.conf and friends in case DNS servers have changed */ |
|
| 510 res_init(); |
|
| 511 } else |
|
| 512 purple_debug_info("dnssrv", "Found 0 entries, errno is %i\n", errno); |
|
| 513 |
|
| 514 if (type == T_SRV) { |
|
| 515 PurpleSrvCallback cb = query_data->cb.srv; |
|
| 516 cb(NULL, 0, query_data->extradata); |
|
| 517 } else if (type == T_TXT) { |
|
| 518 PurpleTxtCallback cb = query_data->cb.txt; |
|
| 519 cb(NULL, query_data->extradata); |
|
| 520 } else { |
|
| 521 purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno); |
|
| 522 } |
|
| 523 |
|
| 524 } else if (size) { |
|
| 525 if (type == T_SRV) { |
|
| 526 PurpleSrvResponse *res; |
|
| 527 PurpleSrvResponse *tmp; |
|
| 528 PurpleSrvCallback cb = query_data->cb.srv; |
|
| 529 ssize_t red; |
|
| 530 purple_debug_info("dnssrv","found %d SRV entries\n", size); |
|
| 531 tmp = res = g_new0(PurpleSrvResponse, size); |
|
| 532 for (i = 0; i < size; i++) { |
|
| 533 red = read(source, tmp++, sizeof(PurpleSrvResponse)); |
|
| 534 if (red != sizeof(PurpleSrvResponse)) { |
|
| 535 purple_debug_error("dnssrv","unable to read srv " |
|
| 536 "response: %s\n", g_strerror(errno)); |
|
| 537 size = 0; |
|
| 538 g_free(res); |
|
| 539 res = NULL; |
|
| 540 } |
|
| 541 } |
|
| 542 |
|
| 543 cb(res, size, query_data->extradata); |
|
| 544 } else if (type == T_TXT) { |
|
| 545 GList *responses = NULL; |
|
| 546 PurpleTxtResponse *res; |
|
| 547 PurpleTxtCallback cb = query_data->cb.txt; |
|
| 548 ssize_t red; |
|
| 549 purple_debug_info("dnssrv","found %d TXT entries\n", size); |
|
| 550 for (i = 0; i < size; i++) { |
|
| 551 gsize len; |
|
| 552 |
|
| 553 red = read(source, &len, sizeof(len)); |
|
| 554 if (red != sizeof(len)) { |
|
| 555 purple_debug_error("dnssrv","unable to read txt " |
|
| 556 "response length: %s\n", g_strerror(errno)); |
|
| 557 size = 0; |
|
| 558 g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL); |
|
| 559 g_list_free(responses); |
|
| 560 responses = NULL; |
|
| 561 break; |
|
| 562 } |
|
| 563 if (len > MAX_ADDR_RESPONSE_LEN) { |
|
| 564 purple_debug_error("dnssrv", "we've read invalid number\n"); |
|
| 565 size = 0; |
|
| 566 g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL); |
|
| 567 g_list_free(responses); |
|
| 568 responses = NULL; |
|
| 569 break; |
|
| 570 } |
|
| 571 |
|
| 572 res = g_new0(PurpleTxtResponse, 1); |
|
| 573 res->content = g_new0(gchar, len); |
|
| 574 |
|
| 575 red = read(source, res->content, len); |
|
| 576 if (red < 0 || (gsize)red != len) { |
|
| 577 purple_debug_error("dnssrv","unable to read txt " |
|
| 578 "response: %s\n", g_strerror(errno)); |
|
| 579 size = 0; |
|
| 580 purple_txt_response_destroy(res); |
|
| 581 g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL); |
|
| 582 g_list_free(responses); |
|
| 583 responses = NULL; |
|
| 584 break; |
|
| 585 } |
|
| 586 responses = g_list_prepend(responses, res); |
|
| 587 } |
|
| 588 |
|
| 589 responses = g_list_reverse(responses); |
|
| 590 cb(responses, query_data->extradata); |
|
| 591 } else { |
|
| 592 purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno); |
|
| 593 } |
|
| 594 } |
|
| 595 } |
|
| 596 } |
|
| 597 |
|
| 598 waitpid(query_data->pid, &status, 0); |
|
| 599 purple_srv_txt_query_destroy(query_data); |
|
| 600 } |
|
| 601 |
|
| 602 #else /* _WIN32 */ |
|
| 603 |
|
| 604 /* The Jabber Server code was inspiration for parts of this. */ |
|
| 605 |
|
| 606 static gboolean |
|
| 607 res_main_thread_cb(gpointer data) |
|
| 608 { |
|
| 609 PurpleSrvResponse *srvres = NULL; |
|
| 610 PurpleSrvTxtQueryData *query_data = data; |
|
| 611 if(query_data->error_message != NULL) { |
|
| 612 purple_debug_error("dnssrv", "%s", query_data->error_message); |
|
| 613 if (query_data->type == DNS_TYPE_SRV) { |
|
| 614 if (query_data->cb.srv) |
|
| 615 query_data->cb.srv(srvres, 0, query_data->extradata); |
|
| 616 } else if (query_data->type == DNS_TYPE_TXT) { |
|
| 617 if (query_data->cb.txt) |
|
| 618 query_data->cb.txt(NULL, query_data->extradata); |
|
| 619 } |
|
| 620 } else { |
|
| 621 if (query_data->type == DNS_TYPE_SRV) { |
|
| 622 PurpleSrvResponse *srvres_tmp = NULL; |
|
| 623 GList *lst = query_data->results; |
|
| 624 int size = g_list_length(lst); |
|
| 625 |
|
| 626 if(query_data->cb.srv && size > 0) |
|
| 627 srvres_tmp = srvres = g_new0(PurpleSrvResponse, size); |
|
| 628 while (lst) { |
|
| 629 PurpleSrvResponse *lstdata = lst->data; |
|
| 630 lst = g_list_delete_link(lst, lst); |
|
| 631 |
|
| 632 if(query_data->cb.srv) |
|
| 633 memcpy(srvres_tmp++, lstdata, sizeof(PurpleSrvResponse)); |
|
| 634 g_free(lstdata); |
|
| 635 } |
|
| 636 |
|
| 637 query_data->results = NULL; |
|
| 638 |
|
| 639 purple_debug_info("dnssrv", "found %d SRV entries\n", size); |
|
| 640 |
|
| 641 if(query_data->cb.srv) query_data->cb.srv(srvres, size, query_data->extradata); |
|
| 642 } else if (query_data->type == DNS_TYPE_TXT) { |
|
| 643 GList *lst = query_data->results; |
|
| 644 |
|
| 645 purple_debug_info("dnssrv", "found %d TXT entries\n", g_list_length(lst)); |
|
| 646 |
|
| 647 if (query_data->cb.txt) { |
|
| 648 query_data->results = NULL; |
|
| 649 query_data->cb.txt(lst, query_data->extradata); |
|
| 650 } |
|
| 651 } else { |
|
| 652 purple_debug_error("dnssrv", "unknown query type"); |
|
| 653 } |
|
| 654 } |
|
| 655 |
|
| 656 query_data->resolver = NULL; |
|
| 657 query_data->handle = 0; |
|
| 658 |
|
| 659 purple_srv_txt_query_destroy(query_data); |
|
| 660 |
|
| 661 return FALSE; |
|
| 662 } |
|
| 663 |
|
| 664 static gpointer |
|
| 665 res_thread(gpointer data) |
|
| 666 { |
|
| 667 PDNS_RECORD dr = NULL; |
|
| 668 int type; |
|
| 669 DNS_STATUS ds; |
|
| 670 PurpleSrvTxtQueryData *query_data = data; |
|
| 671 type = query_data->type; |
|
| 672 ds = DnsQuery_UTF8(query_data->query, type, DNS_QUERY_STANDARD, NULL, &dr, NULL); |
|
| 673 if (ds != ERROR_SUCCESS) { |
|
| 674 gchar *msg = g_win32_error_message(ds); |
|
| 675 if (type == DNS_TYPE_SRV) { |
|
| 676 query_data->error_message = g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg, ds); |
|
| 677 } else if (type == DNS_TYPE_TXT) { |
|
| 678 query_data->error_message = g_strdup_printf("Couldn't look up TXT record. %s (%lu).\n", msg, ds); |
|
| 679 } |
|
| 680 g_free(msg); |
|
| 681 } else { |
|
| 682 if (type == DNS_TYPE_SRV) { |
|
| 683 PDNS_RECORD dr_tmp; |
|
| 684 GList *lst = NULL; |
|
| 685 DNS_SRV_DATA *srv_data; |
|
| 686 PurpleSrvResponse *srvres; |
|
| 687 |
|
| 688 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { |
|
| 689 /* Discard any incorrect entries. I'm not sure if this is necessary */ |
|
| 690 if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { |
|
| 691 continue; |
|
| 692 } |
|
| 693 |
|
| 694 srv_data = &dr_tmp->Data.SRV; |
|
| 695 srvres = g_new0(PurpleSrvResponse, 1); |
|
| 696 strncpy(srvres->hostname, srv_data->pNameTarget, 255); |
|
| 697 srvres->hostname[255] = '\0'; |
|
| 698 srvres->pref = srv_data->wPriority; |
|
| 699 srvres->port = srv_data->wPort; |
|
| 700 srvres->weight = srv_data->wWeight; |
|
| 701 |
|
| 702 lst = g_list_prepend(lst, srvres); |
|
| 703 } |
|
| 704 |
|
| 705 DnsRecordListFree(dr, DnsFreeRecordList); |
|
| 706 query_data->results = purple_srv_sort(lst); |
|
| 707 } else if (type == DNS_TYPE_TXT) { |
|
| 708 PDNS_RECORD dr_tmp; |
|
| 709 GList *lst = NULL; |
|
| 710 DNS_TXT_DATA *txt_data; |
|
| 711 PurpleTxtResponse *txtres; |
|
| 712 |
|
| 713 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { |
|
| 714 GString *s; |
|
| 715 int i; |
|
| 716 |
|
| 717 /* Discard any incorrect entries. I'm not sure if this is necessary */ |
|
| 718 if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { |
|
| 719 continue; |
|
| 720 } |
|
| 721 |
|
| 722 txt_data = &dr_tmp->Data.TXT; |
|
| 723 txtres = g_new0(PurpleTxtResponse, 1); |
|
| 724 |
|
| 725 s = g_string_new(""); |
|
| 726 for (i = 0; i < (int)txt_data->dwStringCount; ++i) |
|
| 727 s = g_string_append(s, txt_data->pStringArray[i]); |
|
| 728 txtres->content = g_string_free(s, FALSE); |
|
| 729 |
|
| 730 lst = g_list_append(lst, txtres); |
|
| 731 } |
|
| 732 |
|
| 733 DnsRecordListFree(dr, DnsFreeRecordList); |
|
| 734 query_data->results = lst; |
|
| 735 } else { |
|
| 736 |
|
| 737 } |
|
| 738 } |
|
| 739 |
|
| 740 /* back to main thread */ |
|
| 741 /* Note: this should *not* be attached to query_data->handle - it will cause leakage */ |
|
| 742 purple_timeout_add(0, res_main_thread_cb, query_data); |
|
| 743 |
|
| 744 g_thread_exit(NULL); |
|
| 745 return NULL; |
|
| 746 } |
|
| 747 |
|
| 748 #endif |
|
| 749 |
|
| 750 PurpleSrvTxtQueryData * |
|
| 751 purple_srv_resolve(PurpleAccount *account, const char *protocol, |
|
| 752 const char *transport, const char *domain, PurpleSrvCallback cb, |
|
| 753 gpointer extradata) |
|
| 754 { |
|
| 755 char *query; |
|
| 756 char *hostname; |
|
| 757 PurpleSrvTxtQueryData *query_data; |
|
| 758 PurpleProxyType proxy_type; |
|
| 759 #ifndef _WIN32 |
|
| 760 PurpleSrvInternalQuery internal_query; |
|
| 761 int in[2], out[2]; |
|
| 762 int pid; |
|
| 763 #else |
|
| 764 GError* err = NULL; |
|
| 765 #endif |
|
| 766 |
|
| 767 if (!protocol || !*protocol || !transport || !*transport || !domain || !*domain) { |
|
| 768 purple_debug_error("dnssrv", "Wrong arguments\n"); |
|
| 769 cb(NULL, 0, extradata); |
|
| 770 g_return_val_if_reached(NULL); |
|
| 771 } |
|
| 772 |
|
| 773 proxy_type = purple_proxy_info_get_proxy_type( |
|
| 774 purple_proxy_get_setup(account)); |
|
| 775 if (proxy_type == PURPLE_PROXY_TOR) { |
|
| 776 purple_debug_info("dnssrv", "Aborting SRV lookup in Tor Proxy mode.\n"); |
|
| 777 cb(NULL, 0, extradata); |
|
| 778 return NULL; |
|
| 779 } |
|
| 780 |
|
| 781 #ifdef USE_IDN |
|
| 782 if (!dns_str_is_ascii(domain)) { |
|
| 783 int ret = purple_network_convert_idn_to_ascii(domain, &hostname); |
|
| 784 if (ret != 0) { |
|
| 785 purple_debug_error("dnssrv", "IDNA ToASCII failed\n"); |
|
| 786 cb(NULL, 0, extradata); |
|
| 787 return NULL; |
|
| 788 } |
|
| 789 } else /* Fallthru is intentional */ |
|
| 790 #endif |
|
| 791 hostname = g_strdup(domain); |
|
| 792 |
|
| 793 query = g_strdup_printf("_%s._%s.%s", protocol, transport, hostname); |
|
| 794 purple_debug_info("dnssrv","querying SRV record for %s: %s\n", domain, |
|
| 795 query); |
|
| 796 g_free(hostname); |
|
| 797 |
|
| 798 query_data = query_data_new(PurpleDnsTypeSrv, query, extradata); |
|
| 799 query_data->cb.srv = cb; |
|
| 800 |
|
| 801 if (purple_srv_txt_query_ui_resolve(query_data)) |
|
| 802 { |
|
| 803 return query_data; |
|
| 804 } |
|
| 805 |
|
| 806 #ifndef _WIN32 |
|
| 807 if(pipe(in) || pipe(out)) { |
|
| 808 purple_debug_error("dnssrv", "Could not create pipe\n"); |
|
| 809 g_free(query); |
|
| 810 g_free(query_data); |
|
| 811 cb(NULL, 0, extradata); |
|
| 812 return NULL; |
|
| 813 } |
|
| 814 |
|
| 815 /* |
|
| 816 * TODO: We should put a cap on the number of forked processes that we |
|
| 817 * allow at any given time. If we get too many requests they |
|
| 818 * should be put into a queue and handled later. (This is what |
|
| 819 * we do for A record lookups.) |
|
| 820 */ |
|
| 821 pid = fork(); |
|
| 822 if (pid == -1) { |
|
| 823 purple_debug_error("dnssrv", "Could not create process!\n"); |
|
| 824 g_free(query); |
|
| 825 g_free(query_data); |
|
| 826 cb(NULL, 0, extradata); |
|
| 827 return NULL; |
|
| 828 } |
|
| 829 |
|
| 830 /* Child */ |
|
| 831 if (pid == 0) |
|
| 832 { |
|
| 833 g_free(query); |
|
| 834 g_free(query_data); |
|
| 835 |
|
| 836 close(out[0]); |
|
| 837 close(in[1]); |
|
| 838 resolve(in[0], out[1]); |
|
| 839 /* resolve() does not return */ |
|
| 840 } |
|
| 841 |
|
| 842 close(out[1]); |
|
| 843 close(in[0]); |
|
| 844 |
|
| 845 internal_query.type = T_SRV; |
|
| 846 strncpy(internal_query.query, query, 255); |
|
| 847 internal_query.query[255] = '\0'; |
|
| 848 |
|
| 849 if (write(in[1], &internal_query, sizeof(internal_query)) < 0) |
|
| 850 purple_debug_error("dnssrv", "Could not write to SRV resolver\n"); |
|
| 851 |
|
| 852 query_data->pid = pid; |
|
| 853 query_data->fd_out = out[0]; |
|
| 854 query_data->fd_in = in[1]; |
|
| 855 query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data); |
|
| 856 |
|
| 857 return query_data; |
|
| 858 #else |
|
| 859 query_data->resolver = g_thread_try_new("dnssrv srv resolver", res_thread, query_data, &err); |
|
| 860 if (query_data->resolver == NULL) { |
|
| 861 query_data->error_message = g_strdup_printf("SRV thread create failure: %s\n", (err && err->message) ? err->message : ""); |
|
| 862 g_error_free(err); |
|
| 863 } |
|
| 864 else |
|
| 865 g_thread_unref(query_data->resolver); |
|
| 866 |
|
| 867 /* The query isn't going to happen, so finish the SRV lookup now. |
|
| 868 * Asynchronously call the callback since stuff may not expect |
|
| 869 * the callback to be called before this returns */ |
|
| 870 if (query_data->error_message != NULL) |
|
| 871 query_data->handle = purple_timeout_add(0, res_main_thread_cb, query_data); |
|
| 872 |
|
| 873 return query_data; |
|
| 874 #endif |
|
| 875 } |
|
| 876 |
|
| 877 PurpleSrvTxtQueryData *purple_txt_resolve(PurpleAccount *account, |
|
| 878 const char *owner, const char *domain, PurpleTxtCallback cb, |
|
| 879 gpointer extradata) |
|
| 880 { |
|
| 881 char *query; |
|
| 882 char *hostname; |
|
| 883 PurpleSrvTxtQueryData *query_data; |
|
| 884 PurpleProxyType proxy_type; |
|
| 885 #ifndef _WIN32 |
|
| 886 PurpleSrvInternalQuery internal_query; |
|
| 887 int in[2], out[2]; |
|
| 888 int pid; |
|
| 889 #else |
|
| 890 GError* err = NULL; |
|
| 891 #endif |
|
| 892 |
|
| 893 proxy_type = purple_proxy_info_get_proxy_type( |
|
| 894 purple_proxy_get_setup(account)); |
|
| 895 if (proxy_type == PURPLE_PROXY_TOR) { |
|
| 896 purple_debug_info("dnssrv", "Aborting TXT lookup in Tor Proxy mode.\n"); |
|
| 897 cb(NULL, extradata); |
|
| 898 return NULL; |
|
| 899 } |
|
| 900 |
|
| 901 #ifdef USE_IDN |
|
| 902 if (!dns_str_is_ascii(domain)) { |
|
| 903 int ret = purple_network_convert_idn_to_ascii(domain, &hostname); |
|
| 904 if (ret != 0) { |
|
| 905 purple_debug_error("dnssrv", "IDNA ToASCII failed\n"); |
|
| 906 cb(NULL, extradata); |
|
| 907 return NULL; |
|
| 908 } |
|
| 909 } else /* fallthru is intentional */ |
|
| 910 #endif |
|
| 911 hostname = g_strdup(domain); |
|
| 912 |
|
| 913 query = g_strdup_printf("%s.%s", owner, hostname); |
|
| 914 purple_debug_info("dnssrv","querying TXT record for %s: %s\n", domain, |
|
| 915 query); |
|
| 916 g_free(hostname); |
|
| 917 |
|
| 918 query_data = query_data_new(PurpleDnsTypeTxt, query, extradata); |
|
| 919 query_data->cb.txt = cb; |
|
| 920 |
|
| 921 if (purple_srv_txt_query_ui_resolve(query_data)) { |
|
| 922 /* query intentionally not freed |
|
| 923 */ |
|
| 924 return query_data; |
|
| 925 } |
|
| 926 |
|
| 927 #ifndef _WIN32 |
|
| 928 if(pipe(in) || pipe(out)) { |
|
| 929 purple_debug_error("dnssrv", "Could not create pipe\n"); |
|
| 930 g_free(query); |
|
| 931 g_free(query_data); |
|
| 932 cb(NULL, extradata); |
|
| 933 return NULL; |
|
| 934 } |
|
| 935 |
|
| 936 /* |
|
| 937 * TODO: We should put a cap on the number of forked processes that we |
|
| 938 * allow at any given time. If we get too many requests they |
|
| 939 * should be put into a queue and handled later. (This is what |
|
| 940 * we do for A record lookups.) |
|
| 941 */ |
|
| 942 pid = fork(); |
|
| 943 if (pid == -1) { |
|
| 944 purple_debug_error("dnssrv", "Could not create process!\n"); |
|
| 945 g_free(query); |
|
| 946 g_free(query_data); |
|
| 947 cb(NULL, extradata); |
|
| 948 return NULL; |
|
| 949 } |
|
| 950 |
|
| 951 /* Child */ |
|
| 952 if (pid == 0) |
|
| 953 { |
|
| 954 g_free(query); |
|
| 955 g_free(query_data); |
|
| 956 |
|
| 957 close(out[0]); |
|
| 958 close(in[1]); |
|
| 959 resolve(in[0], out[1]); |
|
| 960 /* resolve() does not return */ |
|
| 961 } |
|
| 962 |
|
| 963 close(out[1]); |
|
| 964 close(in[0]); |
|
| 965 |
|
| 966 internal_query.type = T_TXT; |
|
| 967 strncpy(internal_query.query, query, 255); |
|
| 968 internal_query.query[255] = '\0'; |
|
| 969 |
|
| 970 if (write(in[1], &internal_query, sizeof(internal_query)) < 0) |
|
| 971 purple_debug_error("dnssrv", "Could not write to TXT resolver\n"); |
|
| 972 |
|
| 973 query_data->pid = pid; |
|
| 974 query_data->fd_out = out[0]; |
|
| 975 query_data->fd_in = in[1]; |
|
| 976 query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data); |
|
| 977 |
|
| 978 return query_data; |
|
| 979 #else |
|
| 980 query_data->resolver = g_thread_try_new("dnssrv srv resolver", res_thread, query_data, &err); |
|
| 981 if (query_data->resolver == NULL) { |
|
| 982 query_data->error_message = g_strdup_printf("TXT thread create failure: %s\n", (err && err->message) ? err->message : ""); |
|
| 983 g_error_free(err); |
|
| 984 } |
|
| 985 else |
|
| 986 g_thread_unref(query_data->resolver); |
|
| 987 |
|
| 988 /* The query isn't going to happen, so finish the TXT lookup now. |
|
| 989 * Asynchronously call the callback since stuff may not expect |
|
| 990 * the callback to be called before this returns */ |
|
| 991 if (query_data->error_message != NULL) |
|
| 992 query_data->handle = purple_timeout_add(0, res_main_thread_cb, query_data); |
|
| 993 |
|
| 994 return query_data; |
|
| 995 #endif |
|
| 996 } |
|
| 997 |
|
| 998 const gchar * |
|
| 999 purple_txt_response_get_content(PurpleTxtResponse *resp) |
|
| 1000 { |
|
| 1001 g_return_val_if_fail(resp != NULL, NULL); |
|
| 1002 |
|
| 1003 return resp->content; |
|
| 1004 } |
|
| 1005 |
|
| 1006 void purple_txt_response_destroy(PurpleTxtResponse *resp) |
|
| 1007 { |
|
| 1008 g_return_if_fail(resp != NULL); |
|
| 1009 |
|
| 1010 g_free(resp->content); |
|
| 1011 g_free(resp); |
|
| 1012 } |
|
| 1013 |
|
| 1014 /* |
|
| 1015 * Only used as the callback for the ui ops. |
|
| 1016 */ |
|
| 1017 static void |
|
| 1018 purple_srv_query_resolved(PurpleSrvTxtQueryData *query_data, GList *records) |
|
| 1019 { |
|
| 1020 GList *l; |
|
| 1021 PurpleSrvResponse *records_array; |
|
| 1022 int i = 0, length; |
|
| 1023 |
|
| 1024 g_return_if_fail(records != NULL); |
|
| 1025 |
|
| 1026 if (query_data->cb.srv == NULL) { |
|
| 1027 purple_srv_txt_query_destroy(query_data); |
|
| 1028 |
|
| 1029 while (records) { |
|
| 1030 g_free(records->data); |
|
| 1031 records = g_list_delete_link(records, records); |
|
| 1032 } |
|
| 1033 return; |
|
| 1034 } |
|
| 1035 |
|
| 1036 records = purple_srv_sort(records); |
|
| 1037 length = g_list_length(records); |
|
| 1038 |
|
| 1039 purple_debug_info("dnssrv", "SRV records resolved for %s, count: %d\n", |
|
| 1040 query_data->query, length); |
|
| 1041 |
|
| 1042 records_array = g_new(PurpleSrvResponse, length); |
|
| 1043 for (l = records; l; l = l->next, i++) { |
|
| 1044 records_array[i] = *(PurpleSrvResponse *)l->data; |
|
| 1045 } |
|
| 1046 |
|
| 1047 query_data->cb.srv(records_array, length, query_data->extradata); |
|
| 1048 |
|
| 1049 purple_srv_txt_query_destroy(query_data); |
|
| 1050 |
|
| 1051 while (records) { |
|
| 1052 g_free(records->data); |
|
| 1053 records = g_list_delete_link(records, records); |
|
| 1054 } |
|
| 1055 } |
|
| 1056 |
|
| 1057 /* |
|
| 1058 * Only used as the callback for the ui ops. |
|
| 1059 */ |
|
| 1060 static void |
|
| 1061 purple_txt_query_resolved(PurpleSrvTxtQueryData *query_data, GList *entries) |
|
| 1062 { |
|
| 1063 g_return_if_fail(entries != NULL); |
|
| 1064 |
|
| 1065 purple_debug_info("dnssrv", "TXT entries resolved for %s, count: %d\n", query_data->query, g_list_length(entries)); |
|
| 1066 |
|
| 1067 /* the callback should g_free the entries. |
|
| 1068 */ |
|
| 1069 if (query_data->cb.txt != NULL) |
|
| 1070 query_data->cb.txt(entries, query_data->extradata); |
|
| 1071 else { |
|
| 1072 while (entries) { |
|
| 1073 g_free(entries->data); |
|
| 1074 entries = g_list_delete_link(entries, entries); |
|
| 1075 } |
|
| 1076 } |
|
| 1077 |
|
| 1078 purple_srv_txt_query_destroy(query_data); |
|
| 1079 } |
|
| 1080 |
|
| 1081 static void |
|
| 1082 purple_srv_query_failed(PurpleSrvTxtQueryData *query_data, const gchar *error_message) |
|
| 1083 { |
|
| 1084 purple_debug_error("dnssrv", "%s\n", error_message); |
|
| 1085 |
|
| 1086 if (query_data->cb.srv != NULL) |
|
| 1087 query_data->cb.srv(NULL, 0, query_data->extradata); |
|
| 1088 |
|
| 1089 purple_srv_txt_query_destroy(query_data); |
|
| 1090 } |
|
| 1091 |
|
| 1092 static gboolean |
|
| 1093 purple_srv_txt_query_ui_resolve(PurpleSrvTxtQueryData *query_data) |
|
| 1094 { |
|
| 1095 PurpleSrvTxtQueryUiOps *ops = purple_srv_txt_query_get_ui_ops(); |
|
| 1096 |
|
| 1097 if (ops && ops->resolve) |
|
| 1098 return ops->resolve(query_data, (query_data->type == T_SRV ? purple_srv_query_resolved : purple_txt_query_resolved), purple_srv_query_failed); |
|
| 1099 |
|
| 1100 return FALSE; |
|
| 1101 } |
|
| 1102 |
|
| 1103 void |
|
| 1104 purple_srv_txt_query_set_ui_ops(PurpleSrvTxtQueryUiOps *ops) |
|
| 1105 { |
|
| 1106 srv_txt_query_ui_ops = ops; |
|
| 1107 } |
|
| 1108 |
|
| 1109 PurpleSrvTxtQueryUiOps * |
|
| 1110 purple_srv_txt_query_get_ui_ops(void) |
|
| 1111 { |
|
| 1112 /* It is perfectly acceptable for srv_txt_query_ui_ops to be NULL; this just |
|
| 1113 * means that the default platform-specific implementation will be used. |
|
| 1114 */ |
|
| 1115 return srv_txt_query_ui_ops; |
|
| 1116 } |
|
| 1117 |
|
| 1118 char * |
|
| 1119 purple_srv_txt_query_get_query(PurpleSrvTxtQueryData *query_data) |
|
| 1120 { |
|
| 1121 g_return_val_if_fail(query_data != NULL, NULL); |
|
| 1122 |
|
| 1123 return query_data->query; |
|
| 1124 } |
|
| 1125 |
|
| 1126 |
|
| 1127 int |
|
| 1128 purple_srv_txt_query_get_query_type(PurpleSrvTxtQueryData *query_data) |
|
| 1129 { |
|
| 1130 g_return_val_if_fail(query_data != NULL, 0); |
|
| 1131 |
|
| 1132 return query_data->type; |
|
| 1133 } |
|
| 1134 |
|
| 1135 /************************************************************************** |
|
| 1136 * GBoxed code |
|
| 1137 **************************************************************************/ |
|
| 1138 static PurpleSrvTxtQueryUiOps * |
|
| 1139 purple_srv_txt_query_ui_ops_copy(PurpleSrvTxtQueryUiOps *ops) |
|
| 1140 { |
|
| 1141 PurpleSrvTxtQueryUiOps *ops_new; |
|
| 1142 |
|
| 1143 g_return_val_if_fail(ops != NULL, NULL); |
|
| 1144 |
|
| 1145 ops_new = g_new(PurpleSrvTxtQueryUiOps, 1); |
|
| 1146 *ops_new = *ops; |
|
| 1147 |
|
| 1148 return ops_new; |
|
| 1149 } |
|
| 1150 |
|
| 1151 GType |
|
| 1152 purple_srv_txt_query_ui_ops_get_type(void) |
|
| 1153 { |
|
| 1154 static GType type = 0; |
|
| 1155 |
|
| 1156 if (type == 0) { |
|
| 1157 type = g_boxed_type_register_static("PurpleSrvTxtQueryUiOps", |
|
| 1158 (GBoxedCopyFunc)purple_srv_txt_query_ui_ops_copy, |
|
| 1159 (GBoxedFreeFunc)g_free); |
|
| 1160 } |
|
| 1161 |
|
| 1162 return type; |
|
| 1163 } |
|