1 /*
2 * The PCI Library -- Resolving ID's via DNS
3 *
4 * Copyright (c) 2007--2008 Martin Mares <[email protected]>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include <string.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14
15 #include "internal.h"
16 #include "names.h"
17
18 #ifdef PCI_USE_DNS
19
20 /*
21 * Our definition of BYTE_ORDER confuses arpa/nameser_compat.h on
22 * Solaris so we must undef it before including arpa/nameser.h.
23 */
24 #ifdef PCI_OS_SUNOS
25 #undef BYTE_ORDER
26 #endif
27
28 #include <netinet/in.h>
29 #include <arpa/nameser.h>
30 #include <resolv.h>
31 #include <netdb.h>
32
33 /*
34 * Unfortunately, there are no portable functions for DNS RR parsing,
35 * so we will do the bit shuffling with our own bare hands.
36 */
37
38 #define GET16(x) do { if (p+2 > end) goto err; x = (p[0] << 8) | p[1]; p += 2; } while (0)
39 #define GET32(x) do { if (p+4 > end) goto err; x = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; p += 4; } while (0)
40
41 enum dns_section {
42 DNS_SEC_QUESTION,
43 DNS_SEC_ANSWER,
44 DNS_SEC_AUTHORITY,
45 DNS_SEC_ADDITIONAL,
46 DNS_NUM_SECTIONS
47 };
48
49 struct dns_state {
50 u16 counts[DNS_NUM_SECTIONS];
51 byte *sections[DNS_NUM_SECTIONS+1];
52 byte *sec_ptr, *sec_end;
53
54 /* Result of dns_parse_rr(): */
55 u16 rr_type;
56 u16 rr_class;
57 u32 rr_ttl;
58 u16 rr_len;
59 byte *rr_data;
60 };
61
62 static byte *
dns_skip_name(byte * p,byte * end)63 dns_skip_name(byte *p, byte *end)
64 {
65 while (p < end)
66 {
67 unsigned int x = *p++;
68 if (!x)
69 return p;
70 switch (x & 0xc0)
71 {
72 case 0: /* Uncompressed: x = length */
73 p += x;
74 break;
75 case 0xc0: /* Indirection: 1 byte more for offset */
76 p++;
77 return (p < end) ? p : NULL;
78 default: /* RFU */
79 return NULL;
80 }
81 }
82 return NULL;
83 }
84
85 static int
dns_parse_packet(struct dns_state * s,byte * p,unsigned int plen)86 dns_parse_packet(struct dns_state *s, byte *p, unsigned int plen)
87 {
88 byte *end = p + plen;
89 unsigned int i, j, len;
90 unsigned int UNUSED x;
91
92 #if 0
93 /* Dump the packet */
94 for (i=0; i<plen; i++)
95 {
96 if (!(i%16)) printf("%04x:", i);
97 printf(" %02x", p[i]);
98 if ((i%16)==15 || i==plen-1) putchar('\n');
99 }
100 #endif
101
102 GET32(x); /* ID and flags are ignored */
103 for (i=0; i<DNS_NUM_SECTIONS; i++)
104 GET16(s->counts[i]);
105 for (i=0; i<DNS_NUM_SECTIONS; i++)
106 {
107 s->sections[i] = p;
108 for (j=0; j < s->counts[i]; j++)
109 {
110 p = dns_skip_name(p, end); /* Name */
111 if (!p)
112 goto err;
113 GET32(x); /* Type and class */
114 if (i != DNS_SEC_QUESTION)
115 {
116 GET32(x); /* TTL */
117 GET16(len); /* Length of data */
118 p += len;
119 if (p > end)
120 goto err;
121 }
122 }
123 }
124 s->sections[i] = p;
125 return 0;
126
127 err:
128 return -1;
129 }
130
131 static void
dns_init_section(struct dns_state * s,int i)132 dns_init_section(struct dns_state *s, int i)
133 {
134 s->sec_ptr = s->sections[i];
135 s->sec_end = s->sections[i+1];
136 }
137
138 static int
dns_parse_rr(struct dns_state * s)139 dns_parse_rr(struct dns_state *s)
140 {
141 byte *p = s->sec_ptr;
142 byte *end = s->sec_end;
143
144 if (p == end)
145 return 0;
146 p = dns_skip_name(p, end);
147 if (!p)
148 goto err;
149 GET16(s->rr_type);
150 GET16(s->rr_class);
151 GET32(s->rr_ttl);
152 GET16(s->rr_len);
153 s->rr_data = p;
154 s->sec_ptr = p + s->rr_len;
155 return 1;
156
157 err:
158 return -1;
159 }
160
161 char
pci_id_net_lookup(struct pci_access * a,int cat,int id1,int id2,int id3,int id4)162 *pci_id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4)
163 {
164 static int resolver_inited;
165 char name[256], dnsname[256], txt[256], *domain;
166 byte answer[4096];
167 const byte *data;
168 int res, j, dlen;
169 struct dns_state ds;
170
171 domain = pci_get_param(a, "net.domain");
172 if (!domain || !domain[0])
173 return NULL;
174
175 switch (cat)
176 {
177 case ID_VENDOR:
178 sprintf(name, "%04x", id1);
179 break;
180 case ID_DEVICE:
181 sprintf(name, "%04x.%04x", id2, id1);
182 break;
183 case ID_SUBSYSTEM:
184 sprintf(name, "%04x.%04x.%04x.%04x", id4, id3, id2, id1);
185 break;
186 case ID_GEN_SUBSYSTEM:
187 sprintf(name, "%04x.%04x.s", id2, id1);
188 break;
189 case ID_CLASS:
190 sprintf(name, "%02x.c", id1);
191 break;
192 case ID_SUBCLASS:
193 sprintf(name, "%02x.%02x.c", id2, id1);
194 break;
195 case ID_PROGIF:
196 sprintf(name, "%02x.%02x.%02x.c", id3, id2, id1);
197 break;
198 default:
199 return NULL;
200 }
201 sprintf(dnsname, "%.100s.%.100s", name, domain);
202
203 a->debug("Resolving %s\n", dnsname);
204 if (!resolver_inited)
205 {
206 resolver_inited = 1;
207 res_init();
208 }
209 res = res_query(dnsname, ns_c_in, ns_t_txt, answer, sizeof(answer));
210 if (res < 0)
211 {
212 a->debug("\tfailed, h_errno=%d\n", h_errno);
213 return NULL;
214 }
215 if (dns_parse_packet(&ds, answer, res) < 0)
216 {
217 a->debug("\tMalformed DNS packet received\n");
218 return NULL;
219 }
220 dns_init_section(&ds, DNS_SEC_ANSWER);
221 while (dns_parse_rr(&ds) > 0)
222 {
223 if (ds.rr_class != ns_c_in || ds.rr_type != ns_t_txt)
224 {
225 a->debug("\tUnexpected RR in answer: class %d, type %d\n", ds.rr_class, ds.rr_type);
226 continue;
227 }
228 data = ds.rr_data;
229 dlen = ds.rr_len;
230 j = 0;
231 while (j < dlen && j+1+data[j] <= dlen)
232 {
233 memcpy(txt, &data[j+1], data[j]);
234 txt[data[j]] = 0;
235 j += 1+data[j];
236 a->debug("\t\"%s\"\n", txt);
237 if (txt[0] == 'i' && txt[1] == '=')
238 return strdup(txt+2);
239 }
240 }
241
242 return NULL;
243 }
244
245 #else
246
pci_id_net_lookup(struct pci_access * a UNUSED,int cat UNUSED,int id1 UNUSED,int id2 UNUSED,int id3 UNUSED,int id4 UNUSED)247 char *pci_id_net_lookup(struct pci_access *a UNUSED, int cat UNUSED, int id1 UNUSED, int id2 UNUSED, int id3 UNUSED, int id4 UNUSED)
248 {
249 return NULL;
250 }
251
252 #endif
253