blob: 7c8e0e750eb0bfaac3f9b176b2f2e245f15e78d3 [file] [log] [blame]
Simon Kelleycdeda282006-03-16 20:16:06 +00001/* dnsmasq is Copyright (c) 2000-2006 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
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
Simon Kelley9e4abcb2004-01-22 19:47:41 +000013#include "dnsmasq.h"
14
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010015struct iface_param {
16 struct in_addr relay, primary;
17 struct dhcp_context *current;
18 int ind;
19};
20
21static int complete_context(struct daemon *daemon, struct in_addr local, int if_index,
22 struct in_addr netmask, struct in_addr broadcast, void *vparam);
23
Simon Kelley3be34542004-09-11 19:12:13 +010024void dhcp_init(struct daemon *daemon)
Simon Kelley44a2a312004-03-10 20:04:35 +000025{
26 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
27 struct sockaddr_in saddr;
Simon Kelley7cebd202006-05-06 14:13:33 +010028 int oneopt = 1;
Simon Kelley3be34542004-09-11 19:12:13 +010029 struct dhcp_config *configs, *cp;
Simon Kelleydfa666f2004-08-02 18:27:27 +010030
Simon Kelley44a2a312004-03-10 20:04:35 +000031 if (fd == -1)
Simon Kelleyb8187c82005-11-26 21:46:27 +000032 die (_("cannot create DHCP socket : %s"), NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000033
Simon Kelley7cebd202006-05-06 14:13:33 +010034 if (!fix_fd(fd) ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010035#if defined(HAVE_LINUX_NETWORK)
Simon Kelley3be34542004-09-11 19:12:13 +010036 setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000037#elif defined(IP_RECVIF)
Simon Kelley3be34542004-09-11 19:12:13 +010038 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000039#endif
Simon Kelley3be34542004-09-11 19:12:13 +010040 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
Simon Kelleyb8187c82005-11-26 21:46:27 +000041 die(_("failed to set options on DHCP socket: %s"), NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000042
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000043 /* When bind-interfaces is set, there might be more than one dnmsasq
44 instance binding port 67. That's Ok if they serve different networks.
45 Need to set REUSEADDR to make this posible. */
46 if ((daemon->options & OPT_NOWILD) &&
47 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)) == -1)
Simon Kelleyb8187c82005-11-26 21:46:27 +000048 die(_("failed to set SO_REUSEADDR on DHCP socket: %s"), NULL);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000049
Simon Kelley44a2a312004-03-10 20:04:35 +000050 saddr.sin_family = AF_INET;
51 saddr.sin_port = htons(DHCP_SERVER_PORT);
52 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010053#ifdef HAVE_SOCKADDR_SA_LEN
54 saddr.sin_len = sizeof(struct sockaddr_in);
55#endif
56
Simon Kelley44a2a312004-03-10 20:04:35 +000057 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
Simon Kelleyb8187c82005-11-26 21:46:27 +000058 die(_("failed to bind DHCP server socket: %s"), NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000059
Simon Kelley3be34542004-09-11 19:12:13 +010060 daemon->dhcpfd = fd;
61
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010062#ifndef HAVE_LINUX_NETWORK
63 /* When we're not using capabilities, we need to do this here before
64 we drop root. Also, set buffer size small, to avoid wasting
65 kernel buffers */
Simon Kelley44a2a312004-03-10 20:04:35 +000066
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010067 if (daemon->options & OPT_NO_PING)
68 daemon->dhcp_icmp_fd = -1;
69 else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
70 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
71 die(_("cannot create ICMP raw socket: %s."), NULL);
72
73 /* Make BPF raw send socket */
74 init_bpf(daemon);
75#endif
Simon Kelley3be34542004-09-11 19:12:13 +010076
Simon Kelleydfa666f2004-08-02 18:27:27 +010077 /* If the same IP appears in more than one host config, then DISCOVER
78 for one of the hosts will get the address, but REQUEST will be NAKed,
79 since the address is reserved by the other one -> protocol loop. */
Simon Kelley3be34542004-09-11 19:12:13 +010080 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
Simon Kelleydfa666f2004-08-02 18:27:27 +010081 for (cp = configs->next; cp; cp = cp->next)
82 if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
Simon Kelleyb8187c82005-11-26 21:46:27 +000083 die(_("duplicate IP address %s in dhcp-config directive."), inet_ntoa(cp->addr));
Simon Kelley3be34542004-09-11 19:12:13 +010084
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010085 daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet);
86 daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
87 /* These two each hold a DHCP option max size 255
Simon Kelley3be34542004-09-11 19:12:13 +010088 and get a terminating zero added */
Simon Kelley0a852542005-03-23 20:28:59 +000089 daemon->dhcp_buff = safe_malloc(256);
90 daemon->dhcp_buff2 = safe_malloc(256);
Simon Kelley3d8df262005-08-29 12:19:27 +010091 daemon->ping_results = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +000092}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010093
Simon Kelley3be34542004-09-11 19:12:13 +010094void dhcp_packet(struct daemon *daemon, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000095{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010096 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +000097 struct dhcp_context *context;
98 struct iname *tmp;
99 struct ifreq ifr;
100 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100101 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000102 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100103 struct iovec iov;
104 ssize_t sz;
Simon Kelleycdeda282006-03-16 20:16:06 +0000105 int iface_index = 0, unicast_dest = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000106 struct in_addr iface_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100107 struct iface_param parm;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000108
Simon Kelley44a2a312004-03-10 20:04:35 +0000109 union {
110 struct cmsghdr align; /* this ensures alignment */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100111#ifdef HAVE_LINUX_NETWORK
Simon Kelley44a2a312004-03-10 20:04:35 +0000112 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
113#else
114 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
115#endif
116 } control_u;
117
Simon Kelley44a2a312004-03-10 20:04:35 +0000118 msg.msg_control = control_u.control;
119 msg.msg_controllen = sizeof(control_u);
Simon Kelley44a2a312004-03-10 20:04:35 +0000120 msg.msg_name = NULL;
121 msg.msg_namelen = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100122 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000123 msg.msg_iovlen = 1;
124
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100125 do
126 {
127 msg.msg_flags = 0;
128 while ((sz = recvmsg(daemon->dhcpfd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
129 }
130 while (sz != -1 && (msg.msg_flags & MSG_TRUNC) &&
131 expand_buf(&daemon->dhcp_packet, daemon->dhcp_packet.iov_len + 100));
Simon Kelley44a2a312004-03-10 20:04:35 +0000132
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100133 /* expand_buf may have moved buffer */
134 mess = daemon->dhcp_packet.iov_base;
135 msg.msg_controllen = sizeof(control_u);
136 msg.msg_flags = 0;
137 msg.msg_name = &dest;
138 msg.msg_namelen = sizeof(dest);
139
140 while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) && errno == EINTR);
141
142 if ((msg.msg_flags & MSG_TRUNC) ||
143 sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000144 return;
145
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100146#if defined (HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000147 if (msg.msg_controllen < sizeof(struct cmsghdr))
148 return;
149 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
150 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000151 {
152 iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
153 if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
154 unicast_dest = 1;
155 }
156
Simon Kelley8a911cc2004-03-16 18:35:52 +0000157 if (!(ifr.ifr_ifindex = iface_index) ||
Simon Kelley3be34542004-09-11 19:12:13 +0100158 ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1)
Simon Kelley44a2a312004-03-10 20:04:35 +0000159 return;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000160
Simon Kelley44a2a312004-03-10 20:04:35 +0000161#elif defined(IP_RECVIF)
162 if (msg.msg_controllen < sizeof(struct cmsghdr))
163 return;
164 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
165 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
166 iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000167
Simon Kelley44a2a312004-03-10 20:04:35 +0000168 if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
169 return;
170
171#else
Simon Kelley3be34542004-09-11 19:12:13 +0100172 {
173 struct iname *name;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100174 for (name = daemon->if_names; name->isloop; name = name->next);
Simon Kelley3be34542004-09-11 19:12:13 +0100175 strcpy(ifr.ifr_name, name->name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100176 iface_index = if_nametoindex(name->name);
Simon Kelley3be34542004-09-11 19:12:13 +0100177 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000178#endif
179
Simon Kelley44a2a312004-03-10 20:04:35 +0000180 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley3be34542004-09-11 19:12:13 +0100181 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0 )
Simon Kelley44a2a312004-03-10 20:04:35 +0000182 return;
183 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
184
Simon Kelley3d8df262005-08-29 12:19:27 +0100185 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
186 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
187 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000188
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100189 if (!iface_check(daemon, AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name))
190 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000191
Simon Kelley91dccd02005-03-31 17:48:32 +0100192 /* unlinked contexts are marked by context->current == context */
Simon Kelley3be34542004-09-11 19:12:13 +0100193 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley91dccd02005-03-31 17:48:32 +0100194 context->current = context;
Simon Kelley44a2a312004-03-10 20:04:35 +0000195
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100196 parm.relay = mess->giaddr;
197 parm.primary = iface_addr;
198 parm.current = NULL;
199 parm.ind = iface_index;
Simon Kelleye17fb622006-01-14 20:33:46 +0000200
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100201 if (!iface_enumerate(daemon, &parm, complete_context, NULL))
202 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000203 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100204 iov.iov_len = dhcp_reply(daemon, parm.current, ifr.ifr_name, (size_t)sz, now, unicast_dest);
Simon Kelley7cebd202006-05-06 14:13:33 +0100205 lease_update_file(daemon, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100206 lease_update_dns(daemon);
Simon Kelley7cebd202006-05-06 14:13:33 +0100207 lease_collect(daemon);
Simon Kelley44a2a312004-03-10 20:04:35 +0000208
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100209 if (iov.iov_len == 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000210 return;
211
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100212 msg.msg_name = &dest;
213 msg.msg_namelen = sizeof(dest);
214 msg.msg_control = NULL;
215 msg.msg_controllen = 0;
216 msg.msg_iov = &iov;
217 iov.iov_base = daemon->dhcp_packet.iov_base;
218
219 /* packet buffer may have moved */
220 mess = daemon->dhcp_packet.iov_base;
221
Simon Kelley3be34542004-09-11 19:12:13 +0100222#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100223 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100224#endif
225
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100226 if (mess->giaddr.s_addr)
227 {
228 /* Send to BOOTP relay */
229 if (!dest.sin_port)
230 dest.sin_port = htons(DHCP_SERVER_PORT);
231 dest.sin_addr = mess->giaddr;
232 }
233 else if (mess->ciaddr.s_addr)
234 {
235 dest.sin_addr = mess->ciaddr;
236 if (!dest.sin_port)
237 dest.sin_port = htons(DHCP_CLIENT_PORT);
238 }
239#ifdef HAVE_LINUX_NETWORK
240 else if (ntohs(mess->flags) & 0x8000)
241 {
242 /* broadcast to 255.255.255.255 */
243 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100244 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100245 msg.msg_controllen = sizeof(control_u);
246 cmptr = CMSG_FIRSTHDR(&msg);
247 dest.sin_addr.s_addr = INADDR_BROADCAST;
248 dest.sin_port = htons(DHCP_CLIENT_PORT);
249 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
250 pkt->ipi_ifindex = iface_index;
251 pkt->ipi_spec_dst.s_addr = 0;
252 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
253 cmptr->cmsg_level = SOL_IP;
254 cmptr->cmsg_type = IP_PKTINFO;
Simon Kelley44a2a312004-03-10 20:04:35 +0000255 }
256 else
257 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100258 /* unicast to unconfigured client */
259 dest.sin_addr = mess->yiaddr;
260 dest.sin_port = htons(DHCP_CLIENT_PORT);
Simon Kelley7cebd202006-05-06 14:13:33 +0100261 if (mess->hlen != 0 && mess->hlen <= 14 && mess->htype != 0)
262 {
263 /* inject mac address direct into ARP cache.
264 struct sockaddr limits size to 14 bytes. */
265 struct arpreq req;
266 *((struct sockaddr_in *)&req.arp_pa) = dest;
267 req.arp_ha.sa_family = mess->htype;
268 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
269 strncpy(req.arp_dev, ifr.ifr_name, 16);
270 req.arp_flags = ATF_COM;
271 ioctl(daemon->dhcpfd, SIOCSARP, &req);
272 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000273 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100274#else
275 else
276 {
277 send_via_bpf(daemon, mess, iov.iov_len, iface_addr, &ifr);
278 return;
279 }
280#endif
281
282 while(sendmsg(daemon->dhcpfd, &msg, 0) == -1 && retry_send());
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000283}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100284
Simon Kelley0a852542005-03-23 20:28:59 +0000285/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100286 of each interface (and any relay address) and does the following things:
287
288 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
289 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
290 3) Fills in local (this host) and router (this host or relay) addresses.
291 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
292
Simon Kelley0a852542005-03-23 20:28:59 +0000293 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100294
295static int complete_context(struct daemon *daemon, struct in_addr local, int if_index,
296 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000297{
298 struct dhcp_context *context;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100299 struct iface_param *param = vparam;
300
301 if (if_index != param->ind)
302 return 1; /* no for us. */
Simon Kelley0a852542005-03-23 20:28:59 +0000303
304 for (context = daemon->dhcp; context; context = context->next)
305 {
306 if (!(context->flags & CONTEXT_NETMASK) &&
307 (is_same_net(local, context->start, netmask) ||
308 is_same_net(local, context->end, netmask)))
309 {
310 if (context->netmask.s_addr != netmask.s_addr &&
311 !(is_same_net(local, context->start, netmask) &&
312 is_same_net(local, context->end, netmask)))
313 {
314 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
315 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelleyb8187c82005-11-26 21:46:27 +0000316 syslog(LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelley0a852542005-03-23 20:28:59 +0000317 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
318 }
319 context->netmask = netmask;
320 }
321
322 if (context->netmask.s_addr)
323 {
324 if (is_same_net(local, context->start, context->netmask) &&
325 is_same_net(local, context->end, context->netmask))
326 {
Simon Kelley91dccd02005-03-31 17:48:32 +0100327 /* link it onto the current chain if we've not seen it before */
328 if (context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000329 {
330 context->router = local;
331 context->local = local;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100332 context->current = param->current;
333 param->current = context;
Simon Kelley0a852542005-03-23 20:28:59 +0000334 }
335
336 if (!(context->flags & CONTEXT_BRDCAST))
337 {
338 if (is_same_net(broadcast, context->start, context->netmask))
339 context->broadcast = broadcast;
340 else
341 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
342 }
343 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100344 else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000345 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100346 context->router = param->relay;
347 context->local = param->primary;
Simon Kelley0a852542005-03-23 20:28:59 +0000348 /* fill in missing broadcast addresses for relayed ranges */
349 if (!(context->flags & CONTEXT_BRDCAST))
350 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
351 }
352
353 }
354 }
355
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100356 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000357}
358
Simon Kelley59353a62004-11-21 19:34:28 +0000359struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000360{
Simon Kelley36717ee2004-09-20 19:20:58 +0100361 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100362 possible ranges. Make sure that the address isn't in use
363 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000364
Simon Kelley36717ee2004-09-20 19:20:58 +0100365 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100366 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100367
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100368 for (tmp = context; tmp; tmp = tmp->current)
369 if (taddr.s_addr == context->local.s_addr)
370 return NULL;
371
372 for (tmp = context; tmp; tmp = tmp->current)
373 {
374 start = ntohl(tmp->start.s_addr);
375 end = ntohl(tmp->end.s_addr);
376
377 if (!(tmp->flags & CONTEXT_STATIC) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100378 addr >= start &&
379 addr <= end)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100380 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100381 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000382
Simon Kelley59353a62004-11-21 19:34:28 +0000383 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000384}
Simon Kelley59353a62004-11-21 19:34:28 +0000385
386struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr)
387{
Simon Kelleye17fb622006-01-14 20:33:46 +0000388 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000389 These are chained on ->current.
390 Here we have an address, and return the actual context correponding to that
391 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000392 any dhcp-range. In that case we return a static range if possible, or failing that,
393 any context on the correct subnet. (If there's more than one, this is a dodgy
394 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000395
Simon Kelleye17fb622006-01-14 20:33:46 +0000396 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000397
Simon Kelleye17fb622006-01-14 20:33:46 +0000398 if ((tmp = address_available(context, taddr)))
Simon Kelley59353a62004-11-21 19:34:28 +0000399 return tmp;
400
401 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelleye17fb622006-01-14 20:33:46 +0000402 if (is_same_net(taddr, tmp->start, tmp->netmask) &&
403 (tmp->flags & CONTEXT_STATIC))
Simon Kelley59353a62004-11-21 19:34:28 +0000404 return tmp;
405
Simon Kelleye17fb622006-01-14 20:33:46 +0000406 for (tmp = context; tmp; tmp = tmp->current)
407 if (is_same_net(taddr, tmp->start, tmp->netmask))
408 return tmp;
409
410 return NULL;
Simon Kelley59353a62004-11-21 19:34:28 +0000411}
412
Simon Kelleydfa666f2004-08-02 18:27:27 +0100413struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
414{
415 struct dhcp_config *config;
416
417 for (config = configs; config; config = config->next)
418 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
419 return config;
420
421 return NULL;
422}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000423
Simon Kelleycdeda282006-03-16 20:16:06 +0000424/* Is every member of check matched by a member of pool?
425 If negonly, match unless there's a negative tag which matches. */
426int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000427{
428 struct dhcp_netid *tmp1;
429
Simon Kelleycdeda282006-03-16 20:16:06 +0000430 if (!check && !negonly)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000431 return 0;
432
433 for (; check; check = check->next)
434 {
435 if (check->net[0] != '#')
436 {
437 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
438 if (strcmp(check->net, tmp1->net) == 0)
439 break;
Simon Kelleycdeda282006-03-16 20:16:06 +0000440 if (!tmp1 || negonly)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000441 return 0;
442 }
443 else
444 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
445 if (strcmp((check->net)+1, tmp1->net) == 0)
446 return 0;
447 }
448 return 1;
449}
450
Simon Kelley3be34542004-09-11 19:12:13 +0100451int address_allocate(struct dhcp_context *context, struct daemon *daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000452 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100453 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000454{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100455 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000456 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000457 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000458
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100459 struct in_addr start, addr;
460 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000461 int i, pass;
462 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100463
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100464 /* hash hwaddr */
465 for (j = 0, i = 0; i < hw_len; i++)
466 j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
467
Simon Kelleycdeda282006-03-16 20:16:06 +0000468 for (pass = 0; pass <= 1; pass++)
469 for (c = context; c; c = c->current)
470 if (c->flags & CONTEXT_STATIC)
471 continue;
472 else if (!match_netid(c->filter, netids, pass))
473 continue;
474 else
475 {
476 /* pick a seed based on hwaddr then iterate until we find a free address. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000477 start.s_addr = addr.s_addr =
478 htonl(ntohl(c->start.s_addr) +
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100479 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
Simon Kelleycdeda282006-03-16 20:16:06 +0000480
481 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100482 /* eliminate addresses in use by the server. */
483 for (d = context; d; d = d->current)
484 if (addr.s_addr == d->local.s_addr)
485 break;
486
487 if (!d &&
488 !lease_find_by_addr(addr) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000489 !config_find_by_address(daemon->dhcp_conf, addr))
490 {
491 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100492 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
493 ((float)PING_WAIT)));
494
495 *addrp = addr;
496
497 if (daemon->options & OPT_NO_PING)
498 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000499
500 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100501 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000502 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100503 on us repeatedly. As a final check, if we did more
504 than 60% of the possible ping checks in the last
505 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000506 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100507 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000508 victim = r; /* old record */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100509 else if (++count == max || r->addr.s_addr == addr.s_addr)
510 return 1;
511
Simon Kelleycdeda282006-03-16 20:16:06 +0000512 if (icmp_ping(daemon, addr))
513 /* address in use: perturb address selection so that we are
514 less likely to try this address again. */
515 c->addr_epoch++;
516 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100517 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000518 /* at this point victim may hold an expired record */
519 if (!victim)
520 {
521 if ((victim = malloc(sizeof(struct ping_result))))
522 {
523 victim->next = daemon->ping_results;
524 daemon->ping_results = victim;
525 }
526 }
527
528 /* record that this address is OK for 30s
529 without more ping checks */
530 if (victim)
531 {
532 victim->addr = addr;
533 victim->time = now;
534 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100535 return 1;
536 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000537 }
Simon Kelley3be34542004-09-11 19:12:13 +0100538
Simon Kelleycdeda282006-03-16 20:16:06 +0000539 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
540
541 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
542 addr = c->start;
543
544 } while (addr.s_addr != start.s_addr);
545 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000546 return 0;
547}
548
549static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
550{
Simon Kelleyb8187c82005-11-26 21:46:27 +0000551 if (!context) /* called via find_config() from lease_update_from_configs() */
Simon Kelley0a852542005-03-23 20:28:59 +0000552 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100553 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000554 return 1;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000555 for (; context; context = context->current)
556 if (is_same_net(config->addr, context->start, context->netmask))
557 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000558
559 return 0;
560}
561
Simon Kelley0a852542005-03-23 20:28:59 +0000562
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000563struct dhcp_config *find_config(struct dhcp_config *configs,
564 struct dhcp_context *context,
565 unsigned char *clid, int clid_len,
Simon Kelleycdeda282006-03-16 20:16:06 +0000566 unsigned char *hwaddr, int hw_len,
567 int hw_type, char *hostname)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000568{
569 struct dhcp_config *config;
570
Simon Kelley0a852542005-03-23 20:28:59 +0000571 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000572 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100573 if (config->flags & CONFIG_CLID)
574 {
575 if (config->clid_len == clid_len &&
576 memcmp(config->clid, clid, clid_len) == 0 &&
577 is_addr_in_context(context, config))
578 return config;
579
580 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
581 cope with that here */
582 if (*clid == 0 && config->clid_len == clid_len-1 &&
583 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
584 is_addr_in_context(context, config))
585 return config;
586 }
587
Simon Kelley0a852542005-03-23 20:28:59 +0000588
Simon Kelleycdeda282006-03-16 20:16:06 +0000589 for (config = configs; config; config = config->next)
590 if ((config->flags & CONFIG_HWADDR) &&
591 config->wildcard_mask == 0 &&
592 config->hwaddr_len == hw_len &&
593 (config->hwaddr_type == hw_type || config->hwaddr_type == 0) &&
594 memcmp(config->hwaddr, hwaddr, hw_len) == 0 &&
595 is_addr_in_context(context, config))
596 return config;
Simon Kelley3d8df262005-08-29 12:19:27 +0100597
Simon Kelley0a852542005-03-23 20:28:59 +0000598 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000599 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100600 if ((config->flags & CONFIG_NAME) &&
601 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000602 is_addr_in_context(context, config))
603 return config;
604
Simon Kelleycdeda282006-03-16 20:16:06 +0000605 for (config = configs; config; config = config->next)
606 if ((config->flags & CONFIG_HWADDR) &&
607 config->wildcard_mask != 0 &&
608 config->hwaddr_len == hw_len &&
609 (config->hwaddr_type == hw_type || config->hwaddr_type == 0) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100610 is_addr_in_context(context, config) &&
611 memcmp_masked(config->hwaddr, hwaddr, hw_len, config->wildcard_mask))
612 return config;
613
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000614 return NULL;
615}
616
Simon Kelley3be34542004-09-11 19:12:13 +0100617void dhcp_read_ethers(struct daemon *daemon)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000618{
Simon Kelley44a2a312004-03-10 20:04:35 +0000619 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000620 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100621 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100622 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000623 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100624 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley3be34542004-09-11 19:12:13 +0100625 struct dhcp_config *config, *configs = daemon->dhcp_conf;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000626 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100627
628 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000629
630 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100631 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000632 syslog(LOG_ERR, _("failed to read %s:%m"), ETHERSFILE);
Simon Kelley3be34542004-09-11 19:12:13 +0100633 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100634 }
635
Simon Kelley44a2a312004-03-10 20:04:35 +0000636 while (fgets(buff, MAXDNAME, f))
637 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000638 lineno++;
639
Simon Kelley33820b72004-04-03 21:10:00 +0100640 while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000641 buff[strlen(buff)-1] = 0;
642
643 if ((*buff == '#') || (*buff == '+'))
644 continue;
645
Simon Kelley33820b72004-04-03 21:10:00 +0100646 for (ip = buff; *ip && !isspace(*ip); ip++);
647 for(; *ip && isspace(*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000648 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000649 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000650 {
651 syslog(LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
652 continue;
653 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000654
655 /* check for name or dotted-quad */
656 for (cp = ip; *cp; cp++)
657 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
658 break;
659
660 if (!*cp)
661 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000662 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000663 {
664 syslog(LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
665 continue;
666 }
667
Simon Kelley33820b72004-04-03 21:10:00 +0100668 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000669
670 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100671 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000672 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000673 }
674 else
675 {
Simon Kelley1cff1662004-03-12 08:12:58 +0000676 if (!canonicalise(ip))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000677 {
678 syslog(LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
679 continue;
680 }
681
Simon Kelley33820b72004-04-03 21:10:00 +0100682 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000683
684 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100685 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
Simon Kelley1cff1662004-03-12 08:12:58 +0000686 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000687 }
688
Simon Kelley1cff1662004-03-12 08:12:58 +0000689 if (!config)
690 {
Simon Kelley33820b72004-04-03 21:10:00 +0100691 for (config = configs; config; config = config->next)
692 if ((config->flags & CONFIG_HWADDR) &&
Simon Kelley91dccd02005-03-31 17:48:32 +0100693 config->wildcard_mask == 0 &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000694 config->hwaddr_len == ETHER_ADDR_LEN &&
695 (config->hwaddr_type == ARPHRD_ETHER || config->hwaddr_type == 0) &&
Simon Kelley33820b72004-04-03 21:10:00 +0100696 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
697 break;
698
699 if (!config)
700 {
701 if (!(config = malloc(sizeof(struct dhcp_config))))
702 continue;
703 config->flags = 0;
Simon Kelley91dccd02005-03-31 17:48:32 +0100704 config->wildcard_mask = 0;
Simon Kelley33820b72004-04-03 21:10:00 +0100705 config->next = configs;
706 configs = config;
707 }
708
709 config->flags |= flags;
710
711 if (flags & CONFIG_NAME)
712 {
713 if ((config->hostname = malloc(strlen(ip)+1)))
714 strcpy(config->hostname, ip);
715 else
716 config->flags &= ~CONFIG_NAME;
717 }
718
719 if (flags & CONFIG_ADDR)
720 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000721 }
Simon Kelley33820b72004-04-03 21:10:00 +0100722
Simon Kelleyde379512004-06-22 20:23:33 +0100723 config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
Simon Kelley33820b72004-04-03 21:10:00 +0100724 memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
Simon Kelleycdeda282006-03-16 20:16:06 +0000725 config->hwaddr_len = ETHER_ADDR_LEN;
726 config->hwaddr_type = ARPHRD_ETHER;
Simon Kelley33820b72004-04-03 21:10:00 +0100727 count++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000728 }
729
730 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100731
Simon Kelleyb8187c82005-11-26 21:46:27 +0000732 syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley3be34542004-09-11 19:12:13 +0100733
734 daemon->dhcp_conf = configs;
Simon Kelley44a2a312004-03-10 20:04:35 +0000735}
736
737void dhcp_update_configs(struct dhcp_config *configs)
738{
739 /* Some people like to keep all static IP addresses in /etc/hosts.
740 This goes through /etc/hosts and sets static addresses for any DHCP config
Simon Kelley3d8df262005-08-29 12:19:27 +0100741 records which don't have an address and whose name matches.
742 We take care to maintain the invariant that any IP address can appear
743 in at most one dhcp-host. */
Simon Kelley44a2a312004-03-10 20:04:35 +0000744
Simon Kelley1ab84e22004-01-29 16:48:35 +0000745 struct dhcp_config *config;
746 struct crec *crec;
Simon Kelley44a2a312004-03-10 20:04:35 +0000747
Simon Kelley1ab84e22004-01-29 16:48:35 +0000748 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100749 if (!(config->flags & CONFIG_ADDR) &&
750 (config->flags & CONFIG_NAME) &&
Simon Kelley1ab84e22004-01-29 16:48:35 +0000751 (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
752 (crec->flags & F_HOSTS))
Simon Kelley33820b72004-04-03 21:10:00 +0100753 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100754 if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000755 syslog(LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
Simon Kelley3d8df262005-08-29 12:19:27 +0100756 inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
757 else
758 {
759 config->addr = crec->addr.addr.addr.addr4;
760 config->flags |= CONFIG_ADDR;
761 }
Simon Kelley33820b72004-04-03 21:10:00 +0100762 }
Simon Kelley1ab84e22004-01-29 16:48:35 +0000763}
Simon Kelley44a2a312004-03-10 20:04:35 +0000764
Simon Kelleybb01cb92004-12-13 20:56:23 +0000765/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
766 for this address. If it has a domain part, that must match the set domain and
767 it gets stripped. */
768char *host_from_dns(struct daemon *daemon, struct in_addr addr)
769{
770 struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
771 char *hostname = NULL;
772
773 if (lookup && (lookup->flags & F_HOSTS))
774 {
775 hostname = daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000776 strncpy(hostname, cache_get_name(lookup), 256);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100777 hostname[255] = 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000778 hostname = strip_hostname(daemon, hostname);
779 }
780
781 return hostname;
782}
783
784char *strip_hostname(struct daemon *daemon, char *hostname)
785{
786 char *dot = strchr(hostname, '.');
787 if (dot)
788 {
789 if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix))
790 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000791 syslog(LOG_WARNING, _("Ignoring DHCP host name %s because it has an illegal domain part"), hostname);
Simon Kelleybb01cb92004-12-13 20:56:23 +0000792 hostname = NULL;
793 }
794 else
795 {
796 *dot = 0; /* truncate */
797 if (strlen(hostname) == 0)
798 hostname = NULL; /* nothing left */
799 }
800 }
801 return hostname;
802}