1
/* Copyright (c) 2007 Simon Kelley
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.
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.
13
/* dhcp_lease_time <address> */
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.
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.
23
Requires dnsmasq 2.40 or later.
26
#include <sys/types.h>
27
#include <netinet/in.h>
29
#include <arpa/inet.h>
30
#include <sys/socket.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>
42
#define DHCP_CHADDR_MAX 16
44
#define DHCP_COOKIE 0x63825363
46
#define OPTION_LEASE_TIME 51
47
#define OPTION_OVERLOAD 52
48
#define OPTION_MESSAGE_TYPE 53
49
#define OPTION_END 255
51
#define DHCP_SERVER_PORT 67
53
#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
54
#define option_ptr(opt) ((void *)&(((unsigned char *)(opt))[2]))
57
typedef unsigned char u8;
58
typedef unsigned short u16;
59
typedef unsigned int u32;
62
u8 op, htype, hlen, hops;
65
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
66
u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
68
unsigned char options[308];
71
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
73
while (*p != OPTION_END)
76
return NULL; /* malformed packet */
77
else if (*p == OPTION_PAD)
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)
93
return opt == OPTION_END ? p : NULL;
96
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
98
unsigned char *ret, *overload;
100
/* skip over DHCP cookie; */
101
if ((ret = option_find1(&mess->options[0], ((unsigned char *)mess) + size, opt_type, minsize)))
104
/* look for overload option. */
105
if (!(overload = option_find1(&mess->options[0], ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
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)))
113
/* finally try sname area */
114
if ((overload[2] & 2) &&
115
(ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
121
static unsigned int option_uint(unsigned char *opt, int size)
123
/* this worries about unaligned data and byte order */
124
unsigned int ret = 0;
126
unsigned char *p = option_ptr(opt);
128
for (i = 0; i < size; i++)
129
ret = (ret << 8) | *p++;
134
int main(int argc, char **argv)
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);
145
fprintf(stderr, "usage: dhcp_lease_time <address>\n");
151
perror("cannot create socket");
155
lease.s_addr = inet_addr(argv[1]);
157
memset(&packet, 0, sizeof(packet));
162
packet.op = BOOTREQUEST;
163
packet.ciaddr = lease;
164
packet.cookie = htonl(DHCP_COOKIE);
166
*(p++) = OPTION_MESSAGE_TYPE;
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);
176
if (sendto(fd, &packet, sizeof(packet), 0,
177
(struct sockaddr *)&dest, sizeof(dest)) == -1)
179
perror("sendto failed");
183
alarm(3); /* noddy timeout. */
185
rc = recv(fd, &packet, sizeof(packet), 0);
187
if (rc < (ssize_t)(sizeof(packet) - sizeof(packet.options)))
189
perror("recv failed");
193
if ((p = option_find(&packet, (size_t)rc, OPTION_LEASE_TIME, 4)))
195
unsigned int t = option_uint(p, 4);
203
if ((x = (t/3600)%24))
213
return 1; /* no lease */