summarylogtreecommitdiffstats
path: root/dhd.c
diff options
context:
space:
mode:
Diffstat (limited to 'dhd.c')
-rw-r--r--dhd.c646
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;
+}