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