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