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