diff options
Diffstat (limited to 'dhd.c')
-rw-r--r-- | dhd.c | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/dhd.c b/dhd.c new file mode 100644 index 000000000000..1a93cd11bbe4 --- /dev/null +++ b/dhd.c @@ -0,0 +1,646 @@ +/* Ultra-fast dhcp discovery + * + * $Id: dhcp_discover.c,v 2004/01/03 20:31:01 mike Exp $ + * dhd.c,v LatinSuD 2006 + * + * Compile: + * gcc -o dhd dhd.c -lnet -lpcap -lpthread + * + * Example usage: + * dhd + * dhd eth0 + * + * Example output: + * #0: + * SERVER-MAC: 00:01:38:12:34:56 + * IP: 192.168.1.1 + * CLIENT-MAC: 00:0e:35:ab:cd:ef + * IP: 192.168.1.40 + * + * >> "0 "0 + * + * Dhd injects 10 dhcp requests on interface. In this case, + * it has been received a response, numbered as "0". + * Later, response 0 was received twice more. + */ + +#define DHD_VERSION "2.1" + +/* + * Changes: + * 2.1 (2007-04-07): + * - Identify equivalent packets with different time fields + * - Add domain-suffix field + * - Minor cleanup & enhanced usage help + * + * 2.0 (2006-??-??): + * - Beautify printf + * + * libnet 1.1 + * Build a DHCP discover packet + * To view: /usr/sbin/tcpdump -vvvvven -s 4096 'port 67 or port 68' + * + * Copyright (c) 1998 - 2004 Mike D. Schiffman <mike@infonexus.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <stdio.h> +#include <libnet.h> +#include <stdio.h> +#include <pcap.h> +#include <resolv.h> +#include <unistd.h> +#include <pthread.h> +#include <string.h> + +/* Ethernet addresses are 6 bytes */ +#ifndef ETHER_ADDR_LEN +#define ETHER_ADDR_LEN 6 +#endif + +/* Ethernet header */ +struct sniff_ethernet { + u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */ + u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */ + u_short ether_type; /* IP? ARP? RARP? etc */ +}; + +/* IP header */ +struct sniff_ip { + u_char ip_vhl; /* version << 4 | header length >> 2 */ + u_char ip_tos; /* type of service */ + u_short ip_len; /* total length */ + u_short ip_id; /* identification */ + u_short ip_off; /* fragment offset field */ + u_char ip_ttl; /* time to live */ + u_char ip_p; /* protocol */ + u_short ip_sum; /* checksum */ + struct in_addr ip_src, ip_dst; /* source and dest address */ +}; + +typedef struct { + u_short uh_sport; /* source port */ + u_short uh_dport; /* destination port */ + + u_short len; + u_short chk; + + unsigned char type; + char a; + char b; + char c; + u_short d; + u_short e; +} sniff_udp; + +/* ethernet headers are always exactly 14 bytes */ +int SIZE_ETHERNET = 14; +#define IP_HL(ip) (((ip)->ip_vhl) & 0x0f) +#define IP_V(ip) (((ip)->ip_vhl) >> 4) + +const struct sniff_ethernet *ethernet; /* The ethernet header */ +struct sniff_ip *ip; /* The IP header */ +const struct sniff_tcp *tcp; /* The TCP header */ +const char *payload; /* Packet payload */ + +pthread_t sniffer, injector; + +void print_mac(unsigned char *packet, int moffset) +{ + int i; + if (moffset > 80) + exit(0); + for (i = 0; i < 6; i++) + printf("%02x%c", packet[i + moffset], (i < 5) ? ':' : '\0'); +} + +void print_ip(unsigned char *ip, int offset) +{ + int i; + + for (i = 0; i < 4; i++) { + printf("%d", ip[i + offset]); + if (i < 3) + printf("."); + } +} + +#define DNS 0 +#define MASK 1 +#define CIP 2 +#define SIP 3 +#define GW 4 + +int didCR = 1; + +#define HASH 8 +#define PCACHE 256 +int h_pack[PCACHE]; +int n_pack = 0; + +unsigned int miChecksum(u_char * packet, int len) +{ + int i; + int s1 = 0; + int s2 = 0; + u_char * ptr=packet; + + while (ptr - packet < len - 1) { + if (ptr - packet + ptr[1] + 2 > len) + break; + + // ignore time dependent fields + if (ptr[0]==2 || ptr[0]==51 || ptr[0]==58 || ptr[0]==59) { + ptr += 2 + ptr[1]; + continue; + } + + for (i = 0; i < ptr[1]+2; i++) { + s1 += ptr[i] * i; + s2 += ptr[i]; + } + + ptr += 2 + ptr[1]; + } + + return s1 + (s2 << 16); +} + +// Caches known packets, so they are printed only once +int cache_packet(u_char * packet, int len, int *num) +{ + int i; + int hash = miChecksum(packet, len); + + for (i = 0; i < n_pack; i++) { + if (h_pack[i] == hash) { + *num = i; + return 0; + } + } + + if (n_pack < PCACHE) { + h_pack[n_pack] = hash; + *num = n_pack; + n_pack++; + } + return -1; +} + +// Pcap callback that does all the work +void got_packet(u_char * args, const struct pcap_pkthdr *header, + const u_char * packet) +{ + sniff_udp *udp; + int i; + uint size_ip; + u_char *ptr; + int idx; + int dijoIp = 0; + + ethernet = (struct sniff_ethernet *) (packet); + ip = (struct sniff_ip *) (packet + SIZE_ETHERNET); + size_ip = (IP_HL(ip)) * 4; + + udp = (sniff_udp *) (packet + SIZE_ETHERNET + size_ip); + + if (*(((char *) udp) + 8) != 2) + return; + + ptr = ((unsigned char *) udp) + 0xf8; + + if (cache_packet(ptr, header->len - (ptr - packet), &idx) == 0) { + // packet already in cache, show only reference number + if (didCR) + printf("\n >>"); + printf(" \"%d ", idx); + didCR = 0; + return; + } else { + // start dissecting packet + puts(""); + if (!didCR) + puts(""); + printf(" #%d:\n", idx); + } + + printf("\tSERVER-MAC: \x1b[1m"); + print_mac((unsigned char *) packet, 6); + printf("\x1b[0m\n"); + + // print only known interesting fields + while (ptr - packet < header->len - 1) { + if (ptr - packet + ptr[1] + 2 > header->len) + break; + + if (ptr[0] == 6) { + printf("\tDNS: \x1b[1m"); + for (i = 0; i < (ptr[1] / 4); i++) { + if (i != 0) + printf(", "); + print_ip(ptr, 2 + 4 * i); + } + printf("\x1b[0m\n"); + + } else if (ptr[0] == 1) { + + printf("\t MASK: \x1b[1m"); + print_ip(ptr, 2); + printf("\x1b[0m\n"); + + } else if (ptr[0] == 54) { + printf("\t IP: \x1b[1m"); + print_ip(ptr, 2); + printf("\x1b[0m\n"); + printf("\tCLIENT-MAC: \x1b[1m"); + print_mac((unsigned char *) udp, 0x24); + printf("\x1b[0m\n"); + printf("\t IP: \x1b[1m"); + print_ip((unsigned char *) udp, 0x18); + printf("\x1b[0m\n"); + dijoIp = 1; + + } else if (ptr[0] == 15) { + printf("\tDOMAIN-SUFFIX: \x1b[1m"); + for (i=0; i<ptr[1]; i++) + printf("%c", (ptr[2+i]>=0x20 && ptr[2+i]<0x7F)?ptr[2+i]:' '); + printf("\x1b[0m\n"); + + } else if (ptr[0] == 3) { + printf("\tGW: \x1b[1m"); + print_ip(ptr, 2); + printf("\x1b[0m\n"); + + } else if (ptr[0] == 53) { + if (ptr[2] != 2) + break; + } + ptr += 2 + ptr[1]; + } + if (!dijoIp) { + printf("\tCLIENT-IP: \x1b[1m"); + print_ip((unsigned char *) udp, 0x18); + printf("\x1b[0m\n"); + } + + didCR = 1; +} + + +void usage(char *prog) +{ + fprintf(stderr, "DHD " DHD_VERSION " - A fast DHcp Discover tool.\nUsage: dhd [INTERFACE]\n\tIf no INTERFACE is given, it will write on all available interfaces and sniff on any.\n"); + exit(1); +} + +typedef struct { + char *intf; + libnet_t *l; + pcap_t *pcap; + struct libnet_ether_addr *ethaddr; +} context; + +struct libnet_stats ls; +u_char *options; + +void init_libnet(context * c) +{ + char errbuf[LIBNET_ERRBUF_SIZE]; + int i; + + c->l = libnet_init(LIBNET_LINK, // injection type + c->intf, // network interface + errbuf); // errbuf + if (!c->l) { + fprintf(stderr, "libnet_init: %s", errbuf); + exit(EXIT_FAILURE); + } + + if ((c->ethaddr = libnet_get_hwaddr(c->l)) == NULL) { + fprintf(stderr, "libnet_get_hwaddr: %s\n", libnet_geterror(c->l)); + exit(EXIT_FAILURE); + } + for (i = 0; i < 6; i++) { + fprintf(stderr, "%02x", c->ethaddr->ether_addr_octet[i]); + if (i != 5) { + fprintf(stderr, ":"); + } + } + +} + + + +void init_packet(context * c) +{ + + u_long options_len, orig_len; + int i; + + libnet_ptag_t t; + libnet_ptag_t ip; + libnet_ptag_t udp; + libnet_ptag_t dhcp; + + + u_char options_req[] = + { LIBNET_DHCP_SUBNETMASK, LIBNET_DHCP_BROADCASTADDR, + LIBNET_DHCP_TIMEOFFSET, LIBNET_DHCP_ROUTER, LIBNET_DHCP_DOMAINNAME, + LIBNET_DHCP_DNS, + LIBNET_DHCP_HOSTNAME + }; + u_char enet_dst[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + u_char *tmp; + + // build options packet + i = 0; + options_len = 3; // update total payload size + + // we are a discover packet + options = malloc(3); + options[i++] = LIBNET_DHCP_MESSAGETYPE; // type + options[i++] = 1; // len + options[i++] = LIBNET_DHCP_MSGDISCOVER; // data + + orig_len = options_len; + options_len += sizeof(options_req) + 2; // update total payload size + + // workaround for realloc on old machines + // options = realloc(options, options_len); // resize options buffer + tmp = malloc(options_len); + memcpy(tmp, options, orig_len); + free(options); + options = tmp; + + // we are going to request some parameters + options[i++] = LIBNET_DHCP_PARAMREQUEST; // type + options[i++] = sizeof(options_req); // len + memcpy(options + i, options_req, sizeof(options_req)); // data + i += sizeof(options_req); + + + // end our options packet + // workaround for realloc on old machines + // options = realloc(options, options_len); // resize options buffer + orig_len = options_len; + options_len += 1; + tmp = malloc(options_len); + memcpy(tmp, options, orig_len); + free(options); + options = tmp; + options[i++] = LIBNET_DHCP_END; + + + // make sure we are at least the minimum length, if not fill + // this could go in libnet, but we will leave it in the app for now + if (options_len + LIBNET_DHCPV4_H < LIBNET_BOOTP_MIN_LEN) { + orig_len = options_len; + options_len = LIBNET_BOOTP_MIN_LEN - LIBNET_DHCPV4_H; + + // workaround for realloc on old machines + // options = realloc(options, options_len); + tmp = malloc(options_len); + memcpy(tmp, options, orig_len); + free(options); + options = tmp; + + memset(options + i, 0, options_len - i); + } + // the goodies are here + dhcp = libnet_build_dhcpv4(LIBNET_DHCP_REQUEST, // opcode + 1, // hardware type + 6, // hardware address length + 0, // hop count + 0xdeadbeee, // transaction id + 0, // seconds since bootstrap + 0x8000, // flags + 0, // client ip + 0, // your ip + 0, // server ip + 0, // gateway ip + c->ethaddr->ether_addr_octet, // client hardware addr + //unamac, // client hardware addr + NULL, // server host name + NULL, // boot file + options, // dhcp options stuck in payload since it is dynamic + options_len, // length of options + c->l, // libnet handle + 0); // libnet id + + // wrap it + udp = libnet_build_udp(68, // source port + 67, // destination port + LIBNET_UDP_H + LIBNET_DHCPV4_H + options_len, // packet size + 0, // checksum + NULL, // payload + 0, // payload size + c->l, // libnet handle + 0); // libnet id + + // hook me up with some ipv4 + ip = libnet_build_ipv4(LIBNET_IPV4_H + LIBNET_UDP_H + LIBNET_DHCPV4_H + options_len, // length + 0x10, // TOS + 0, // IP ID + 0, // IP Frag + 16, // TTL + IPPROTO_UDP, // protocol + 0, // checksum + inet_addr("0.0.0.0"), // src ip + inet_addr("255.255.255.255"), // destination ip + NULL, // payload + 0, // payload size + c->l, // libnet handle + 0); // libnet id + + // we can just autobuild since we arent doing anything tricky + t = libnet_autobuild_ethernet(enet_dst, // ethernet destination + ETHERTYPE_IP, // protocol type + c->l); // libnet handle +} + +int init_pcap(char *intf) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + pcap_t *pcap; + struct bpf_program fp; /* The compiled filter expression */ + char filter_exp[] = "udp and (port 67 or port 68)"; /* The filter expression */ + bpf_u_int32 net = 0; /* The IP of our sniffing device */ + setbuf(stdout, 0); + + //pcap = pcap_open_offline("sample.cap", errbuf); + pcap = pcap_open_live(intf, BUFSIZ, 0, 1000, errbuf); + if (pcap == NULL) { + fprintf(stderr, "Couldn't open device %s: %s\n", "any", errbuf); + return (2); + } + + if (pcap_compile(pcap, &fp, filter_exp, 0, net) == -1) { + fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, + pcap_geterr(pcap)); + return (2); + } + if (pcap_setfilter(pcap, &fp) == -1) { + fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, + pcap_geterr(pcap)); + return (2); + } + if (pcap_datalink(pcap) == DLT_LINUX_SLL) + SIZE_ETHERNET = 16; + //if (pcap_datalink (pcap) == DLT_EN10MB); + + pcap_loop(pcap, -1, got_packet, (unsigned char *) errbuf); + + return (0); +} + + +void inyecta(context * c) +{ + // write to the wire + if (libnet_write(c->l) == -1) { + fprintf(stderr, " dhd: libnet_write: %s\n", strerror(errno)); + } +} + +void end_libnet(context * c) +{ + libnet_destroy(c->l); + + // free mem + free(options); + + exit(0); +} + +int get_datalink(char *intf) +{ + int res; + pcap_t *handle; + char buf[8192]; + handle = pcap_open_live(intf, 0, 0, 0, buf); + if (!handle) { + //printf("NOP SE PUDO ABRIR [%s] : %s\n", intf, buf); + return 0; + } + res = pcap_datalink(handle); + //printf(" :%s: ", pcap_datalink_val_to_description(res)); + pcap_close(handle); + return res; +} + +context ifaces[16]; +int nIfaces = 0; + +void init_iface(char * iface) { + if (get_datalink(iface) == DLT_EN10MB) { + ifaces[nIfaces].intf = strdup(iface); + fprintf(stderr, "Injecting on %s (", + ifaces[nIfaces].intf); + init_libnet(&ifaces[nIfaces]); + fprintf(stderr, ")\n"); + init_packet(&ifaces[nIfaces]); + nIfaces++; + } +} + +void init_all_ifaces() +{ + FILE *f; + char *i1, *i2; + char buf[1024]; + f = fopen("/proc/net/dev", "r"); + if (!f) + return; + + while (fgets(buf, 1023, f) && nIfaces<16) { + i1 = buf; + while (*i1 == ' ') + i1++; + + i2 = i1; + while (*i2 != '\0') { + if (*i2 == ':') { + *i2 = '\0'; + if (strcmp(i1, "lo")) { + init_iface(i1); + } + } + i2++; + } + } +} + +void *esnifa(void *data) +{ + fprintf(stderr, "Sniffing on %s\n", (char *) data); + init_pcap((char *) data); + + return NULL; +} + +int main(int argc, char *argv[]) +{ + int i, j, dormir; + + if (getuid() != 0) { + fprintf(stderr, "Need to be root!\n"); + return -1; + } + + setbuf(stdout, 0); + dormir = 100; + + // init + if (argc == 1) { + pthread_create(&sniffer, NULL, esnifa, "any"); + init_all_ifaces(); + } else if (argc == 2) { + if (!strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")) + usage(argv[0]); + pthread_create(&sniffer, NULL, esnifa, (void *) argv[1]); + init_iface(argv[1]); + } else { + fprintf(stderr, "Only 1 interface can be specified!\n"); + usage(argv[0]); + } + + // inject all packets on all ifaces + for (i = 0; i < 10; i++) { + usleep(dormir * 1000); + for (j = 0; j < nIfaces; j++) { + inyecta(&ifaces[j]); + } + dormir = dormir + 100; + } + + usleep(250000); + if (!didCR) + puts(""); + + for (j = 0; j < nIfaces; j++) + end_libnet(&ifaces[j]); + + return 0; +} |