/* * The PCI Library -- Resolving ID's via DNS * * Copyright (c) 2007--2008 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL v2+. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include "internal.h" #include "names.h" #ifdef PCI_USE_DNS /* * Our definition of BYTE_ORDER confuses arpa/nameser_compat.h on * Solaris so we must undef it before including arpa/nameser.h. */ #ifdef PCI_OS_SUNOS #undef BYTE_ORDER #endif #include #include #include #include /* * Unfortunately, there are no portable functions for DNS RR parsing, * so we will do the bit shuffling with our own bare hands. */ #define GET16(x) do { if (p+2 > end) goto err; x = (p[0] << 8) | p[1]; p += 2; } while (0) #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) enum dns_section { DNS_SEC_QUESTION, DNS_SEC_ANSWER, DNS_SEC_AUTHORITY, DNS_SEC_ADDITIONAL, DNS_NUM_SECTIONS }; struct dns_state { u16 counts[DNS_NUM_SECTIONS]; byte *sections[DNS_NUM_SECTIONS+1]; byte *sec_ptr, *sec_end; /* Result of dns_parse_rr(): */ u16 rr_type; u16 rr_class; u32 rr_ttl; u16 rr_len; byte *rr_data; }; static byte * dns_skip_name(byte *p, byte *end) { while (p < end) { unsigned int x = *p++; if (!x) return p; switch (x & 0xc0) { case 0: /* Uncompressed: x = length */ p += x; break; case 0xc0: /* Indirection: 1 byte more for offset */ p++; return (p < end) ? p : NULL; default: /* RFU */ return NULL; } } return NULL; } static int dns_parse_packet(struct dns_state *s, byte *p, unsigned int plen) { byte *end = p + plen; unsigned int i, j, len; unsigned int UNUSED x; #if 0 /* Dump the packet */ for (i=0; icounts[i]); for (i=0; isections[i] = p; for (j=0; j < s->counts[i]; j++) { p = dns_skip_name(p, end); /* Name */ if (!p) goto err; GET32(x); /* Type and class */ if (i != DNS_SEC_QUESTION) { GET32(x); /* TTL */ GET16(len); /* Length of data */ p += len; if (p > end) goto err; } } } s->sections[i] = p; return 0; err: return -1; } static void dns_init_section(struct dns_state *s, int i) { s->sec_ptr = s->sections[i]; s->sec_end = s->sections[i+1]; } static int dns_parse_rr(struct dns_state *s) { byte *p = s->sec_ptr; byte *end = s->sec_end; if (p == end) return 0; p = dns_skip_name(p, end); if (!p) goto err; GET16(s->rr_type); GET16(s->rr_class); GET32(s->rr_ttl); GET16(s->rr_len); s->rr_data = p; s->sec_ptr = p + s->rr_len; return 1; err: return -1; } char *pci_id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4) { static int resolver_inited; char name[256], dnsname[256], txt[256], *domain; byte answer[4096]; const byte *data; int res, j, dlen; struct dns_state ds; domain = pci_get_param(a, "net.domain"); if (!domain || !domain[0]) return NULL; switch (cat) { case ID_VENDOR: sprintf(name, "%04x", id1); break; case ID_DEVICE: sprintf(name, "%04x.%04x", id2, id1); break; case ID_SUBSYSTEM: sprintf(name, "%04x.%04x.%04x.%04x", id4, id3, id2, id1); break; case ID_GEN_SUBSYSTEM: sprintf(name, "%04x.%04x.s", id2, id1); break; case ID_CLASS: sprintf(name, "%02x.c", id1); break; case ID_SUBCLASS: sprintf(name, "%02x.%02x.c", id2, id1); break; case ID_PROGIF: sprintf(name, "%02x.%02x.%02x.c", id3, id2, id1); break; default: return NULL; } sprintf(dnsname, "%.100s.%.100s", name, domain); a->debug("Resolving %s\n", dnsname); if (!resolver_inited) { resolver_inited = 1; res_init(); } res = res_query(dnsname, ns_c_in, ns_t_txt, answer, sizeof(answer)); if (res < 0) { a->debug("\tfailed, h_errno=%d\n", h_errno); return NULL; } if (dns_parse_packet(&ds, answer, res) < 0) { a->debug("\tMalformed DNS packet received\n"); return NULL; } dns_init_section(&ds, DNS_SEC_ANSWER); while (dns_parse_rr(&ds) > 0) { if (ds.rr_class != ns_c_in || ds.rr_type != ns_t_txt) { a->debug("\tUnexpected RR in answer: class %d, type %d\n", ds.rr_class, ds.rr_type); continue; } data = ds.rr_data; dlen = ds.rr_len; j = 0; while (j < dlen && j+1+data[j] <= dlen) { memcpy(txt, &data[j+1], data[j]); txt[data[j]] = 0; j += 1+data[j]; a->debug("\t\"%s\"\n", txt); if (txt[0] == 'i' && txt[1] == '=') return strdup(txt+2); } } return NULL; } #else 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) { return NULL; } #endif