1 /*
2  * This file is part of the Sofia-SIP package
3  *
4  * Copyright (C) 2006 Nokia Corporation.
5  *
6  * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24 
25 /**
26  * This is an example program for @b sresolv library in synchronous mode.
27  *
28  * @author Pekka Pessi <Pekka.Pessi@nokia.com>
29  *
30  * @date Original Created: Tue Jul 16 18:50:14 2002 ppessi
31  */
32 
33 /**@page sip-dig Resolve SIP URIs.
34  *
35  * @section sip_dig_synopsis Synopsis
36  * <tt>sip-dig [OPTIONS] uri...</tt>
37  *
38  * @section sip_dig_description Description
39  * The @em sip-dig utility resolves SIP URIs as described in @RFC3263. It
40  * queries NAPTR, SRV and A/AAAA records and prints out the resulting
41  * transport addresses.
42  *
43  * The default transports are: UDP, TCP, SCTP, TLS and TLS-SCTP. The SIPS
44  * URIs are resolved using only TLS transports, TLS and TLS-SCTP. If not
45  * otherwise indicated by NAPTR or SRV records, the sip-dig uses UDP and TCP
46  * as transports for SIP and TLS for SIPS URIs.
47  *
48  * The results are printed intended, with a preference followed by weight,
49  * then protocol name, port number and IP address in numeric format.
50  *
51  * @section sip_dig_options Command Line Options
52  * The @e sip-dig utility accepts following command line options:
53  * <dl>
54  * <dt>-p <em>protoname</em></dt>
55  * <dd>Use named transport protocol. The <em>protoname</em> can be either
56  * well-known, e.g., "udp", or it can specify NAPTR service and SRV
57  * identifier, e.g., "tls-udp/SIPS+D2U/_sips._udp.".
58  * </dd>
59  * <dt>--udp</dt>
60  * <dd>Use UDP transport protocol.
61  * </dd>
62  * <dt>--tcp</dt>
63  * <dd>Use TCP transport protocol.
64  * </dd>
65  * <dt>--tls</dt>
66  * <dd>Use TLS over TCP transport protocol.
67  * </dd>
68  * <dt>--sctp</dt>
69  * <dd>Use SCTP transport protocol.
70  * </dd>
71  * <dt>--tls-sctp</dt>
72  * <dd>Use TLS over SCTP transport protocol.
73  * </dd>
74  * <dt>--no-sctp</dt>
75  * <dd>Ignore SCTP or TLS-SCTP records in the list of default transports.
76  * This option has no effect if transport protocols has been explicitly
77  * listed.
78  * </dd>
79  * <dt>-4</dt>
80  * <dd>Query IP4 addresses (A records)
81  * </dd>
82  * <dt>-6</dt>
83  * <dd>Query IP6 addresses (AAAA records).
84  * </dd>
85  * <dt>-v</dt>
86  * <dd>Be verbatim.
87  * </dd>
88  * <dt></dt>
89  * <dd>
90  * </dd>
91  * </dl>
92  *
93  * @section sip_dig_return Return Codes
94  * <table>
95  * <tr><td>0<td>when successful (a 2XX-series response is received)
96  * <tr><td>1<td>when unsuccessful (a 3XX..6XX-series response is received)
97  * <tr><td>2<td>initialization failure
98  * </table>
99  *
100  * @section sip_dig_examples Examples
101  *
102  * Resolve sip:openlaboratory.net, prefer TLS over TCP, TCP over UDP:
103  * @code
104  * $ sip-dig --tls --tcp --udp sip:openlaboratory.net
105  *	1 0.333 tls 5061 212.213.221.127
106  *	2 0.333 tcp 5060 212.213.221.127
107  *	3 0.333 udp 5060 212.213.221.127
108  * @endcode
109  *
110  * Resolve sips:example.net with TLS over SCTP (TLS-SCTP) and TLS:
111  * @code
112  * $ sip-dig -p tls-sctp --tls sips:example.net
113  *	1 0.500 tls-udp 5061 172.21.55.26
114  *	2 0.500 tls 5061 172.21.55.26
115  * @endcode
116  *
117  * @section sip_dig_environment Environment
118  * #SRESOLV_DEBUG, SRESOLV_CONF
119  *
120  * @section sip_dig_bugs Reporting Bugs
121  * Report bugs to <sofia-sip-devel@lists.sourceforge.net>.
122  *
123  * @section sip_dig_author Author
124  * Written by Pekka Pessi <pekka -dot pessi -at- nokia -dot- com>
125  *
126  * @section sip_dig_copyright Copyright
127  * Copyright (C) 2006 Nokia Corporation.
128  *
129  * This program is free software; see the source for copying conditions.
130  * There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
131  * PARTICULAR PURPOSE.
132  */
133 
134 #include "config.h"
135 
136 #include "sofia-sip/su.h"
137 
138 #include "sofia-resolv/sres.h"
139 #include "sofia-resolv/sres_record.h"
140 
141 #include "sofia-sip/url.h"
142 #include "sofia-sip/su_alloc.h"
143 #include "sofia-sip/su_string.h"
144 #include "sofia-sip/hostdomain.h"
145 
146 char const name[] = "sip-dig";
147 
148 #include <assert.h>
149 #include <stdlib.h>
150 #include <string.h>
151 #include <stdio.h>
152 
153 enum { N_TPORT = 16 };
154 
155 struct transport { char const *name, *service, *srv; };
156 
157 struct dig {
158   sres_resolver_t *sres;
159 
160   unsigned preference, ip4, ip6, sips, print;
161 
162   struct transport tports[N_TPORT + 1];
163 };
164 
165 int dig_naptr(struct dig *dig, char const *host, double weight);
166 
167 int dig_all_srvs(struct dig *dig, char const *tport, char const *host,
168 		 double weight);
169 
170 int dig_srv(struct dig *dig, char const *tport, char const *host,
171 	    double weight);
172 
173 int dig_srv_at(struct dig *dig,
174 	       char const *tport, sres_record_t **answers,
175 	       double weight, int pweight,
176 	       int priority);
177 
178 int dig_addr(struct dig *dig,
179 	     char const *tport, char const *host, char const *port,
180 	     double weight);
181 
182 void print_addr_results(struct transport const *tports,
183 			char const *tport, char const *tport2,
184 			sres_record_t **answers, int type, int af,
185 			char const *port,
186 			double weight, int preference);
187 
188 void print_result(char const *addr, char const *port, char const *tport,
189 		  double weight,
190 		  unsigned preference);
191 
192 int prepare_transport(struct dig *dig, char const *tport);
193 
194 int count_transports(struct dig *dig,
195 		     char const *tp1,
196 		     char const *tp2);
197 
usage(int exitcode)198 void usage(int exitcode)
199 {
200   fprintf(stderr, "usage: %s [OPTIONS] [@dnsserver] uri\n", name);
201   exit(exitcode);
202 }
203 
main(int argc,char * argv[])204 int main(int argc, char *argv[])
205 {
206   int exitcode = 0;
207   int o_sctp = 1, o_tls_sctp = 1, o_verbatim = 1;
208   int family = 0, multiple = 0;
209   /*
210   char const *dnsserver = NULL;
211   */
212   char const *string;
213   url_t *uri = NULL;
214 
215   char const *host;
216   char const *port;
217   char *transport = NULL, tport[32];
218 
219   struct dig dig[1] = {{ NULL }};
220 
221   if (su_init() != 0)
222     return -1;
223 
224   while (argv[1] && argv[1][0] == '-') {
225     if (strcmp(argv[1], "-v") == 0)
226       o_verbatim++;
227     else if (strcmp(argv[1], "-6") == 0)
228       dig->ip6 = ++family;
229     else if (strcmp(argv[1], "-4") == 0)
230       dig->ip4 = ++family;
231     else if (strncmp(argv[1], "-p", 2) == 0) {
232       char const *proto;
233 
234       if (argv[1][2] == '=')
235 	proto = argv[1] + 3;
236       else if (argv[1][2])
237 	proto = argv[1] + 2;
238       else
239 	proto = argv++[2];
240 
241       if (proto == NULL)
242 	usage(2);
243 
244       if (prepare_transport(dig, proto) < 0)
245 	exit(2);
246     }
247     else if (strcmp(argv[1], "--udp") == 0)
248       prepare_transport(dig, "udp");
249     else if (strcmp(argv[1], "--tcp") == 0)
250       prepare_transport(dig, "tcp");
251     else if (strcmp(argv[1], "--tls") == 0)
252       prepare_transport(dig, "tls");
253     else if (strcmp(argv[1], "--sctp") == 0)
254       prepare_transport(dig, "sctp");
255     else if (strcmp(argv[1], "--tls-sctp") == 0)
256       prepare_transport(dig, "tls-sctp");
257     else if (strcmp(argv[1], "--tls-udp") == 0)
258       prepare_transport(dig, "tls-udp");
259     else if (strcmp(argv[1], "--no-sctp") == 0)
260       o_sctp = 0, o_tls_sctp = 0;
261     else if (strcmp(argv[1], "--help") == 0)
262       usage(0);
263     else if (strcmp(argv[1], "-h") == 0)
264       usage(0);
265     else if (strcmp(argv[1], "-?") == 0)
266       usage(0);
267     else if (strcmp(argv++[1], "-") == 0)
268       break;
269     else
270       usage(2);
271     argv++;
272   }
273 
274   if (!family)
275     dig->ip4 = 1, dig->ip6 = 2;
276 
277   /*
278   if (argv[1] && argv[1][0] == '@')
279     dnsserver = argv++[1] + 1;
280   */
281 
282   if (!argv[1])
283     usage(2);
284 
285   multiple = argv[1] && argv[2];
286 
287   if (!count_transports(dig, NULL, NULL)) {
288     prepare_transport(dig, "udp");
289     prepare_transport(dig, "tcp");
290     if (o_sctp)
291       prepare_transport(dig, "sctp");
292     prepare_transport(dig, "tls");
293     if (o_tls_sctp)
294       prepare_transport(dig, "tls-sctp");
295   }
296 
297   dig->sres = sres_resolver_new(getenv("SRESOLV_CONF"));
298   if (!dig->sres)
299     perror("sres_resolver_new"), exit(1);
300 
301   for (; (string = argv[1]); argv++) {
302     if (multiple)
303       puts(string);
304 
305     uri = url_hdup(NULL, (void *)string);
306 
307     if (uri && uri->url_type == url_unknown)
308       url_sanitize(uri);
309 
310     if (uri && uri->url_type == url_any)
311       continue;
312 
313     if (!uri || (uri->url_type != url_sip && uri->url_type != url_sips)) {
314       fprintf(stderr, "%s: invalid uri\n", string);
315       exitcode = 1;
316       continue;
317     }
318 
319     port = url_port(uri);
320     if (port && !port[0]) port = NULL;
321     if (url_param(uri->url_params, "transport=", tport, sizeof tport) > 0)
322       transport = tport;
323 
324     host = uri->url_host;
325 
326     if (host_is_ip_address(host)) {
327       if (transport) {
328 	print_result(host, port, transport, 1.0, 1);
329       }
330       else if (uri->url_type == url_sips) {
331 	print_result(host, port, "tls", 1.0, 1);
332       }
333       else {
334 	print_result(host, port, "udp", 1.0, 1);
335 	print_result(host, port, "tcp", 1.0, 2);
336       }
337       continue;
338     }
339 
340     if (!host_is_domain(host)) {
341       fprintf(stderr, "%s: invalid host\n", string);
342       exitcode = 1;
343       continue;
344     }
345 
346     dig->sips = uri->url_type == url_sips;
347     dig->preference = 1;
348 
349     if (!port && !transport && dig_naptr(dig, host, 1.0))
350       continue /* resolved naptr */;
351     else if (!port && dig_all_srvs(dig, transport, host, 1.0))
352       continue /* resolved srv */;
353     else if (dig_addr(dig, transport, host, port, 1.0))
354       continue /* resolved a/aaaa */;
355 
356     fprintf(stderr, "%s: not found\n", string);
357     exitcode = 1;
358   }
359 
360   sres_resolver_unref(dig->sres);
361 
362   return exitcode;
363 }
364 
365 
transport_is_secure(char const * tportname)366 int transport_is_secure(char const *tportname)
367 {
368   return su_casenmatch(tportname, "tls", 3);
369 }
370 
prepare_transport(struct dig * dig,char const * tport)371 int prepare_transport(struct dig *dig, char const *tport)
372 {
373   struct transport *tports = dig->tports;
374   int j;
375 
376   for (j = 0; j < N_TPORT; j++) {
377     if (!tports[j].name)
378       break;
379     if (su_casematch(tports[j].name, tport))
380       return 1;
381   }
382 
383   if (j == N_TPORT)
384     return 0;
385 
386   if (strchr(tport, '/')) {
387     char *service = strchr(tport, '/');
388     char *srv = strchr(service + 1, '/');
389 
390     if (!srv || srv[strlen(srv) - 1] != '.') {
391       fprintf(stderr, "%s: invalid transport specifier \"%s\"\n", name, tport);
392       fputs(
393 "\tspecifier should have name/service/srv-id\n"
394 "\twhere name is protocol name (e.g, \"tls-udp\")\n"
395 "\t      service specifies service as per RFC 2915 (e.g., \"SIPS+D2U\")\n"
396 "\t      srv-id is prefix for SRV lookup (e.g., \"_sips._udp.\")\n",
397          stderr);
398       if (srv)
399 	fputs("\t      and it should end with a dot \".\"\n", stderr);
400       return -1;
401     }
402 
403     *service++ = '\0', *srv++ = '\0';
404 
405     tports[j].name = tport,
406     tports[j].service = service;
407     tports[j].srv = srv;
408   }
409   else if (su_casematch(tport, "udp")) {
410     tports[j].name = "udp";
411     tports[j].service = "SIP+D2U";
412     tports[j].srv = "_sip._udp.";
413   }
414   else if (su_casematch(tport, "tcp")) {
415     tports[j].name = "tcp";
416     tports[j].service = "SIP+D2T";
417     tports[j].srv = "_sip._tcp.";
418   }
419   else if (su_casematch(tport, "tls")) {
420     tports[j].name = "tls";
421     tports[j].service = "SIPS+D2T";
422     tports[j].srv = "_sips._tcp.";
423   }
424   else if (su_casematch(tport, "sctp")) {
425     tports[j].name = "sctp";
426     tports[j].service = "SIP+D2S";
427     tports[j].srv = "_sip._sctp.";
428   }
429   else if (su_casematch(tport, "tls-sctp")) {
430     tports[j].name = "tls-sctp";
431     tports[j].service = "SIPS+D2S";
432     tports[j].srv = "_sips._sctp.";
433   }
434   else {
435     fprintf(stderr, "%s: unknown transport \"%s\"\n", name, tport);
436     return -1;
437   }
438 
439   j++;
440 
441   tports[j].service = tports[j].srv = tports[j].name = NULL;
442 
443   return 1;
444 }
445 
446 int
count_transports(struct dig * dig,char const * tport,char const * tport2)447 count_transports(struct dig *dig,
448 		 char const *tport,
449 		 char const *tport2)
450 {
451 
452   int i, tcount = 0;
453   struct transport const *tports = dig->tports;
454 
455   for (i = 0; tports[i].name; i++) {
456     if (dig->sips && !transport_is_secure(tports[i].name))
457       continue;
458     if (!tport || su_casematch(tport, tports[i].name))
459       tcount++;
460     else if (tport2 && su_casematch(tport2, tports[i].name))
461       tcount++;
462   }
463 
464   return tcount;
465 }
466 
467 struct transport const *
transport_by_service(struct transport const * tports,char const * s)468 transport_by_service(struct transport const *tports, char const *s)
469 {
470   int i;
471 
472   for (i = 0; tports[i].name; i++) {
473     if (su_casematch(tports[i].service, s))
474       return tports + i;
475   }
476 
477   return NULL;
478 }
479 
dig_naptr(struct dig * dig,char const * host,double weight)480 int dig_naptr(struct dig *dig,
481 	      char const *host,
482 	      double weight)
483 {
484   sres_record_t **answers = NULL;
485   struct transport const *tp;
486   int i, error;
487   int order = 0, count = 0, nacount = 0, scount = 0;
488 
489   error = sres_blocking_query(dig->sres, sres_type_naptr, host, 0, &answers);
490   if (error < 0)
491     return 0;
492 
493   /* Sort by priority */
494   sres_sort_answers(dig->sres, answers);
495 
496   /* Count number of matching naptrs */
497   for (i = 0; answers[i]; i++) {
498     sres_naptr_record_t const *na = answers[i]->sr_naptr;
499 
500     if (na->na_record->r_type != sres_type_naptr || na->na_record->r_status)
501       continue;
502 
503     if (dig->print)
504       printf("%s\n\t%d IN NAPTR %u %u \"%s\" \"%s\" \"%s\" %s\n",
505 	     na->na_record->r_name, na->na_record->r_ttl,
506 	     na->na_order, na->na_prefer,
507 	     na->na_flags, na->na_services,
508 	     na->na_regexp, na->na_replace);
509 
510     if (!su_casematch(na->na_flags, "s") && !su_casematch(na->na_flags, "a"))
511       continue;
512 
513     if (nacount && order != na->na_order)
514       continue;
515 
516     if (dig->sips && !su_casenmatch(na->na_services, "SIPS+", 5))
517       continue;
518 
519     if (!transport_by_service(dig->tports, na->na_services))
520       continue;
521 
522     order = na->na_order, nacount++;
523   }
524 
525   if (nacount == 0) {
526     sres_free_answers(dig->sres, answers);
527     return 0;
528   }
529 
530   for (i = 0; answers[i]; i++) {
531     sres_naptr_record_t const *na = answers[i]->sr_naptr;
532 
533     if (na->na_record->r_type != sres_type_naptr || na->na_record->r_status)
534       continue;
535     if (order != na->na_order)
536       continue;
537     if (!su_casematch(na->na_flags, "s") && !su_casematch(na->na_flags, "a"))
538       continue;
539     if (dig->sips && !su_casenmatch(na->na_services, "SIPS+", 5))
540       continue;
541 
542     tp = transport_by_service(dig->tports, na->na_services);
543     if (!tp)
544       continue;
545 
546     if (su_casematch(na->na_flags, "s")) {
547       scount = dig_srv(dig, tp->name, na->na_replace, weight / nacount);
548     }
549     else if (su_casematch(na->na_flags, "a")) {
550       scount = dig_addr(dig, tp->name, na->na_replace, NULL, weight / nacount);
551     }
552     else
553       scount = 0;
554 
555     count += scount;
556   }
557 
558   return count;
559 }
560 
dig_all_srvs(struct dig * dig,char const * tport,char const * host,double weight)561 int dig_all_srvs(struct dig *dig,
562 		 char const *tport,
563 		 char const *host,
564 		 double weight)
565 {
566   int i, j, n;
567   int tcount, count = 0, scount;
568   char *domain;
569 
570   struct {
571     char const *proto; sres_record_t **answers;
572   } srvs[N_TPORT + 1] = {{ NULL }};
573 
574   tcount = count_transports(dig, tport, NULL);
575   if (!tcount)
576     return 0;
577 
578   for (i = 0, n = 0; dig->tports[i].name; i++) {
579     if (tport && !su_casematch(dig->tports[i].name, tport))
580       continue;
581 
582     if (dig->sips && !transport_is_secure(dig->tports[i].name))
583       continue;
584 
585     domain = su_strcat(NULL, dig->tports[i].srv, host);
586 
587     if (domain) {
588       if (sres_blocking_query(dig->sres, sres_type_srv, domain, 0,
589 			      &srvs[n].answers) >= 0) {
590 	srvs[n++].proto = dig->tports[i].name;
591       }
592       free(domain);
593     }
594   }
595 
596   if (n == 0)
597     return 0;
598 
599   for (i = 0; i < n; i++) {
600     unsigned priority = 0, pweight = 0, m = 0;
601     sres_record_t **answers = srvs[i].answers;
602     char const *tport = srvs[i].proto;
603 
604     for (j = 0; answers[j]; j++) {
605       sres_srv_record_t const *srv = answers[j]->sr_srv;
606 
607       if (srv->srv_record->r_type != sres_type_srv)
608 	continue;
609       if (srv->srv_record->r_status != 0)
610 	continue;
611 
612       if (srv->srv_priority != priority && pweight != 0) {
613 	scount = dig_srv_at(dig, tport, answers, weight / n, pweight,
614 			    priority);
615 	if (scount) dig->preference++;
616 	count += scount;
617 	pweight = 0, m = 0;
618       }
619 
620       priority = srv->srv_priority, pweight += srv->srv_weight, m++;
621     }
622 
623     if (m) {
624       scount = dig_srv_at(dig, tport, answers, weight / n, pweight, priority);
625       if (scount)
626 	dig->preference++;
627       count += scount;
628     }
629   }
630 
631   return count;
632 }
633 
dig_srv(struct dig * dig,char const * tport,char const * domain,double weight)634 int dig_srv(struct dig *dig,
635 	    char const *tport,
636 	    char const *domain,
637 	    double weight)
638 {
639   sres_record_t **answers = NULL;
640   int j, n, error;
641   int count = 0, scount = 0;
642 
643   uint32_t priority, pweight;
644 
645   assert(tport && domain);
646 
647   error = sres_blocking_query(dig->sres, sres_type_srv, domain, 0, &answers);
648   if (error < 0)
649     return 0;
650 
651   /* Sort by priority */
652   sres_sort_answers(dig->sres, answers);
653 
654   priority = 0; pweight = 0; n = 0;
655 
656   for (j = 0; answers[j]; j++) {
657     sres_srv_record_t const *srv = answers[j]->sr_srv;
658 
659     if (srv->srv_record->r_type != sres_type_srv)
660       continue;
661     if (srv->srv_record->r_status != 0)
662       continue;
663 
664     if (srv->srv_priority != priority && pweight != 0) {
665       scount = dig_srv_at(dig, tport, answers, weight, pweight,
666 			  priority);
667       if (scount) dig->preference++;
668       count += scount;
669       pweight = 0, n = 0;
670     }
671 
672     priority = srv->srv_priority, pweight += srv->srv_weight, n++;
673   }
674 
675   if (n) {
676     scount = dig_srv_at(dig, tport, answers, weight, pweight, priority);
677     if (scount) dig->preference++;
678     count += scount;
679   }
680 
681   sres_free_answers(dig->sres, answers);
682 
683   return count;
684 }
685 
dig_srv_at(struct dig * dig,char const * tport,sres_record_t ** answers,double weight,int pweight,int priority)686 int dig_srv_at(struct dig *dig,
687 	       char const *tport,
688 	       sres_record_t **answers,
689 	       double weight, int pweight,
690 	       int priority)
691 {
692   int count = 0;
693   int i;
694   char port[8];
695 
696   if (pweight == 0)
697     pweight = 1;
698 
699   for (i = 0; answers[i]; i++) {
700     sres_srv_record_t const *srv = answers[i]->sr_srv;
701     if (srv->srv_record->r_type != sres_type_srv)
702       continue;
703     if (srv->srv_record->r_status != 0)
704       continue;
705     if (srv->srv_priority != priority)
706       continue;
707     snprintf(port, sizeof port, "%u", srv->srv_port);
708 
709     count += dig_addr(dig, tport, srv->srv_target, port,
710 		      weight * srv->srv_weight / pweight);
711   }
712 
713   return count;
714 }
715 
dig_addr(struct dig * dig,char const * tport,char const * host,char const * port,double weight)716 int dig_addr(struct dig *dig,
717 	     char const *tport,
718 	     char const *host,
719 	     char const *port,
720 	     double weight)
721 {
722   int error, i;
723   char const *tport2 = NULL;
724   sres_record_t **answers1 = NULL, **answers2 = NULL;
725   unsigned count1 = 0, count2 = 0, tcount = 0;
726   int type1 = 0, type2 = 0, family1 = 0, family2 = 0;
727 
728   if (dig->ip6 > dig->ip4) {
729     type1 = sres_type_aaaa, family1 = AF_INET6;
730     if (dig->ip4)
731       type2 = sres_type_a, family2 = AF_INET;
732   }
733   else {
734     type1 = sres_type_a, family1 = AF_INET;
735     if (dig->ip6)
736       type2 = sres_type_aaaa, family2 = AF_INET6;
737   }
738 
739   if (tport == NULL) {
740     if (dig->sips)
741       tport = "tls";
742     else
743       tport = "udp", tport2 = "tcp";
744   }
745 
746   tcount = count_transports(dig, tport, tport2);
747   if (!tcount)
748     return 0;
749 
750   if (type1) {
751     error = sres_blocking_query(dig->sres, type1, host, 0, &answers1);
752     if (error >= 0)
753       for (i = 0; answers1[i]; i++) {
754 	sres_common_t *r = answers1[i]->sr_record;
755 	count1 += r->r_type == type1 &&	r->r_status == 0;
756       }
757   }
758 
759   if (type2) {
760     error = sres_blocking_query(dig->sres, type2, host, 0, &answers2);
761     if (error >= 0)
762       for (i = 0; answers2[i]; i++) {
763 	sres_common_t *r = answers2[i]->sr_record;
764 	count2 += r->r_type == type2 &&	r->r_status == 0;
765       }
766   }
767 
768   if (count1 + count2) {
769     double w = weight / (count1 + count2) / tcount;
770 
771     if (count1)
772       print_addr_results(dig->tports, tport, tport2,
773 			 answers1, type1, family1, port,
774 			 w, dig->preference);
775     if (count2)
776       print_addr_results(dig->tports, tport, tport2,
777 			 answers2, type2, family2, port,
778 			 w, dig->preference);
779   }
780 
781   sres_free_answers(dig->sres, answers1);
782   sres_free_answers(dig->sres, answers2);
783 
784   return count1 + count2;
785 }
786 
787 void
print_addr_results(struct transport const * tports,char const * tport,char const * tport2,sres_record_t ** answers,int type,int af,char const * port,double weight,int preference)788 print_addr_results(struct transport const *tports,
789 		   char const *tport, char const *tport2,
790 		   sres_record_t **answers, int type, int af,
791 		   char const *port,
792 		   double weight, int preference)
793 {
794   int i, j;
795   char addr[64];
796 
797   for (i = 0; answers[i]; i++) {
798     if (answers[i]->sr_record->r_type != type)
799       continue;
800     if (answers[i]->sr_record->r_status != 0)
801       continue;
802 
803     su_inet_ntop(af, &answers[i]->sr_a->a_addr, addr, sizeof addr);
804 
805     for (j = 0; tports[j].name; j++) {
806       if (su_casematch(tport, tports[j].name))
807 	print_result(addr, port, tport, weight, preference);
808       if (su_casematch(tport2, tports[j].name))
809 	print_result(addr, port, tport2, weight, preference);
810     }
811   }
812 }
813 
print_result(char const * addr,char const * port,char const * tport,double weight,unsigned preference)814 void print_result(char const *addr,
815 		  char const *port,
816 		  char const *tport,
817 		  double weight,
818 		  unsigned preference)
819 {
820   if (!port || !port[0])
821     port = transport_is_secure(tport) ? "5061" : "5060";
822 
823   printf("\t%u %.3f %s %s %s\n", preference, weight, tport, port, addr);
824 }
825