• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
option_find1(unsigned char * p,unsigned char * end,int opt,int minsize)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 
option_find(struct dhcp_packet * mess,size_t size,int opt_type,int minsize)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 
option_uint(unsigned char * opt,int size)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 
main(int argc,char ** argv)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