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