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