blob: 91edbfaa1043ec908c116eb19a9b483e7ab4ebda [file] [log] [blame]
Simon Kelley5aabfc72007-08-29 11:24:47 +01001/* 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
Simon Kelleyaa63a212013-04-22 15:01:52 +010023 This version requires dnsmasq 2.67 or later.
Simon Kelley5aabfc72007-08-29 11:24:47 +010024*/
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
Simon Kelleyaa63a212013-04-22 15:01:52 +010049#define OPTION_REQUESTED_OPTIONS 55
Simon Kelley5aabfc72007-08-29 11:24:47 +010050#define OPTION_END 255
51#define DHCPINFORM 8
52#define DHCP_SERVER_PORT 67
53
54#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
55#define option_ptr(opt) ((void *)&(((unsigned char *)(opt))[2]))
56
57
58typedef unsigned char u8;
59typedef unsigned short u16;
60typedef unsigned int u32;
61
62struct dhcp_packet {
63 u8 op, htype, hlen, hops;
64 u32 xid;
65 u16 secs, flags;
66 struct in_addr ciaddr, yiaddr, siaddr, giaddr;
67 u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
68 u32 cookie;
69 unsigned char options[308];
70};
71
72static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
73{
74 while (*p != OPTION_END)
75 {
76 if (p >= end)
77 return NULL; /* malformed packet */
78 else if (*p == OPTION_PAD)
79 p++;
80 else
81 {
82 int opt_len;
83 if (p >= end - 2)
84 return NULL; /* malformed packet */
85 opt_len = option_len(p);
Simon Kelley225accd2019-08-14 21:52:50 +010086 if (end - p < (2 + opt_len))
Simon Kelley5aabfc72007-08-29 11:24:47 +010087 return NULL; /* malformed packet */
88 if (*p == opt && opt_len >= minsize)
89 return p;
90 p += opt_len + 2;
91 }
92 }
93
94 return opt == OPTION_END ? p : NULL;
95}
96
97static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
98{
99 unsigned char *ret, *overload;
100
101 /* skip over DHCP cookie; */
102 if ((ret = option_find1(&mess->options[0], ((unsigned char *)mess) + size, opt_type, minsize)))
103 return ret;
104
105 /* look for overload option. */
106 if (!(overload = option_find1(&mess->options[0], ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
107 return NULL;
108
109 /* Can we look in filename area ? */
110 if ((overload[2] & 1) &&
111 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
112 return ret;
113
114 /* finally try sname area */
115 if ((overload[2] & 2) &&
116 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
117 return ret;
118
119 return NULL;
120}
121
122static unsigned int option_uint(unsigned char *opt, int size)
123{
124 /* this worries about unaligned data and byte order */
125 unsigned int ret = 0;
126 int i;
127 unsigned char *p = option_ptr(opt);
128
129 for (i = 0; i < size; i++)
130 ret = (ret << 8) | *p++;
131
132 return ret;
133}
134
135int main(int argc, char **argv)
136{
137 struct in_addr lease;
138 struct dhcp_packet packet;
139 unsigned char *p = packet.options;
140 struct sockaddr_in dest;
141 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
142 ssize_t rc;
143
144 if (argc < 2)
145 {
146 fprintf(stderr, "usage: dhcp_lease_time <address>\n");
147 exit(1);
148 }
149
150 if (fd == -1)
151 {
152 perror("cannot create socket");
153 exit(1);
154 }
155
156 lease.s_addr = inet_addr(argv[1]);
157
158 memset(&packet, 0, sizeof(packet));
159
160 packet.hlen = 0;
161 packet.htype = 0;
162
163 packet.op = BOOTREQUEST;
164 packet.ciaddr = lease;
165 packet.cookie = htonl(DHCP_COOKIE);
166
167 *(p++) = OPTION_MESSAGE_TYPE;
168 *(p++) = 1;
169 *(p++) = DHCPINFORM;
170
Josh Soref730c6742017-02-06 16:14:04 +0000171 /* Explicitly request the lease time, it won't be sent otherwise:
Simon Kelleyaa63a212013-04-22 15:01:52 +0100172 this is a dnsmasq extension, not standard. */
173 *(p++) = OPTION_REQUESTED_OPTIONS;
174 *(p++) = 1;
175 *(p++) = OPTION_LEASE_TIME;
176
Simon Kelley5aabfc72007-08-29 11:24:47 +0100177 *(p++) = OPTION_END;
178
179 dest.sin_family = AF_INET;
180 dest.sin_addr.s_addr = inet_addr("127.0.0.1");
181 dest.sin_port = ntohs(DHCP_SERVER_PORT);
182
183 if (sendto(fd, &packet, sizeof(packet), 0,
184 (struct sockaddr *)&dest, sizeof(dest)) == -1)
185 {
186 perror("sendto failed");
187 exit(1);
188 }
189
190 alarm(3); /* noddy timeout. */
191
192 rc = recv(fd, &packet, sizeof(packet), 0);
193
194 if (rc < (ssize_t)(sizeof(packet) - sizeof(packet.options)))
195 {
196 perror("recv failed");
197 exit(1);
198 }
199
200 if ((p = option_find(&packet, (size_t)rc, OPTION_LEASE_TIME, 4)))
201 {
202 unsigned int t = option_uint(p, 4);
203 if (t == 0xffffffff)
204 printf("infinite");
205 else
206 {
207 unsigned int x;
208 if ((x = t/86400))
Rosen Penevcbd29e52017-06-27 22:29:51 +0100209 printf("%ud", x);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100210 if ((x = (t/3600)%24))
Rosen Penevcbd29e52017-06-27 22:29:51 +0100211 printf("%uh", x);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100212 if ((x = (t/60)%60))
Rosen Penevcbd29e52017-06-27 22:29:51 +0100213 printf("%um", x);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100214 if ((x = t%60))
Rosen Penevcbd29e52017-06-27 22:29:51 +0100215 printf("%us", x);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100216 }
217 return 0;
218 }
219
220 return 1; /* no lease */
221}