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