]> mj.ucw.cz Git - pciutils.git/blob - lib/names-net.c
Released as 3.5.4
[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 #include <stdio.h>
12
13 #include "internal.h"
14 #include "names.h"
15
16 #ifdef PCI_USE_DNS
17
18 #include <netinet/in.h>
19 #include <arpa/nameser.h>
20 #include <resolv.h>
21 #include <netdb.h>
22
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];
41   byte *sections[DNS_NUM_SECTIONS+1];
42   byte *sec_ptr, *sec_end;
43
44   /* Result of dns_parse_rr(): */
45   u16 rr_type;
46   u16 rr_class;
47   u32 rr_ttl;
48   u16 rr_len;
49   byte *rr_data;
50 };
51
52 static byte *
53 dns_skip_name(byte *p, byte *end)
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
76 dns_parse_packet(struct dns_state *s, byte *p, unsigned int plen)
77 {
78   byte *end = p + plen;
79   unsigned int i, j, len;
80   unsigned int UNUSED x;
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
151 char
152 *pci_id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4)
153 {
154   static int resolver_inited;
155   char name[256], dnsname[256], txt[256], *domain;
156   byte answer[4096];
157   const byte *data;
158   int res, j, dlen;
159   struct dns_state ds;
160
161   domain = pci_get_param(a, "net.domain");
162   if (!domain || !domain[0])
163     return NULL;
164
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     }
191   sprintf(dnsname, "%s.%s", name, domain);
192
193   a->debug("Resolving %s\n", dnsname);
194   if (!resolver_inited)
195     {
196       resolver_inited = 1;
197       res_init();
198     }
199   res = res_query(dnsname, ns_c_in, ns_t_txt, answer, sizeof(answer));
200   if (res < 0)
201     {
202       a->debug("\tfailed, h_errno=%d\n", h_errno);
203       return NULL;
204     }
205   if (dns_parse_packet(&ds, answer, res) < 0)
206     {
207       a->debug("\tMalformed DNS packet received\n");
208       return NULL;
209     }
210   dns_init_section(&ds, DNS_SEC_ANSWER);
211   while (dns_parse_rr(&ds) > 0)
212     {
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;
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];
226           a->debug("\t\"%s\"\n", txt);
227           if (txt[0] == 'i' && txt[1] == '=')
228             return strdup(txt+2);
229         }
230     }
231
232   return NULL;
233 }
234
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