diff options
Diffstat (limited to 'lib/names-net.c')
-rw-r--r-- | lib/names-net.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/lib/names-net.c b/lib/names-net.c new file mode 100644 index 0000000..95a6fd2 --- /dev/null +++ b/lib/names-net.c @@ -0,0 +1,250 @@ +/* + * The PCI Library -- Resolving ID's via DNS + * + * Copyright (c) 2007--2008 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#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 <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include <netdb.h> + +/* + * 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; i<plen; i++) + { + if (!(i%16)) printf("%04x:", i); + printf(" %02x", p[i]); + if ((i%16)==15 || i==plen-1) putchar('\n'); + } +#endif + + GET32(x); /* ID and flags are ignored */ + for (i=0; i<DNS_NUM_SECTIONS; i++) + GET16(s->counts[i]); + for (i=0; i<DNS_NUM_SECTIONS; i++) + { + s->sections[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 |