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: This version requires dnsmasq 2.67 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_REQUESTED_OPTIONS 55
50: #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:
58: typedef unsigned char u8;
59: typedef unsigned short u16;
60: typedef unsigned int u32;
61:
62: struct 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:
72: static 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);
86: if (p >= end - (2 + opt_len))
87: 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:
97: static 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:
122: static 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:
135: int 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:
171: /* Explicity request the lease time, it won't be sent otherwise:
172: this is a dnsmasq extension, not standard. */
173: *(p++) = OPTION_REQUESTED_OPTIONS;
174: *(p++) = 1;
175: *(p++) = OPTION_LEASE_TIME;
176:
177: *(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))
209: printf("%dd", x);
210: if ((x = (t/3600)%24))
211: printf("%dh", x);
212: if ((x = (t/60)%60))
213: printf("%dm", x);
214: if ((x = t%60))
215: printf("%ds", x);
216: }
217: return 0;
218: }
219:
220: return 1; /* no lease */
221: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>