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