summary refs log blame commit diff
path: root/dhcp_probe.c
blob: 03bde748d2518e591304b1894aeebaf495fb5aad (plain) (tree)

























































































































































































































































                                                                                                                                        
#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<sys/file.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include<time.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <getopt.h>

//TODO: Name DHCPPROBE'

#define BROADCAST_ADDRESS "255.255.255.255"

#define MAX_DHCP_CHADDR_LENGTH 16
#define MAX_DHCP_SNAME_LENGTH 64
#define MAX_DHCP_FILE_LENGTH 128
#define DHCP_MAGIC_LENGTH 4
#define MAX_DHCP_OPTIONS_LENGTH 312 - DHCP_MAGIC_LENGTH

typedef struct dhcp_packet
{
    uint8_t op;
    uint8_t htype;
    uint8_t hlen;
    uint8_t hops;
    uint32_t xid;
    uint16_t secs;
    uint16_t flags;
    uint32_t ciaddr;
    uint32_t yiaddr;
    uint32_t siaddr;
    uint32_t giaddr;
    uint8_t chaddr[MAX_DHCP_CHADDR_LENGTH];
    uint8_t sname[MAX_DHCP_SNAME_LENGTH];
    uint8_t file[MAX_DHCP_FILE_LENGTH];
    uint8_t magic[DHCP_MAGIC_LENGTH];
    uint8_t opt[MAX_DHCP_OPTIONS_LENGTH];
} __attribute__((__packed__)) dhcp_packet_t;

static int fill_mac_address(uint8_t* base_addr, const char* interface_name)
{
    int fd;
    struct ifreq ifr;

    if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
        exit(1);

    strcpy(ifr.ifr_name, interface_name);

    if(ioctl(fd, SIOCGIFHWADDR, &ifr) < 0)
        exit(1);

    close(fd);

    memcpy(base_addr, ifr.ifr_addr.sa_data, 6);

    return 0;
}

static int construct_dhcp_discover(dhcp_packet_t* packet, const char* iface_name)
{
    srand(time(NULL));

    packet->op = 1; //request
    packet->htype = 6; //wifi
    packet->hlen = 6; //default
    packet->hops = 0;
    packet->xid = htonl(rand()); //random
    packet->secs = htons(0);
    packet->flags = htons(0x8000); //dont know my own ip, so send back by broadcast
    packet->ciaddr = 0; //my ip address
    packet->yiaddr = 0; //my future ip address
    packet->siaddr = 0; //server address
    packet->giaddr = 0; //gateway address

    if(fill_mac_address(packet->chaddr, iface_name) < 0)
        exit(1);

    packet->magic[0]=0x63;
    packet->magic[1]=0x82;
    packet->magic[2]=0x53;
    packet->magic[3]=0x63;

    packet->opt[0]=53;
    packet->opt[1]=1;
    packet->opt[2]=1;

    packet->opt[MAX_DHCP_OPTIONS_LENGTH - 1] = 0xFF;

    return 0;
}

static int handle_dhcp_option(uint8_t option_type, const uint8_t* option_start, uint32_t option_length)
{
    //printf("Option type: %d (length: %d)\n", option_type, option_length);

    // http://networksorcery.com/enp/protocol/bootp/options.htm
    switch(option_type)
    {
        case 1: //mask
        {
            printf("Mask: %u.%u.%u.%u\n", option_start[0], option_start[1], option_start[2], option_start[3]);
            break;
        }
        case 6: //dns
        {
            printf("DNS Server: %u.%u.%u.%u\n", option_start[0], option_start[1], option_start[2], option_start[3]);
            break;
        }
        case 15: //domain name
        {
            char domain_name[option_length + 1];
            strncpy(domain_name, (const char*)option_start, option_length);
            domain_name[option_length] = '\0';
            printf("Domain name: %s\n", domain_name);
            break;
        }
        case 54: //router
        {
            printf("DHCP server ip: %u.%u.%u.%u\n", option_start[0], option_start[1], option_start[2], option_start[3]);
            break;
        }
        default: //undefined
            return 0; //-1
    }

    return 0;
}

