1ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat/* Copyright (c) 2007 Simon Kelley 2ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 3ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat This program is free software; you can redistribute it and/or modify 4ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat it under the terms of the GNU General Public License as published by 5ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat the Free Software Foundation; version 2 dated June, 1991. 6ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 7ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat This program is distributed in the hope that it will be useful, 8ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat but WITHOUT ANY WARRANTY; without even the implied warranty of 9ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat GNU General Public License for more details. 11ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat*/ 12ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 13ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat/* dhcp_lease_time <address> */ 14ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 15ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat/* Send a DHCPINFORM message to a dnsmasq server running on the local host 16ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat and print (to stdout) the time remaining in any lease for the given 17ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat address. The time is given as string printed to stdout. 18ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 19ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat If an error occurs or no lease exists for the given address, 20ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat nothing is sent to stdout a message is sent to stderr and a 21ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat non-zero error code is returned. 22ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 23ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat Requires dnsmasq 2.40 or later. 24ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat*/ 25ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 26ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <sys/types.h> 27ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <netinet/in.h> 28ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <net/if.h> 29ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <arpa/inet.h> 30ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <sys/socket.h> 31ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <unistd.h> 32ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <stdio.h> 33ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <string.h> 34ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <stdlib.h> 35ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <net/if_arp.h> 36ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <sys/ioctl.h> 37ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <linux/types.h> 38ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <linux/netlink.h> 39ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <linux/rtnetlink.h> 40ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#include <errno.h> 41ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 42ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define DHCP_CHADDR_MAX 16 43ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define BOOTREQUEST 1 44ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define DHCP_COOKIE 0x63825363 45ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define OPTION_PAD 0 46ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define OPTION_LEASE_TIME 51 47ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define OPTION_OVERLOAD 52 48ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define OPTION_MESSAGE_TYPE 53 49ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define OPTION_END 255 50ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define DHCPINFORM 8 51ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define DHCP_SERVER_PORT 67 52ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 53ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define option_len(opt) ((int)(((unsigned char *)(opt))[1])) 54ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat#define option_ptr(opt) ((void *)&(((unsigned char *)(opt))[2])) 55ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 56ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 57ffd68729961f7383f2e35494a03ccdef20f86c98San Mehattypedef unsigned char u8; 58ffd68729961f7383f2e35494a03ccdef20f86c98San Mehattypedef unsigned short u16; 59ffd68729961f7383f2e35494a03ccdef20f86c98San Mehattypedef unsigned int u32; 60ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 61ffd68729961f7383f2e35494a03ccdef20f86c98San Mehatstruct dhcp_packet { 62ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat u8 op, htype, hlen, hops; 63ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat u32 xid; 64ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat u16 secs, flags; 65ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat struct in_addr ciaddr, yiaddr, siaddr, giaddr; 66ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128]; 67ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat u32 cookie; 68ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat unsigned char options[308]; 69ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat}; 70ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 71ffd68729961f7383f2e35494a03ccdef20f86c98San Mehatstatic unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize) 72ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat{ 73ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat while (*p != OPTION_END) 74ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat { 75ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if (p >= end) 76ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return NULL; /* malformed packet */ 77ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat else if (*p == OPTION_PAD) 78ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat p++; 79ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat else 80ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat { 81ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat int opt_len; 82ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if (p >= end - 2) 83ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return NULL; /* malformed packet */ 84ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat opt_len = option_len(p); 85ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if (p >= end - (2 + opt_len)) 86ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return NULL; /* malformed packet */ 87ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if (*p == opt && opt_len >= minsize) 88ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return p; 89ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat p += opt_len + 2; 90ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat } 91ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat } 92ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 93ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return opt == OPTION_END ? p : NULL; 94ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat} 95ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 96ffd68729961f7383f2e35494a03ccdef20f86c98San Mehatstatic unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize) 97ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat{ 98ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat unsigned char *ret, *overload; 99ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 100ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat /* skip over DHCP cookie; */ 101ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if ((ret = option_find1(&mess->options[0], ((unsigned char *)mess) + size, opt_type, minsize))) 102ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return ret; 103ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 104ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat /* look for overload option. */ 105ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if (!(overload = option_find1(&mess->options[0], ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1))) 106ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return NULL; 107ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 108ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat /* Can we look in filename area ? */ 109ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if ((overload[2] & 1) && 110ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize))) 111ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return ret; 112ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 113ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat /* finally try sname area */ 114ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if ((overload[2] & 2) && 115ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize))) 116ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return ret; 117ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 118ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return NULL; 119ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat} 120ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 121ffd68729961f7383f2e35494a03ccdef20f86c98San Mehatstatic unsigned int option_uint(unsigned char *opt, int size) 122ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat{ 123ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat /* this worries about unaligned data and byte order */ 124ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat unsigned int ret = 0; 125ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat int i; 126ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat unsigned char *p = option_ptr(opt); 127ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 128ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat for (i = 0; i < size; i++) 129ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat ret = (ret << 8) | *p++; 130ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 131ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return ret; 132ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat} 133ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 134ffd68729961f7383f2e35494a03ccdef20f86c98San Mehatint main(int argc, char **argv) 135ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat{ 136ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat struct in_addr lease; 137ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat struct dhcp_packet packet; 138ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat unsigned char *p = packet.options; 139ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat struct sockaddr_in dest; 140ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 141ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat ssize_t rc; 142ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 143ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if (argc < 2) 144ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat { 145ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat fprintf(stderr, "usage: dhcp_lease_time <address>\n"); 146ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat exit(1); 147ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat } 148ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 149ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if (fd == -1) 150ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat { 151ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat perror("cannot create socket"); 152ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat exit(1); 153ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat } 154ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 155ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat lease.s_addr = inet_addr(argv[1]); 156ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 157ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat memset(&packet, 0, sizeof(packet)); 158ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 159ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat packet.hlen = 0; 160ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat packet.htype = 0; 161ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 162ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat packet.op = BOOTREQUEST; 163ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat packet.ciaddr = lease; 164ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat packet.cookie = htonl(DHCP_COOKIE); 165ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 166ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat *(p++) = OPTION_MESSAGE_TYPE; 167ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat *(p++) = 1; 168ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat *(p++) = DHCPINFORM; 169ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 170ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat *(p++) = OPTION_END; 171ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 172ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat dest.sin_family = AF_INET; 173ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat dest.sin_addr.s_addr = inet_addr("127.0.0.1"); 174ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat dest.sin_port = ntohs(DHCP_SERVER_PORT); 175ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 176ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if (sendto(fd, &packet, sizeof(packet), 0, 177ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat (struct sockaddr *)&dest, sizeof(dest)) == -1) 178ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat { 179ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat perror("sendto failed"); 180ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat exit(1); 181ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat } 182ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 183ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat alarm(3); /* noddy timeout. */ 184ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 185ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat rc = recv(fd, &packet, sizeof(packet), 0); 186ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 187ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if (rc < (ssize_t)(sizeof(packet) - sizeof(packet.options))) 188ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat { 189ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat perror("recv failed"); 190ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat exit(1); 191ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat } 192ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 193ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if ((p = option_find(&packet, (size_t)rc, OPTION_LEASE_TIME, 4))) 194ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat { 195ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat unsigned int t = option_uint(p, 4); 196ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if (t == 0xffffffff) 197ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat printf("infinite"); 198ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat else 199ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat { 200ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat unsigned int x; 201ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if ((x = t/86400)) 202ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat printf("%dd", x); 203ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if ((x = (t/3600)%24)) 204ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat printf("%dh", x); 205ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if ((x = (t/60)%60)) 206ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat printf("%dm", x); 207ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat if ((x = t%60)) 208ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat printf("%ds", x); 209ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat } 210ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return 0; 211ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat } 212ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat 213ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat return 1; /* no lease */ 214ffd68729961f7383f2e35494a03ccdef20f86c98San Mehat} 215