diff options
Diffstat (limited to 'src/resolve.c')
-rw-r--r-- | src/resolve.c | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/src/resolve.c b/src/resolve.c new file mode 100644 index 0000000..880b678 --- /dev/null +++ b/src/resolve.c @@ -0,0 +1,516 @@ +/* +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.2 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: <nielsen@memberwebs.com> +*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <resolv.h> + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +/* The main processing functions */ +static void processFile(FILE* fIn, FILE* fOut); +static void processReverse(const char* address, FILE* fOut); +static void processNone(const char* address, FILE* fOut); +static void processAddress(const char* address, FILE* fOut); +static void processHost(const char* address, FILE* fOut); +static void processMAC(const char* address, FILE* fOut); + +static void atExit(); +static void usage(); + +/* Are we in reverse resolve mode? */ +int g_doReverse = 0; +int g_doVerbose = 0; + +/* These are globals for efficiency */ +u_char* g_packetBuf = NULL; +char* g_domainBuf = NULL; +u_char* g_routeBuf = NULL; +size_t g_routeLen = 0; + + +int main(int argc, char** argv) +{ + int useFiles = 0; + int ch; + + /* Register clean up function */ + atexit(atExit); + + /* Parse command line options here */ + while((ch = getopt(argc, argv, "rfv")) != -1) + { + switch(ch) + { + /* Treat arguments as files */ + case 'f': + useFiles = 1; + break; + + case 'v': + g_doVerbose = 1; + break; + + /* Do reverse resolve */ + case 'r': + g_doReverse = 1; + break; + + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if(argc) + { + while(argc > 0) + { + /* Process the argument as a file full of addresses */ + if(useFiles) + { + FILE* file = fopen(argv[0], "r"); + if(file == NULL) + err(1, "can't open file: %s", argv[0]); + + processFile(file, stdout); + + fclose(file); + } + + /* Process each argument as an address */ + else + { + processAddress(argv[0], stdout); + } + + argc--; + argv++; + } + } + else + { + /* No args, just process stdin etc... */ + processFile(stdin, stdout); + } + + exit(0); +} + + +/** + * Eats comments and spaces + */ +static void eatSpace(FILE* file) +{ + char ch; + + do + { + ch = getc(file); + + if(ch == '#') + { + while(ch != '\n') + ch = getc(file); + } + } + while(isspace(ch)); + + ungetc(ch, file); +} + + +/** + * Read addresses from a file and process + */ +static void processFile(FILE* fIn, FILE* fOut) +{ + /* Allocate memory, cache for efficiency */ + if(g_domainBuf == NULL) + { + g_domainBuf = (char*)malloc(sizeof(char) * MAXDNAME); + if(g_domainBuf == NULL) + errx(1, "out of memory."); + } + + + /* And process rest of file */ + eatSpace(fIn); + + while(!feof(fIn) && !ferror(fIn)) + { + char ch; + int i = 0; + + while(1) + { + ch = fgetc(fIn); + + if(isspace(ch)) + break; + + if(i < MAXDNAME) + g_domainBuf[i] = ch; + + i++; + } + + g_domainBuf[i] = 0; + + processAddress(g_domainBuf, fOut); + + eatSpace(fIn); + } + + /* TODO: Should this be a warning or quit? */ + if(ferror(fIn)) + err(1, "error reading file"); +} + + +/** + * Main address processing function + */ +static void processAddress(const char* address, FILE* fOut) +{ + if(g_doVerbose) + fprintf(stderr, "resolve: resolving %s\n", address); + + /* If it's an IP then .... */ + if(strlen(address) == strspn(address, "0123456789.")) + { + if(g_doReverse) + processReverse(address, fOut); + + else + processNone(address, fOut); + } + + /* If it's a MAC address then .... */ + else if(strlen(address) == strspn(address, "0123456789abcdefABCDEF-:")) + { + if(g_doReverse) + processNone(address, fOut); + else + processMAC(address, fOut); + } + + /* If it's a net block then .... */ + if(strlen(address) == strspn(address, "0123456789./")) + { + if(g_doReverse) + warnx("can't reverse resolve net block: %s", address); + else + processNone(address, fOut); + } + + /* Otherwise it should be a domain name. try to resolve it. */ + else + { + if(g_doReverse) + processNone(address, fOut); + else + processHost(address, fOut); + } +} + + +/** + * Used if address is aleady (hopefully) in correct form + */ +static void processNone(const char* address, FILE* fOut) +{ + fprintf(fOut, address); + fputc('\n', fOut); +} + + +/** + * Forward resolve a host name into an IP + */ +static void processHost(const char* address, FILE* fOut) +{ + int ret; + ns_msg handle; + int rrnum; + ns_rr rr; + + /* Allocation cached for efficiency */ + if(g_packetBuf == NULL) + { + g_packetBuf = (u_char*)malloc(sizeof(u_char) * PACKETSZ); + if(g_packetBuf == NULL) + errx(1, "out of memory."); + } + + /* Do a DNS lookup */ + ret = res_search(address, C_IN, ns_t_a, g_packetBuf, PACKETSZ); + if(ret == -1) + warnx("couldn't resolve: %s\n", address); + else + { + /* Initialize a handle to this response. */ + if(ns_initparse(g_packetBuf, ret, &handle) < 0) + warn("couldn't parse dns response: %s\n", strerror(errno)); + else + { + /* Parse the packet */ + for(rrnum = 0; rrnum < ns_msg_count(handle, ns_s_an); rrnum++) + { + if(!ns_parserr(&handle, ns_s_an, rrnum, &rr)) + { + if(ns_rr_type(rr) == ns_t_a && /* It's a host adress */ + ns_rr_rdlen(rr) == 0x04) /* And has an IP */ + { + struct in_addr addr; + memcpy(&(addr.s_addr), ns_rr_rdata(rr), 0x04); + fprintf(fOut, inet_ntoa(addr)); + fputc('\n', fOut); + } + } + } + } + } +} + + +/** + * Reverse resolve an IP into a host name + */ +static void processReverse(const char* address, FILE* fOut) +{ + int ret; + ns_msg handle; + int rrnum; + ns_rr rr; + u_int32_t ha; + char name[NS_MAXDNAME]; + struct in_addr addr; + + /* Is it a valid IP address first of all */ + if(!inet_aton(address, &addr)) + { + warnx("invalid ip address: %s\n", address); + return; + } + + /* Format query */ + ha = ntohl(addr.s_addr); + + sprintf(name, "%u.%u.%u.%u.IN-ADDR.ARPA.", + (ha) & 0xff, + (ha >> 8) & 0xff, + (ha >> 16) & 0xff, + (ha >> 24) & 0xff); + + /* Allocation cached for efficiency */ + if(g_packetBuf == NULL) + { + g_packetBuf = (u_char*)malloc(sizeof(u_char) * PACKETSZ); + if(g_packetBuf == NULL) + errx("out of memory."); + } + + /* Do that lookup */ + ret = res_search(name, C_IN, ns_t_ptr, g_packetBuf, PACKETSZ); + if(ret == -1) + warnx("couldn't resolve: %s\n", name); + else + { + /* Initialize a handle to this response. */ + if(ns_initparse(g_packetBuf, ret, &handle) < 0) + warn("couldn't parse dns response"); + else + { + /* Parse the packet */ + for(rrnum = 0; rrnum < ns_msg_count(handle, ns_s_an); rrnum++) + { + if(!ns_parserr(&handle, ns_s_an, rrnum, &rr)) + { + if(ns_rr_type(rr) == ns_t_ptr) /* It's a domain pointer */ + { + /* Expand the host's name */ + if (ns_name_uncompress( + ns_msg_base(handle),/* Start of the packet */ + ns_msg_end(handle), /* End of the packet */ + ns_rr_rdata(rr), /* Position in the packet*/ + name, /* Result */ + MAXDNAME) /* Size of nsList buffer */ + < 0) /* Negative: error */ + { + warn("couldn't parse dns response"); + } + else + { + fprintf(fOut, "%s\n", name); + } + } + } + } + } + } +} + + +/** + * Parse components of a MAC address + */ +static int parseMAC(const char* address, u_char* mac) +{ + int i; + + for(i = 0; i < 6; i++) + { + char* end; + int part; + + part = strtol(address, &end, 16); + + if(i != 5 && *end != ':' && *end != '-') + return 0; + + if(part < 0 || part > 255) + return 0; + + mac[i] = (u_char)part; + + address = end + 1; + } + + return 1; +} + + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +/** + * Reverse resolve MAC address if in the ARP table + */ +static void processMAC(const char* address, FILE* fOut) +{ + u_char* next; + struct rt_msghdr* rtm; + struct sockaddr_inarp* sin; + struct sockaddr_dl* sdl; + u_char mac[6]; + + /* Load the kernel routing table */ + if(g_routeBuf == NULL) + { + int mib[6]; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_FLAGS; + mib[5] = RTF_LLINFO; + + if(sysctl(mib, 6, NULL, &g_routeLen, NULL, 0) < 0) + errx(1, "can't load routing table"); + + g_routeBuf = malloc(g_routeLen); + + if(g_routeBuf == NULL) + errx(1, "out of memory"); + + if(sysctl(mib, 6, g_routeBuf, &g_routeLen, NULL, 0) < 0) + errx(1, "can't load routing table"); + } + + /* Get MAC bytes */ + if(!parseMAC(address, mac)) + { + warn("invalid MAC address: %s", address); + return; + } + + /* Look for it in the routing table */ + for(next = g_routeBuf; next < (g_routeBuf + g_routeLen); next += rtm->rtm_msglen) + { + rtm = (struct rt_msghdr*)next; + sin = (struct sockaddr_inarp*)(rtm + 1); + (char*)sdl = (char*)sin + ROUNDUP(sin->sin_len); + + if(!memcmp(LLADDR(sdl), mac, sizeof(mac))) + { + fprintf(fOut, inet_ntoa(sin->sin_addr)); + fputc('\n', fOut); + return; + } + } + + /* oops */ + warnx("unlisted MAC address: %s", address); +} + + + +static void usage() +{ + fprintf(stderr, "usage: resolve [-rv] -f file ...\n"); + fprintf(stderr, " resolve [-rv] address ...\n"); + exit(2); +} + + +/* + * Clean up function. + */ +static void atExit() +{ + if(g_packetBuf != NULL) + free(g_packetBuf); + if(g_domainBuf != NULL) + free(g_domainBuf); +} + + |