Update gnulib files.
[shishi.git] / lib / resolv.c
blob7cf64ea6f26715a6810b6243a49f61504f4323c1
1 /* resolv.c --- Resolver glue.
2 * Copyright (C) 2003, 2004, 2007 Simon Josefsson
3 * Copyright (C) 2002 Jeremie Miller, Thomas Muldowney,
4 * Ryan Eatmon, Robert Norris
6 * This file is part of Shishi.
8 * Shishi is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * Shishi 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
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with Shishi; if not, see http://www.gnu.org/licenses or write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301, USA
25 /* This file is based on resolver.h from jabberd - Jabber Open Source
26 * Server, licensed under GPL. See:
28 * http://www.jabberstudio.org/cgi-bin/viewcvs.cgi/jabberd2/resolver/
31 #include "internal.h"
33 #ifdef HAVE_LIBRESOLV
35 /* the largest packet we'll send and receive */
36 #if PACKETSZ > 1024
37 # define MAX_PACKET PACKETSZ
38 #else
39 # define MAX_PACKET (1024)
40 #endif
42 typedef union
44 HEADER hdr;
45 unsigned char buf[MAX_PACKET];
46 } dns_packet_t;
48 static void *
49 txt_rr (dns_packet_t packet, unsigned char *eom, unsigned char **scan)
51 size_t len = (size_t) ** scan;
52 char *p;
54 p = xmalloc (len + 1);
55 memcpy (p, *scan + 1, len);
56 p[len] = '\0';
57 *scan += (unsigned char) (len + 1);
59 return p;
62 static void *
63 srv_rr (dns_packet_t packet, unsigned char *eom, unsigned char **scan)
65 unsigned int priority, weight, port;
66 int len;
67 char host[256];
68 Shishi_dns_srv srv;
70 GETSHORT (priority, *scan);
71 GETSHORT (weight, *scan);
72 GETSHORT (port, *scan);
74 len = dn_expand (packet.buf, eom, *scan, host, 256);
75 if (len < 0)
76 return NULL;
77 *scan = (unsigned char *) (*scan + len);
79 srv = xmalloc (sizeof (*srv));
81 srv->priority = priority;
82 srv->weight = weight;
83 srv->port = port;
85 strcpy (srv->name, host);
87 return (void *) srv;
90 /* compare two srv structures, order by priority then by randomised weight */
91 static int
92 srv_compare (const void *a, const void *b)
94 Shishi_dns_srv aa, bb;
96 if (a == NULL)
97 return 1;
98 if (b == NULL)
99 return -1;
101 aa = (*((Shishi_dns *) a))->rr;
102 bb = (*((Shishi_dns *) b))->rr;
104 if (aa->priority > bb->priority)
105 return 1;
106 if (aa->priority < bb->priority)
107 return -1;
109 if (aa->weight > bb->weight)
110 return -1;
111 if (aa->weight < bb->weight)
112 return 1;
114 return 0;
118 * shishi_resolv:
119 * @zone: owner name of data, e.g. "EXAMPLE.ORG"
120 * @querytype: type of data to query for, e.g., SHISHI_DNS_TXT.
122 * Query DNS resolver for data of type @querytype at owner name @zone.
123 * Currently TXT and SRV types are supported.
125 * Return value: Returns linked list of DNS records, or NULL if query
126 * failed.
128 Shishi_dns
129 shishi_resolv (const char *zone, uint16_t querytype)
131 char host[256];
132 dns_packet_t packet;
133 int len, qdcount, ancount, an, n;
134 unsigned char *eom, *scan;
135 Shishi_dns *reply, first;
136 uint16_t type, class, ttl;
138 if (zone == NULL || *zone == '\0')
139 return NULL;
141 switch (querytype)
143 case T_TXT:
144 case T_SRV:
145 break;
147 default:
148 return NULL;
151 /* do the actual query */
152 if ((len = res_query (zone, C_IN, querytype, packet.buf, MAX_PACKET)) < 0
153 || len < (int) sizeof (HEADER))
154 return NULL;
156 /* we got a valid result, containing two types of records - packet
157 * and answer .. we have to skip over the packet records */
159 /* no. of packets, no. of answers */
160 qdcount = ntohs (packet.hdr.qdcount);
161 ancount = ntohs (packet.hdr.ancount);
163 /* end of the returned message */
164 eom = (unsigned char *) (packet.buf + len);
166 /* our current location */
167 scan = (unsigned char *) (packet.buf + sizeof (HEADER));
169 /* skip over the packet records */
170 while (qdcount > 0 && scan < eom)
172 qdcount--;
173 if ((len = dn_expand (packet.buf, eom, scan, host, 256)) < 0)
174 return NULL;
175 scan = (unsigned char *) (scan + len + QFIXEDSZ);
178 /* create an array to store the replies in */
179 reply = xcalloc (ancount, sizeof (Shishi_dns));
181 an = 0;
182 /* loop through the answer buffer and extract SRV records */
183 while (ancount > 0 && scan < eom)
185 ancount--;
186 len = dn_expand (packet.buf, eom, scan, host, 256);
187 if (len < 0)
189 for (n = 0; n < an; n++)
190 free (reply[n]);
191 free (reply);
192 return NULL;
195 scan += len;
197 /* extract the various parts of the record */
198 GETSHORT (type, scan);
199 GETSHORT (class, scan);
200 GETLONG (ttl, scan);
201 GETSHORT (len, scan);
203 /* skip records we're not interested in */
204 if (type != querytype)
206 scan = (unsigned char *) (scan + len);
207 continue;
210 /* create a new reply structure to save it in */
211 reply[an] = xmalloc (sizeof (*reply[0]));
213 reply[an]->type = type;
214 reply[an]->class = class;
215 reply[an]->ttl = ttl;
217 reply[an]->next = NULL;
219 /* type-specific processing */
220 switch (type)
222 case T_TXT:
223 reply[an]->rr = txt_rr (packet, eom, &scan);
224 break;
226 case T_SRV:
227 reply[an]->rr = srv_rr (packet, eom, &scan);
228 break;
230 default:
231 scan = (unsigned char *) (scan + len);
232 continue;
235 /* fell short, we're done */
236 if (reply[an]->rr == NULL)
238 free (reply[an]);
239 reply[an] = NULL;
240 break;
243 /* on to the next one */
244 an++;
247 /* sort srv records them */
248 if (querytype == T_SRV)
249 qsort (reply, an, sizeof (Shishi_dns), srv_compare);
251 /* build a linked list out of the array elements */
252 for (n = 0; n < an - 1; n++)
253 reply[n]->next = reply[n + 1];
255 first = reply[0];
257 free (reply);
259 return first;
262 #else
264 Shishi_dns
265 shishi_resolv (const char *zone, uint16_t querytype)
267 return NULL;
270 #endif
273 * shishi_resolv_free:
274 * @rrs: list of DNS RR as returned by shishi_resolv().
276 * Deallocate list of DNS RR as returned by shishi_resolv().
278 void
279 shishi_resolv_free (Shishi_dns rrs)
281 Shishi_dns next;
283 while (rrs != NULL)
285 next = rrs->next;
286 free (rrs->rr);
287 free (rrs);
288 rrs = next;