7 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #define _PURPLE_DNSSRV_C_
29 #include <arpa/nameser.h>
31 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
32 #include <arpa/nameser_compat.h>
36 /* Missing from the mingw headers */
38 # define DNS_TYPE_SRV PurpleDnsTypeSrv
41 # define DNS_TYPE_TXT PurpleDnsTypeTxt
46 #define T_SRV PurpleDnsTypeSrv
49 #define T_TXT PurpleDnsTypeTxt
54 #include "eventloop.h"
57 static PurpleSrvTxtQueryUiOps
*srv_txt_query_ui_ops
= NULL
;
65 static DNS_STATUS (WINAPI
*MyDnsQuery_UTF8
) (
66 PCSTR lpstrName
, WORD wType
, DWORD fOptions
,
67 PIP4_ARRAY aipServers
, PDNS_RECORD
* ppQueryResultsSet
,
68 PVOID
* pReserved
) = NULL
;
69 static void (WINAPI
*MyDnsRecordListFree
) (PDNS_RECORD pRecordList
,
70 DNS_FREE_TYPE FreeType
) = NULL
;
73 struct _PurpleSrvTxtQueryData
{
75 PurpleSrvCallback srv
;
76 PurpleTxtCallback txt
;
93 typedef struct _PurpleSrvInternalQuery
{
96 } PurpleSrvInternalQuery
;
98 typedef struct _PurpleSrvResponseContainer
{
99 PurpleSrvResponse
*response
;
101 } PurpleSrvResponseContainer
;
103 static gboolean
purple_srv_txt_query_ui_resolve(PurpleSrvTxtQueryData
*query_data
);
106 * Sort by priority, then by weight. Strictly numerically--no
107 * randomness. Technically we only need to sort by pref and then
108 * make sure any records with weight 0 are at the beginning of
109 * their group, but it's just as easy to sort by weight.
112 responsecompare(gconstpointer ar
, gconstpointer br
)
114 PurpleSrvResponse
*a
= (PurpleSrvResponse
*)ar
;
115 PurpleSrvResponse
*b
= (PurpleSrvResponse
*)br
;
117 if(a
->pref
== b
->pref
) {
118 if(a
->weight
== b
->weight
)
120 if(a
->weight
< b
->weight
)
124 if(a
->pref
< b
->pref
)
130 * Iterate over a list of PurpleSrvResponseContainer making the sum
131 * the running total of the sums. Select a random integer in the range
132 * (1, sum+1), then find the first element greater than or equal to the
133 * number selected. From RFC 2782.
135 * @param list The list of PurpleSrvResponseContainer. This function
136 * removes a node from this list and returns the new list.
137 * @param container_ptr The PurpleSrvResponseContainer that was chosen
138 * will be returned here.
141 select_random_response(GList
*list
, PurpleSrvResponseContainer
**container_ptr
)
151 PurpleSrvResponseContainer
*container
= cur
->data
;
152 runningtotal
+= container
->response
->weight
;
153 container
->sum
= runningtotal
;
158 * If the running total is greater than 0, pick a number between
159 * 1 and the runningtotal inclusive. (This is not precisely what
160 * the RFC algorithm describes, but we wish to deal with integers
161 * and avoid floats. This is functionally equivalent.)
162 * If running total is 0, then choose r = 0.
164 r
= runningtotal
? g_random_int_range(1, runningtotal
+ 1) : 0;
166 while (r
> ((PurpleSrvResponseContainer
*)cur
->data
)->sum
) {
170 /* Set the return parameter and remove cur from the list */
171 *container_ptr
= cur
->data
;
172 return g_list_delete_link(list
, cur
);
176 * Reorder a GList of PurpleSrvResponses that have the same priority
180 srv_reorder(GList
*list
, int num
)
183 GList
*cur
, *container_list
= NULL
;
184 PurpleSrvResponseContainer
*container
;
187 /* Nothing to sort */
190 /* First build a list of container structs */
191 for (i
= 0, cur
= list
; i
< num
; i
++, cur
= cur
->next
) {
192 container
= g_new(PurpleSrvResponseContainer
, 1);
193 container
->response
= cur
->data
;
194 container_list
= g_list_prepend(container_list
, container
);
196 container_list
= g_list_reverse(container_list
);
199 * Re-order the list that was passed in as a parameter. We leave
200 * the list nodes in place, but replace their data pointers.
203 while (container_list
) {
204 container_list
= select_random_response(container_list
, &container
);
205 cur
->data
= container
->response
;
212 * Sorts a GList of PurpleSrvResponses according to the
213 * algorithm described in RFC 2782.
215 * @param response GList of PurpleSrvResponse's
216 * @param The original list, resorted
219 purple_srv_sort(GList
*list
)
224 if (!list
|| !list
->next
) {
225 /* Nothing to sort */
229 list
= g_list_sort(list
, responsecompare
);
234 PurpleSrvResponse
*next_response
;
235 pref
= ((PurpleSrvResponse
*)cur
->data
)->pref
;
236 next_response
= cur
->next
? cur
->next
->data
: NULL
;
237 if (!next_response
|| next_response
->pref
!= pref
) {
239 * The 'count' records starting at 'start' all have the same
240 * priority. Sort them by weight.
242 srv_reorder(start
, count
);
253 static PurpleSrvTxtQueryData
*
254 query_data_new(int type
, gchar
*query
, gpointer extradata
)
256 PurpleSrvTxtQueryData
*query_data
= g_new0(PurpleSrvTxtQueryData
, 1);
257 query_data
->type
= type
;
258 query_data
->extradata
= extradata
;
259 query_data
->query
= query
;
261 query_data
->fd_in
= -1;
262 query_data
->fd_out
= -1;
268 purple_srv_txt_query_destroy(PurpleSrvTxtQueryData
*query_data
)
270 PurpleSrvTxtQueryUiOps
*ops
= purple_srv_txt_query_get_ui_ops();
272 if (ops
&& ops
->destroy
)
273 ops
->destroy(query_data
);
275 if (query_data
->handle
> 0)
276 purple_input_remove(query_data
->handle
);
278 if (query_data
->resolver
!= NULL
)
281 * It's not really possible to kill a thread. So instead we
282 * just set the callback to NULL and let the DNS lookup
285 query_data
->cb
.srv
= NULL
;
288 g_free(query_data
->error_message
);
290 if (query_data
->fd_out
!= -1)
291 close(query_data
->fd_out
);
292 if (query_data
->fd_in
!= -1)
293 close(query_data
->fd_in
);
295 g_free(query_data
->query
);
301 dns_str_is_ascii(const char *name
)
304 for (c
= (guchar
*)name
; c
&& *c
; ++c
) {
315 write_to_parent(int in
, int out
, gconstpointer data
, gsize size
)
317 const guchar
*buf
= data
;
321 w
= write(out
, buf
, size
);
325 } else if (w
< 0 && errno
== EINTR
) {
326 /* Let's try some more; */
329 } while (size
> 0 && w
> 0);
332 /* An error occurred */
339 /* Read size bytes to data. Dies if an error occurs. */
341 read_from_parent(int in
, int out
, gpointer data
, gsize size
)
347 r
= read(in
, data
, size
);
351 } else if (r
< 0 && errno
== EINTR
) {
352 /* Let's try some more; */
355 } while (size
> 0 && r
> 0);
358 /* An error occurred */
366 G_GNUC_NORETURN
static void
367 resolve(int in
, int out
)
370 PurpleSrvResponse
*srvres
;
371 PurpleTxtResponse
*txtres
;
373 int size
, qdcount
, ancount
;
376 guint16 type
, dlen
, pref
, weight
, port
;
377 PurpleSrvInternalQuery query
;
380 purple_restore_default_signal_handlers();
383 read_from_parent(in
, out
, &query
, sizeof(query
));
385 size
= res_query( query
.query
, C_IN
, query
.type
, (u_char
*)&answer
, sizeof( answer
));
387 write_to_parent(in
, out
, &(query
.type
), sizeof(query
.type
));
388 write_to_parent(in
, out
, &size
, sizeof(size
));
394 qdcount
= ntohs(answer
.hdr
.qdcount
);
395 ancount
= ntohs(answer
.hdr
.ancount
);
396 cp
= (guchar
*)&answer
+ sizeof(HEADER
);
397 end
= (guchar
*)&answer
+ size
;
399 /* skip over unwanted stuff */
400 while (qdcount
-- > 0 && cp
< end
) {
401 size
= dn_expand( (unsigned char*)&answer
, end
, cp
, name
, 256);
402 if(size
< 0) goto end
;
403 cp
+= size
+ QFIXEDSZ
;
406 while (ancount
-- > 0 && cp
< end
) {
407 size
= dn_expand((unsigned char*)&answer
, end
, cp
, name
, 256);
413 /* skip ttl and class since we already know it */
424 size
= dn_expand( (unsigned char*)&answer
, end
, cp
, name
, 256);
430 srvres
= g_new0(PurpleSrvResponse
, 1);
431 if (strlen(name
) > sizeof(srvres
->hostname
) - 1) {
432 purple_debug_error("dnssrv", "hostname is longer than available buffer ('%s', %zd bytes)!",
435 g_strlcpy(srvres
->hostname
, name
, sizeof(srvres
->hostname
));
438 srvres
->weight
= weight
;
440 ret
= g_list_prepend(ret
, srvres
);
441 } else if (type
== T_TXT
) {
442 txtres
= g_new0(PurpleTxtResponse
, 1);
443 txtres
->content
= g_strndup((gchar
*)(++cp
), dlen
-1);
444 ret
= g_list_append(ret
, txtres
);
452 size
= g_list_length(ret
);
454 if (query
.type
== T_SRV
)
455 ret
= purple_srv_sort(ret
);
457 write_to_parent(in
, out
, &(query
.type
), sizeof(query
.type
));
458 write_to_parent(in
, out
, &size
, sizeof(size
));
461 if (query
.type
== T_SRV
)
462 write_to_parent(in
, out
, ret
->data
, sizeof(PurpleSrvResponse
));
463 if (query
.type
== T_TXT
) {
464 PurpleTxtResponse
*response
= ret
->data
;
465 gsize l
= strlen(response
->content
) + 1 /* null byte */;
466 write_to_parent(in
, out
, &l
, sizeof(l
));
467 write_to_parent(in
, out
, response
->content
, l
);
471 ret
= g_list_remove(ret
, ret
->data
);
481 resolved(gpointer data
, gint source
, PurpleInputCondition cond
)
485 PurpleSrvTxtQueryData
*query_data
= (PurpleSrvTxtQueryData
*)data
;
489 if (read(source
, &type
, sizeof(type
)) == sizeof(type
)) {
490 if (read(source
, &size
, sizeof(size
)) == sizeof(size
)) {
491 if (size
== -1 || size
== 0) {
493 purple_debug_warning("dnssrv", "res_query returned an error\n");
494 /* Re-read resolv.conf and friends in case DNS servers have changed */
497 purple_debug_info("dnssrv", "Found 0 entries, errno is %i\n", errno
);
500 PurpleSrvCallback cb
= query_data
->cb
.srv
;
501 cb(NULL
, 0, query_data
->extradata
);
502 } else if (type
== T_TXT
) {
503 PurpleTxtCallback cb
= query_data
->cb
.txt
;
504 cb(NULL
, query_data
->extradata
);
506 purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno
);
511 PurpleSrvResponse
*res
;
512 PurpleSrvResponse
*tmp
;
513 PurpleSrvCallback cb
= query_data
->cb
.srv
;
515 purple_debug_info("dnssrv","found %d SRV entries\n", size
);
516 tmp
= res
= g_new0(PurpleSrvResponse
, size
);
517 for (i
= 0; i
< size
; i
++) {
518 red
= read(source
, tmp
++, sizeof(PurpleSrvResponse
));
519 if (red
!= sizeof(PurpleSrvResponse
)) {
520 purple_debug_error("dnssrv","unable to read srv "
521 "response: %s\n", g_strerror(errno
));
528 cb(res
, size
, query_data
->extradata
);
529 } else if (type
== T_TXT
) {
530 GList
*responses
= NULL
;
531 PurpleTxtResponse
*res
;
532 PurpleTxtCallback cb
= query_data
->cb
.txt
;
534 purple_debug_info("dnssrv","found %d TXT entries\n", size
);
535 for (i
= 0; i
< size
; i
++) {
538 red
= read(source
, &len
, sizeof(len
));
539 if (red
!= sizeof(len
)) {
540 purple_debug_error("dnssrv","unable to read txt "
541 "response length: %s\n", g_strerror(errno
));
543 g_list_foreach(responses
, (GFunc
)purple_txt_response_destroy
, NULL
);
544 g_list_free(responses
);
549 res
= g_new0(PurpleTxtResponse
, 1);
550 res
->content
= g_new0(gchar
, len
);
552 red
= read(source
, res
->content
, len
);
554 purple_debug_error("dnssrv","unable to read txt "
555 "response: %s\n", g_strerror(errno
));
557 purple_txt_response_destroy(res
);
558 g_list_foreach(responses
, (GFunc
)purple_txt_response_destroy
, NULL
);
559 g_list_free(responses
);
563 responses
= g_list_prepend(responses
, res
);
566 responses
= g_list_reverse(responses
);
567 cb(responses
, query_data
->extradata
);
569 purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno
);
575 waitpid(query_data
->pid
, &status
, 0);
576 purple_srv_txt_query_destroy(query_data
);
581 /** The Jabber Server code was inspiration for parts of this. */
584 res_main_thread_cb(gpointer data
)
586 PurpleSrvResponse
*srvres
= NULL
;
587 PurpleSrvTxtQueryData
*query_data
= data
;
588 if(query_data
->error_message
!= NULL
) {
589 purple_debug_error("dnssrv", "%s", query_data
->error_message
);
590 if (query_data
->type
== DNS_TYPE_SRV
) {
591 if (query_data
->cb
.srv
)
592 query_data
->cb
.srv(srvres
, 0, query_data
->extradata
);
593 } else if (query_data
->type
== DNS_TYPE_TXT
) {
594 if (query_data
->cb
.txt
)
595 query_data
->cb
.txt(NULL
, query_data
->extradata
);
598 if (query_data
->type
== DNS_TYPE_SRV
) {
599 PurpleSrvResponse
*srvres_tmp
= NULL
;
600 GList
*lst
= query_data
->results
;
601 int size
= g_list_length(lst
);
603 if(query_data
->cb
.srv
&& size
> 0)
604 srvres_tmp
= srvres
= g_new0(PurpleSrvResponse
, size
);
606 PurpleSrvResponse
*lstdata
= lst
->data
;
607 lst
= g_list_delete_link(lst
, lst
);
609 if(query_data
->cb
.srv
)
610 memcpy(srvres_tmp
++, lstdata
, sizeof(PurpleSrvResponse
));
614 query_data
->results
= NULL
;
616 purple_debug_info("dnssrv", "found %d SRV entries\n", size
);
618 if(query_data
->cb
.srv
) query_data
->cb
.srv(srvres
, size
, query_data
->extradata
);
619 } else if (query_data
->type
== DNS_TYPE_TXT
) {
620 GList
*lst
= query_data
->results
;
622 purple_debug_info("dnssrv", "found %d TXT entries\n", g_list_length(lst
));
624 if (query_data
->cb
.txt
) {
625 query_data
->results
= NULL
;
626 query_data
->cb
.txt(lst
, query_data
->extradata
);
629 purple_debug_error("dnssrv", "unknown query type");
633 query_data
->resolver
= NULL
;
634 query_data
->handle
= 0;
636 purple_srv_txt_query_destroy(query_data
);
642 res_thread(gpointer data
)
644 PDNS_RECORD dr
= NULL
;
647 PurpleSrvTxtQueryData
*query_data
= data
;
648 type
= query_data
->type
;
649 ds
= MyDnsQuery_UTF8(query_data
->query
, type
, DNS_QUERY_STANDARD
, NULL
, &dr
, NULL
);
650 if (ds
!= ERROR_SUCCESS
) {
651 gchar
*msg
= g_win32_error_message(ds
);
652 if (type
== DNS_TYPE_SRV
) {
653 query_data
->error_message
= g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg
, ds
);
654 } else if (type
== DNS_TYPE_TXT
) {
655 query_data
->error_message
= g_strdup_printf("Couldn't look up TXT record. %s (%lu).\n", msg
, ds
);
659 if (type
== DNS_TYPE_SRV
) {
662 DNS_SRV_DATA
*srv_data
;
663 PurpleSrvResponse
*srvres
;
665 for (dr_tmp
= dr
; dr_tmp
!= NULL
; dr_tmp
= dr_tmp
->pNext
) {
666 /* Discard any incorrect entries. I'm not sure if this is necessary */
667 if (dr_tmp
->wType
!= type
|| strcmp(dr_tmp
->pName
, query_data
->query
) != 0) {
671 srv_data
= &dr_tmp
->Data
.SRV
;
672 srvres
= g_new0(PurpleSrvResponse
, 1);
673 strncpy(srvres
->hostname
, srv_data
->pNameTarget
, 255);
674 srvres
->hostname
[255] = '\0';
675 srvres
->pref
= srv_data
->wPriority
;
676 srvres
->port
= srv_data
->wPort
;
677 srvres
->weight
= srv_data
->wWeight
;
679 lst
= g_list_prepend(lst
, srvres
);
682 MyDnsRecordListFree(dr
, DnsFreeRecordList
);
683 query_data
->results
= purple_srv_sort(lst
);
684 } else if (type
== DNS_TYPE_TXT
) {
687 DNS_TXT_DATA
*txt_data
;
688 PurpleTxtResponse
*txtres
;
690 for (dr_tmp
= dr
; dr_tmp
!= NULL
; dr_tmp
= dr_tmp
->pNext
) {
694 /* Discard any incorrect entries. I'm not sure if this is necessary */
695 if (dr_tmp
->wType
!= type
|| strcmp(dr_tmp
->pName
, query_data
->query
) != 0) {
699 txt_data
= &dr_tmp
->Data
.TXT
;
700 txtres
= g_new0(PurpleTxtResponse
, 1);
702 s
= g_string_new("");
703 for (i
= 0; i
< txt_data
->dwStringCount
; ++i
)
704 s
= g_string_append(s
, txt_data
->pStringArray
[i
]);
705 txtres
->content
= g_string_free(s
, FALSE
);
707 lst
= g_list_append(lst
, txtres
);
710 MyDnsRecordListFree(dr
, DnsFreeRecordList
);
711 query_data
->results
= lst
;
717 /* back to main thread */
718 /* Note: this should *not* be attached to query_data->handle - it will cause leakage */
719 purple_timeout_add(0, res_main_thread_cb
, query_data
);
727 PurpleSrvTxtQueryData
*
728 purple_srv_resolve(const char *protocol
, const char *transport
,
729 const char *domain
, PurpleSrvCallback cb
, gpointer extradata
)
731 return purple_srv_resolve_account(NULL
, protocol
, transport
, domain
,
735 PurpleSrvTxtQueryData
*
736 purple_srv_resolve_account(PurpleAccount
*account
, const char *protocol
,
737 const char *transport
, const char *domain
, PurpleSrvCallback cb
,
742 PurpleSrvTxtQueryData
*query_data
;
743 PurpleProxyType proxy_type
;
745 PurpleSrvInternalQuery internal_query
;
750 static gboolean initialized
= FALSE
;
753 if (!protocol
|| !*protocol
|| !transport
|| !*transport
|| !domain
|| !*domain
) {
754 purple_debug_error("dnssrv", "Wrong arguments\n");
755 cb(NULL
, 0, extradata
);
756 g_return_val_if_reached(NULL
);
759 proxy_type
= purple_proxy_info_get_type(
760 purple_proxy_get_setup(account
));
761 if (proxy_type
== PURPLE_PROXY_TOR
) {
762 purple_debug_info("dnssrv", "Aborting SRV lookup in Tor Proxy mode.");
763 cb(NULL
, 0, extradata
);
768 if (!dns_str_is_ascii(domain
)) {
769 int ret
= purple_network_convert_idn_to_ascii(domain
, &hostname
);
771 purple_debug_error("dnssrv", "IDNA ToASCII failed\n");
772 cb(NULL
, 0, extradata
);
775 } else /* Fallthru is intentional */
777 hostname
= g_strdup(domain
);
779 query
= g_strdup_printf("_%s._%s.%s", protocol
, transport
, hostname
);
780 purple_debug_info("dnssrv","querying SRV record for %s: %s\n", domain
,
784 query_data
= query_data_new(PurpleDnsTypeSrv
, query
, extradata
);
785 query_data
->cb
.srv
= cb
;
787 if (purple_srv_txt_query_ui_resolve(query_data
))
793 if(pipe(in
) || pipe(out
)) {
794 purple_debug_error("dnssrv", "Could not create pipe\n");
797 cb(NULL
, 0, extradata
);
803 purple_debug_error("dnssrv", "Could not create process!\n");
806 cb(NULL
, 0, extradata
);
818 resolve(in
[0], out
[1]);
819 /* resolve() does not return */
825 internal_query
.type
= T_SRV
;
826 strncpy(internal_query
.query
, query
, 255);
827 internal_query
.query
[255] = '\0';
829 if (write(in
[1], &internal_query
, sizeof(internal_query
)) < 0)
830 purple_debug_error("dnssrv", "Could not write to SRV resolver\n");
832 query_data
->pid
= pid
;
833 query_data
->fd_out
= out
[0];
834 query_data
->fd_in
= in
[1];
835 query_data
->handle
= purple_input_add(out
[0], PURPLE_INPUT_READ
, resolved
, query_data
);
840 MyDnsQuery_UTF8
= (void*) wpurple_find_and_loadproc("dnsapi.dll", "DnsQuery_UTF8");
841 MyDnsRecordListFree
= (void*) wpurple_find_and_loadproc(
842 "dnsapi.dll", "DnsRecordListFree");
846 if (!MyDnsQuery_UTF8
|| !MyDnsRecordListFree
)
847 query_data
->error_message
= g_strdup("System missing DNS API (Requires W2K+)\n");
849 query_data
->resolver
= g_thread_create(res_thread
, query_data
, FALSE
, &err
);
850 if (query_data
->resolver
== NULL
) {
851 query_data
->error_message
= g_strdup_printf("SRV thread create failure: %s\n", (err
&& err
->message
) ? err
->message
: "");
856 /* The query isn't going to happen, so finish the SRV lookup now.
857 * Asynchronously call the callback since stuff may not expect
858 * the callback to be called before this returns */
859 if (query_data
->error_message
!= NULL
)
860 query_data
->handle
= purple_timeout_add(0, res_main_thread_cb
, query_data
);
866 PurpleSrvTxtQueryData
*purple_txt_resolve(const char *owner
,
867 const char *domain
, PurpleTxtCallback cb
, gpointer extradata
)
869 return purple_txt_resolve_account(NULL
, owner
, domain
, cb
, extradata
);
872 PurpleSrvTxtQueryData
*purple_txt_resolve_account(PurpleAccount
*account
,
873 const char *owner
, const char *domain
, PurpleTxtCallback cb
,
878 PurpleSrvTxtQueryData
*query_data
;
879 PurpleProxyType proxy_type
;
881 PurpleSrvInternalQuery internal_query
;
886 static gboolean initialized
= FALSE
;
889 proxy_type
= purple_proxy_info_get_type(
890 purple_proxy_get_setup(account
));
891 if (proxy_type
== PURPLE_PROXY_TOR
) {
892 purple_debug_info("dnssrv", "Aborting TXT lookup in Tor Proxy mode.");
898 if (!dns_str_is_ascii(domain
)) {
899 int ret
= purple_network_convert_idn_to_ascii(domain
, &hostname
);
901 purple_debug_error("dnssrv", "IDNA ToASCII failed\n");
905 } else /* fallthru is intentional */
907 hostname
= g_strdup(domain
);
909 query
= g_strdup_printf("%s.%s", owner
, hostname
);
910 purple_debug_info("dnssrv","querying TXT record for %s: %s\n", domain
,
914 query_data
= query_data_new(PurpleDnsTypeTxt
, query
, extradata
);
915 query_data
->cb
.txt
= cb
;
917 if (purple_srv_txt_query_ui_resolve(query_data
)) {
918 /* query intentionally not freed
924 if(pipe(in
) || pipe(out
)) {
925 purple_debug_error("dnssrv", "Could not create pipe\n");
934 purple_debug_error("dnssrv", "Could not create process!\n");
949 resolve(in
[0], out
[1]);
950 /* resolve() does not return */
956 internal_query
.type
= T_TXT
;
957 strncpy(internal_query
.query
, query
, 255);
958 internal_query
.query
[255] = '\0';
960 if (write(in
[1], &internal_query
, sizeof(internal_query
)) < 0)
961 purple_debug_error("dnssrv", "Could not write to TXT resolver\n");
963 query_data
->pid
= pid
;
964 query_data
->fd_out
= out
[0];
965 query_data
->fd_in
= in
[1];
966 query_data
->handle
= purple_input_add(out
[0], PURPLE_INPUT_READ
, resolved
, query_data
);
971 MyDnsQuery_UTF8
= (void*) wpurple_find_and_loadproc("dnsapi.dll", "DnsQuery_UTF8");
972 MyDnsRecordListFree
= (void*) wpurple_find_and_loadproc(
973 "dnsapi.dll", "DnsRecordListFree");
977 if (!MyDnsQuery_UTF8
|| !MyDnsRecordListFree
)
978 query_data
->error_message
= g_strdup("System missing DNS API (Requires W2K+)\n");
980 query_data
->resolver
= g_thread_create(res_thread
, query_data
, FALSE
, &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
: "");
987 /* The query isn't going to happen, so finish the TXT lookup now.
988 * Asynchronously call the callback since stuff may not expect
989 * the callback to be called before this returns */
990 if (query_data
->error_message
!= NULL
)
991 query_data
->handle
= purple_timeout_add(0, res_main_thread_cb
, query_data
);
998 purple_txt_cancel(PurpleSrvTxtQueryData
*query_data
)
1000 purple_srv_txt_query_destroy(query_data
);
1004 purple_srv_cancel(PurpleSrvTxtQueryData
*query_data
)
1006 purple_srv_txt_query_destroy(query_data
);
1010 purple_txt_response_get_content(PurpleTxtResponse
*resp
)
1012 g_return_val_if_fail(resp
!= NULL
, NULL
);
1014 return resp
->content
;
1017 void purple_txt_response_destroy(PurpleTxtResponse
*resp
)
1019 g_return_if_fail(resp
!= NULL
);
1021 g_free(resp
->content
);
1026 * Only used as the callback for the ui ops.
1029 purple_srv_query_resolved(PurpleSrvTxtQueryData
*query_data
, GList
*records
)
1032 PurpleSrvResponse
*records_array
;
1035 g_return_if_fail(records
!= NULL
);
1037 if (query_data
->cb
.srv
== NULL
) {
1038 purple_srv_txt_query_destroy(query_data
);
1041 g_free(records
->data
);
1042 records
= g_list_delete_link(records
, records
);
1047 records
= purple_srv_sort(records
);
1048 length
= g_list_length(records
);
1050 purple_debug_info("dnssrv", "SRV records resolved for %s, count: %d\n",
1051 query_data
->query
, length
);
1053 records_array
= g_new(PurpleSrvResponse
, length
);
1054 for (l
= records
; l
; l
= l
->next
, i
++) {
1055 records_array
[i
] = *(PurpleSrvResponse
*)l
->data
;
1058 query_data
->cb
.srv(records_array
, length
, query_data
->extradata
);
1060 purple_srv_txt_query_destroy(query_data
);
1063 g_free(records
->data
);
1064 records
= g_list_delete_link(records
, records
);
1069 * Only used as the callback for the ui ops.
1072 purple_txt_query_resolved(PurpleSrvTxtQueryData
*query_data
, GList
*entries
)
1074 g_return_if_fail(entries
!= NULL
);
1076 purple_debug_info("dnssrv", "TXT entries resolved for %s, count: %d\n", query_data
->query
, g_list_length(entries
));
1078 /* the callback should g_free the entries.
1080 if (query_data
->cb
.txt
!= NULL
)
1081 query_data
->cb
.txt(entries
, query_data
->extradata
);
1084 g_free(entries
->data
);
1085 entries
= g_list_delete_link(entries
, entries
);
1089 purple_srv_txt_query_destroy(query_data
);
1093 purple_srv_query_failed(PurpleSrvTxtQueryData
*query_data
, const gchar
*error_message
)
1095 purple_debug_error("dnssrv", "%s\n", error_message
);
1097 if (query_data
->cb
.srv
!= NULL
)
1098 query_data
->cb
.srv(NULL
, 0, query_data
->extradata
);
1100 purple_srv_txt_query_destroy(query_data
);
1104 purple_srv_txt_query_ui_resolve(PurpleSrvTxtQueryData
*query_data
)
1106 PurpleSrvTxtQueryUiOps
*ops
= purple_srv_txt_query_get_ui_ops();
1108 if (ops
&& ops
->resolve
)
1109 return ops
->resolve(query_data
, (query_data
->type
== T_SRV
? purple_srv_query_resolved
: purple_txt_query_resolved
), purple_srv_query_failed
);
1115 purple_srv_txt_query_set_ui_ops(PurpleSrvTxtQueryUiOps
*ops
)
1117 srv_txt_query_ui_ops
= ops
;
1120 PurpleSrvTxtQueryUiOps
*
1121 purple_srv_txt_query_get_ui_ops(void)
1123 /* It is perfectly acceptable for srv_txt_query_ui_ops to be NULL; this just
1124 * means that the default platform-specific implementation will be used.
1126 return srv_txt_query_ui_ops
;
1130 purple_srv_txt_query_get_query(PurpleSrvTxtQueryData
*query_data
)
1132 g_return_val_if_fail(query_data
!= NULL
, NULL
);
1134 return query_data
->query
;
1139 purple_srv_txt_query_get_type(PurpleSrvTxtQueryData
*query_data
)
1141 g_return_val_if_fail(query_data
!= NULL
, 0);
1143 return query_data
->type
;