static void print_dhcp_offer_info(const dhcp_packet_t* dhcp_offer)
{
    //uint32_t ip = ntohl(dhcp_offer->yiaddr);
    //printf("Got IP %u.%u.%u.%u\n", ip >> 24, ((ip << 8) >> 24), (ip << 16) >> 24, (ip << 24) >> 24);

    size_t i = 0;
    while(i < MAX_DHCP_OPTIONS_LENGTH && dhcp_offer->opt[i] != 0)
    {
        uint8_t option = dhcp_offer->opt[i++];
        uint32_t length = dhcp_offer->opt[i++];

        if(handle_dhcp_option(option, &dhcp_offer->opt[i], length) < 0)
            exit(1);

        i += dhcp_offer->opt[length];
    }
}

int main(int argc, char** argv)
{
    const char* iface_name = NULL;

    char c;
    while((c = getopt(argc, argv, "i:")) > 0)
    {
        switch(c)
        {
            case 'i':
                iface_name = optarg;
                break;
            default:
            usage:
                printf("Usage: ./dhcpprobe -i %%INTERFACE\n");
                exit(1);
        }
    }

    if(iface_name == NULL)
        goto usage;

    int sock_fd;

    printf("Creating socket...\n");
    if((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
        exit(1);
    else
        printf("Succesfully created socket.\n");

    printf("Setting socket options...\n");
    int broadcast_enabled = 1;
    if(setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &broadcast_enabled, sizeof(int)) < 0)
        exit(1);
    else
        printf("Succesfully set socket options.\n");

    printf("Binding... ");
    struct sockaddr_in server_send_addr = {0};
    server_send_addr.sin_family = AF_INET;
    server_send_addr.sin_addr.s_addr = inet_addr(BROADCAST_ADDRESS);
    server_send_addr.sin_port = htons(67);
    if(bind(sock_fd, (struct sockaddr*)&server_send_addr, sizeof(struct sockaddr_in)) < 0)
         exit(1);
    else
        printf("Succesfully binded.\n");

    printf("Sending dhcpdiscover...\n");
    dhcp_packet_t dhcp_discover = {0};
    construct_dhcp_discover(&dhcp_discover, iface_name);

    if(sendto(sock_fd, &dhcp_discover, sizeof(dhcp_packet_t), 0, (struct sockaddr*)&server_send_addr, sizeof(struct sockaddr_in)) == -1)
        exit(1);
    else
        printf("Succesfully sent dhcpdiscover.\n");

    close(sock_fd);

    if((sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
            exit(1);
    else
        printf("Opened socket to recieve response.\n");

    if(setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &broadcast_enabled, sizeof(int)) < 0)
        exit(1);
    else
        printf("Succesfully set socket options.\n");

    struct sockaddr_in server_recv_addr = {0};
    server_recv_addr.sin_family = AF_INET;
    server_recv_addr.sin_port = htons(68);
    server_recv_addr.sin_addr.s_addr = INADDR_ANY;

    if(bind(sock_fd, (struct sockaddr*)&server_recv_addr, sizeof(struct sockaddr_in)) < 0)
         exit(1);
    else
        printf("Succesfully binded.\n");

    dhcp_packet_t dhcp_recv = {0};
    recieve:
    if(recvfrom(sock_fd, &dhcp_recv, sizeof(dhcp_packet_t), 0, NULL, 0) < 0)
        exit(1);
    else if(dhcp_discover.xid != dhcp_recv.xid)
    {
            printf("Recieved invalid dhcp offer.\n");
            goto recieve;
    }
    else
        printf("Succesfully recieved dhcp offer.\n");

    print_dhcp_offer_info(&dhcp_recv);
}