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