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 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 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 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 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