blob: 7ceb460c86df3734c6f6731f7d014bb3da9c8233 [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 Kelley849a8352006-06-09 21:02:31 +010050 memset(&saddr, 0, sizeof(saddr));
Simon Kelley44a2a312004-03-10 20:04:35 +000051 saddr.sin_family = AF_INET;
52 saddr.sin_port = htons(DHCP_SERVER_PORT);
53 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010054#ifdef HAVE_SOCKADDR_SA_LEN
55 saddr.sin_len = sizeof(struct sockaddr_in);
56#endif
57
Simon Kelley44a2a312004-03-10 20:04:35 +000058 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
Simon Kelleyb8187c82005-11-26 21:46:27 +000059 die(_("failed to bind DHCP server socket: %s"), NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000060
Simon Kelley3be34542004-09-11 19:12:13 +010061 daemon->dhcpfd = fd;
62
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010063#ifndef HAVE_LINUX_NETWORK
64 /* When we're not using capabilities, we need to do this here before
65 we drop root. Also, set buffer size small, to avoid wasting
66 kernel buffers */
Simon Kelley44a2a312004-03-10 20:04:35 +000067
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010068 if (daemon->options & OPT_NO_PING)
69 daemon->dhcp_icmp_fd = -1;
70 else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
71 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
72 die(_("cannot create ICMP raw socket: %s."), NULL);
73
74 /* Make BPF raw send socket */
75 init_bpf(daemon);
76#endif
Simon Kelley3be34542004-09-11 19:12:13 +010077
Simon Kelleydfa666f2004-08-02 18:27:27 +010078 /* If the same IP appears in more than one host config, then DISCOVER
79 for one of the hosts will get the address, but REQUEST will be NAKed,
80 since the address is reserved by the other one -> protocol loop. */
Simon Kelley3be34542004-09-11 19:12:13 +010081 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
Simon Kelleydfa666f2004-08-02 18:27:27 +010082 for (cp = configs->next; cp; cp = cp->next)
83 if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
Simon Kelleyb8187c82005-11-26 21:46:27 +000084 die(_("duplicate IP address %s in dhcp-config directive."), inet_ntoa(cp->addr));
Simon Kelley3be34542004-09-11 19:12:13 +010085
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010086 daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet);
87 daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
88 /* These two each hold a DHCP option max size 255
Simon Kelley3be34542004-09-11 19:12:13 +010089 and get a terminating zero added */
Simon Kelley0a852542005-03-23 20:28:59 +000090 daemon->dhcp_buff = safe_malloc(256);
91 daemon->dhcp_buff2 = safe_malloc(256);
Simon Kelley3d8df262005-08-29 12:19:27 +010092 daemon->ping_results = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +000093}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010094
Simon Kelley3be34542004-09-11 19:12:13 +010095void dhcp_packet(struct daemon *daemon, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000096{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010097 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +000098 struct dhcp_context *context;
99 struct iname *tmp;
100 struct ifreq ifr;
101 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100102 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000103 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100104 struct iovec iov;
105 ssize_t sz;
Simon Kelleycdeda282006-03-16 20:16:06 +0000106 int iface_index = 0, unicast_dest = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000107 struct in_addr iface_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100108 struct iface_param parm;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000109
Simon Kelley44a2a312004-03-10 20:04:35 +0000110 union {
111 struct cmsghdr align; /* this ensures alignment */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100112#ifdef HAVE_LINUX_NETWORK
Simon Kelley44a2a312004-03-10 20:04:35 +0000113 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
114#else
115 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
116#endif
117 } control_u;
118
Simon Kelley44a2a312004-03-10 20:04:35 +0000119 msg.msg_control = control_u.control;
120 msg.msg_controllen = sizeof(control_u);
Simon Kelley44a2a312004-03-10 20:04:35 +0000121 msg.msg_name = NULL;
122 msg.msg_namelen = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100123 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000124 msg.msg_iovlen = 1;
125
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100126 do
127 {
128 msg.msg_flags = 0;
129 while ((sz = recvmsg(daemon->dhcpfd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
130 }
131 while (sz != -1 && (msg.msg_flags & MSG_TRUNC) &&
132 expand_buf(&daemon->dhcp_packet, daemon->dhcp_packet.iov_len + 100));
Simon Kelley44a2a312004-03-10 20:04:35 +0000133
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100134 /* expand_buf may have moved buffer */
135 mess = daemon->dhcp_packet.iov_base;
136 msg.msg_controllen = sizeof(control_u);
137 msg.msg_flags = 0;
138 msg.msg_name = &dest;
139 msg.msg_namelen = sizeof(dest);
140
141 while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) && errno == EINTR);
142
143 if ((msg.msg_flags & MSG_TRUNC) ||
144 sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000145 return;
146
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100147#if defined (HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000148 if (msg.msg_controllen < sizeof(struct cmsghdr))
149 return;
150 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
151 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000152 {
153 iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
154 if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
155 unicast_dest = 1;
156 }
157
Simon Kelley8a911cc2004-03-16 18:35:52 +0000158 if (!(ifr.ifr_ifindex = iface_index) ||
Simon Kelley3be34542004-09-11 19:12:13 +0100159 ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1)
Simon Kelley44a2a312004-03-10 20:04:35 +0000160 return;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000161
Simon Kelley44a2a312004-03-10 20:04:35 +0000162#elif defined(IP_RECVIF)
163 if (msg.msg_controllen < sizeof(struct cmsghdr))
164 return;
165 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
166 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
167 iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000168
Simon Kelley44a2a312004-03-10 20:04:35 +0000169 if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
170 return;
171
172#else
Simon Kelley3be34542004-09-11 19:12:13 +0100173 {
174 struct iname *name;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100175 for (name = daemon->if_names; name->isloop; name = name->next);
Simon Kelley3be34542004-09-11 19:12:13 +0100176 strcpy(ifr.ifr_name, name->name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100177 iface_index = if_nametoindex(name->name);
Simon Kelley3be34542004-09-11 19:12:13 +0100178 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000179#endif
180
Simon Kelley44a2a312004-03-10 20:04:35 +0000181 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley3be34542004-09-11 19:12:13 +0100182 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0 )
Simon Kelley44a2a312004-03-10 20:04:35 +0000183 return;
184 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
185
Simon Kelley3d8df262005-08-29 12:19:27 +0100186 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
187 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
188 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000189
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100190 if (!iface_check(daemon, AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name))
191 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000192
Simon Kelley91dccd02005-03-31 17:48:32 +0100193 /* unlinked contexts are marked by context->current == context */
Simon Kelley3be34542004-09-11 19:12:13 +0100194 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley91dccd02005-03-31 17:48:32 +0100195 context->current = context;
Simon Kelley44a2a312004-03-10 20:04:35 +0000196
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100197 parm.relay = mess->giaddr;
198 parm.primary = iface_addr;
199 parm.current = NULL;
200 parm.ind = iface_index;
Simon Kelleye17fb622006-01-14 20:33:46 +0000201
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100202 if (!iface_enumerate(daemon, &parm, complete_context, NULL))
203 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000204 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100205 iov.iov_len = dhcp_reply(daemon, parm.current, ifr.ifr_name, (size_t)sz, now, unicast_dest);
Simon Kelley7cebd202006-05-06 14:13:33 +0100206 lease_update_file(daemon, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100207 lease_update_dns(daemon);
Simon Kelley7cebd202006-05-06 14:13:33 +0100208 lease_collect(daemon);
Simon Kelley44a2a312004-03-10 20:04:35 +0000209
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100210 if (iov.iov_len == 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000211 return;
212
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100213 msg.msg_name = &dest;
214 msg.msg_namelen = sizeof(dest);
215 msg.msg_control = NULL;
216 msg.msg_controllen = 0;
217 msg.msg_iov = &iov;
218 iov.iov_base = daemon->dhcp_packet.iov_base;
219
220 /* packet buffer may have moved */
221 mess = daemon->dhcp_packet.iov_base;
222
Simon Kelley3be34542004-09-11 19:12:13 +0100223#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100224 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100225#endif
226
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100227 if (mess->giaddr.s_addr)
228 {
229 /* Send to BOOTP relay */
230 if (!dest.sin_port)
231 dest.sin_port = htons(DHCP_SERVER_PORT);
232 dest.sin_addr = mess->giaddr;
233 }
234 else if (mess->ciaddr.s_addr)
235 {
236 dest.sin_addr = mess->ciaddr;
237 if (!dest.sin_port)
238 dest.sin_port = htons(DHCP_CLIENT_PORT);
239 }
240#ifdef HAVE_LINUX_NETWORK
Simon Kelley849a8352006-06-09 21:02:31 +0100241 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
242 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100243 {
Simon Kelley849a8352006-06-09 21:02:31 +0100244 /* broadcast to 255.255.255.255 (or mac address invalid) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100245 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100246 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100247 msg.msg_controllen = sizeof(control_u);
248 cmptr = CMSG_FIRSTHDR(&msg);
249 dest.sin_addr.s_addr = INADDR_BROADCAST;
250 dest.sin_port = htons(DHCP_CLIENT_PORT);
251 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
252 pkt->ipi_ifindex = iface_index;
253 pkt->ipi_spec_dst.s_addr = 0;
254 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
255 cmptr->cmsg_level = SOL_IP;
256 cmptr->cmsg_type = IP_PKTINFO;
Simon Kelley44a2a312004-03-10 20:04:35 +0000257 }
258 else
259 {
Simon Kelley849a8352006-06-09 21:02:31 +0100260 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
261 struct sockaddr limits size to 14 bytes. */
262 struct arpreq req;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100263 dest.sin_addr = mess->yiaddr;
264 dest.sin_port = htons(DHCP_CLIENT_PORT);
Simon Kelley849a8352006-06-09 21:02:31 +0100265 *((struct sockaddr_in *)&req.arp_pa) = dest;
266 req.arp_ha.sa_family = mess->htype;
267 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
268 strncpy(req.arp_dev, ifr.ifr_name, 16);
269 req.arp_flags = ATF_COM;
270 ioctl(daemon->dhcpfd, SIOCSARP, &req);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000271 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100272#else
273 else
274 {
275 send_via_bpf(daemon, mess, iov.iov_len, iface_addr, &ifr);
276 return;
277 }
278#endif
279
280 while(sendmsg(daemon->dhcpfd, &msg, 0) == -1 && retry_send());
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000281}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100282
Simon Kelley0a852542005-03-23 20:28:59 +0000283/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100284 of each interface (and any relay address) and does the following things:
285
286 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
287 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
288 3) Fills in local (this host) and router (this host or relay) addresses.
289 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
290
Simon Kelley0a852542005-03-23 20:28:59 +0000291 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100292
293static int complete_context(struct daemon *daemon, struct in_addr local, int if_index,
294 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000295{
296 struct dhcp_context *context;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100297 struct iface_param *param = vparam;
298
299 if (if_index != param->ind)
300 return 1; /* no for us. */
Simon Kelley0a852542005-03-23 20:28:59 +0000301
302 for (context = daemon->dhcp; context; context = context->next)
303 {
304 if (!(context->flags & CONTEXT_NETMASK) &&
305 (is_same_net(local, context->start, netmask) ||
306 is_same_net(local, context->end, netmask)))
307 {
308 if (context->netmask.s_addr != netmask.s_addr &&
309 !(is_same_net(local, context->start, netmask) &&
310 is_same_net(local, context->end, netmask)))
311 {
312 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
313 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelleyb8187c82005-11-26 21:46:27 +0000314 syslog(LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelley0a852542005-03-23 20:28:59 +0000315 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
316 }
317 context->netmask = netmask;
318 }
319
320 if (context->netmask.s_addr)
321 {
322 if (is_same_net(local, context->start, context->netmask) &&
323 is_same_net(local, context->end, context->netmask))
324 {
Simon Kelley91dccd02005-03-31 17:48:32 +0100325 /* link it onto the current chain if we've not seen it before */
326 if (context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000327 {
328 context->router = local;
329 context->local = local;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100330 context->current = param->current;
331 param->current = context;
Simon Kelley0a852542005-03-23 20:28:59 +0000332 }
333
334 if (!(context->flags & CONTEXT_BRDCAST))
335 {
336 if (is_same_net(broadcast, context->start, context->netmask))
337 context->broadcast = broadcast;
338 else
339 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
340 }
341 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100342 else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000343 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100344 context->router = param->relay;
345 context->local = param->primary;
Simon Kelley0a852542005-03-23 20:28:59 +0000346 /* fill in missing broadcast addresses for relayed ranges */
347 if (!(context->flags & CONTEXT_BRDCAST))
348 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
349 }
350
351 }
352 }
353
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100354 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000355}
356
Simon Kelley59353a62004-11-21 19:34:28 +0000357struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000358{
Simon Kelley36717ee2004-09-20 19:20:58 +0100359 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100360 possible ranges. Make sure that the address isn't in use
361 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000362
Simon Kelley36717ee2004-09-20 19:20:58 +0100363 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100364 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100365
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100366 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100367 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100368 return NULL;
369
370 for (tmp = context; tmp; tmp = tmp->current)
371 {
372 start = ntohl(tmp->start.s_addr);
373 end = ntohl(tmp->end.s_addr);
374
375 if (!(tmp->flags & CONTEXT_STATIC) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100376 addr >= start &&
377 addr <= end)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100378 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100379 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000380
Simon Kelley59353a62004-11-21 19:34:28 +0000381 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000382}
Simon Kelley59353a62004-11-21 19:34:28 +0000383
384struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr)
385{
Simon Kelleye17fb622006-01-14 20:33:46 +0000386 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000387 These are chained on ->current.
388 Here we have an address, and return the actual context correponding to that
389 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000390 any dhcp-range. In that case we return a static range if possible, or failing that,
391 any context on the correct subnet. (If there's more than one, this is a dodgy
392 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000393
Simon Kelleye17fb622006-01-14 20:33:46 +0000394 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000395
Simon Kelleye17fb622006-01-14 20:33:46 +0000396 if ((tmp = address_available(context, taddr)))
Simon Kelley59353a62004-11-21 19:34:28 +0000397 return tmp;
398
399 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelleye17fb622006-01-14 20:33:46 +0000400 if (is_same_net(taddr, tmp->start, tmp->netmask) &&
401 (tmp->flags & CONTEXT_STATIC))
Simon Kelley59353a62004-11-21 19:34:28 +0000402 return tmp;
403
Simon Kelleye17fb622006-01-14 20:33:46 +0000404 for (tmp = context; tmp; tmp = tmp->current)
405 if (is_same_net(taddr, tmp->start, tmp->netmask))
406 return tmp;
407
408 return NULL;
Simon Kelley59353a62004-11-21 19:34:28 +0000409}
410
Simon Kelleydfa666f2004-08-02 18:27:27 +0100411struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
412{
413 struct dhcp_config *config;
414
415 for (config = configs; config; config = config->next)
416 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
417 return config;
418
419 return NULL;
420}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000421
Simon Kelleycdeda282006-03-16 20:16:06 +0000422/* Is every member of check matched by a member of pool?
423 If negonly, match unless there's a negative tag which matches. */
424int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000425{
426 struct dhcp_netid *tmp1;
427
Simon Kelleycdeda282006-03-16 20:16:06 +0000428 if (!check && !negonly)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000429 return 0;
430
431 for (; check; check = check->next)
432 {
433 if (check->net[0] != '#')
434 {
435 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
436 if (strcmp(check->net, tmp1->net) == 0)
437 break;
Simon Kelleycdeda282006-03-16 20:16:06 +0000438 if (!tmp1 || negonly)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000439 return 0;
440 }
441 else
442 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
443 if (strcmp((check->net)+1, tmp1->net) == 0)
444 return 0;
445 }
446 return 1;
447}
448
Simon Kelley3be34542004-09-11 19:12:13 +0100449int address_allocate(struct dhcp_context *context, struct daemon *daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000450 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100451 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000452{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100453 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000454 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000455 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000456
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100457 struct in_addr start, addr;
458 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000459 int i, pass;
460 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100461
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100462 /* hash hwaddr */
463 for (j = 0, i = 0; i < hw_len; i++)
464 j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
465
Simon Kelleycdeda282006-03-16 20:16:06 +0000466 for (pass = 0; pass <= 1; pass++)
467 for (c = context; c; c = c->current)
468 if (c->flags & CONTEXT_STATIC)
469 continue;
470 else if (!match_netid(c->filter, netids, pass))
471 continue;
472 else
473 {
474 /* pick a seed based on hwaddr then iterate until we find a free address. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000475 start.s_addr = addr.s_addr =
476 htonl(ntohl(c->start.s_addr) +
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100477 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
Simon Kelleycdeda282006-03-16 20:16:06 +0000478
479 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100480 /* eliminate addresses in use by the server. */
481 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100482 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100483 break;
484
485 if (!d &&
486 !lease_find_by_addr(addr) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000487 !config_find_by_address(daemon->dhcp_conf, addr))
488 {
489 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100490 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
491 ((float)PING_WAIT)));
492
493 *addrp = addr;
494
495 if (daemon->options & OPT_NO_PING)
496 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000497
498 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100499 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000500 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100501 on us repeatedly. As a final check, if we did more
502 than 60% of the possible ping checks in the last
503 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000504 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100505 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000506 victim = r; /* old record */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100507 else if (++count == max || r->addr.s_addr == addr.s_addr)
508 return 1;
509
Simon Kelleycdeda282006-03-16 20:16:06 +0000510 if (icmp_ping(daemon, addr))
511 /* address in use: perturb address selection so that we are
512 less likely to try this address again. */
513 c->addr_epoch++;
514 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100515 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000516 /* at this point victim may hold an expired record */
517 if (!victim)
518 {
519 if ((victim = malloc(sizeof(struct ping_result))))
520 {
521 victim->next = daemon->ping_results;
522 daemon->ping_results = victim;
523 }
524 }
525
526 /* record that this address is OK for 30s
527 without more ping checks */
528 if (victim)
529 {
530 victim->addr = addr;
531 victim->time = now;
532 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100533 return 1;
534 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000535 }
Simon Kelley3be34542004-09-11 19:12:13 +0100536
Simon Kelleycdeda282006-03-16 20:16:06 +0000537 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
538
539 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
540 addr = c->start;
541
542 } while (addr.s_addr != start.s_addr);
543 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000544 return 0;
545}
546
547static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
548{
Simon Kelleyb8187c82005-11-26 21:46:27 +0000549 if (!context) /* called via find_config() from lease_update_from_configs() */
Simon Kelley0a852542005-03-23 20:28:59 +0000550 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100551 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000552 return 1;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000553 for (; context; context = context->current)
554 if (is_same_net(config->addr, context->start, context->netmask))
555 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000556
557 return 0;
558}
559
Simon Kelley0a852542005-03-23 20:28:59 +0000560
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000561struct dhcp_config *find_config(struct dhcp_config *configs,
562 struct dhcp_context *context,
563 unsigned char *clid, int clid_len,
Simon Kelleycdeda282006-03-16 20:16:06 +0000564 unsigned char *hwaddr, int hw_len,
565 int hw_type, char *hostname)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000566{
567 struct dhcp_config *config;
568
Simon Kelley0a852542005-03-23 20:28:59 +0000569 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000570 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100571 if (config->flags & CONFIG_CLID)
572 {
573 if (config->clid_len == clid_len &&
574 memcmp(config->clid, clid, clid_len) == 0 &&
575 is_addr_in_context(context, config))
576 return config;
577
578 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
579 cope with that here */
580 if (*clid == 0 && config->clid_len == clid_len-1 &&
581 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
582 is_addr_in_context(context, config))
583 return config;
584 }
585
Simon Kelley0a852542005-03-23 20:28:59 +0000586
Simon Kelleycdeda282006-03-16 20:16:06 +0000587 for (config = configs; config; config = config->next)
588 if ((config->flags & CONFIG_HWADDR) &&
589 config->wildcard_mask == 0 &&
590 config->hwaddr_len == hw_len &&
591 (config->hwaddr_type == hw_type || config->hwaddr_type == 0) &&
592 memcmp(config->hwaddr, hwaddr, hw_len) == 0 &&
593 is_addr_in_context(context, config))
594 return config;
Simon Kelley3d8df262005-08-29 12:19:27 +0100595
Simon Kelley0a852542005-03-23 20:28:59 +0000596 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000597 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100598 if ((config->flags & CONFIG_NAME) &&
599 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000600 is_addr_in_context(context, config))
601 return config;
602
Simon Kelleycdeda282006-03-16 20:16:06 +0000603 for (config = configs; config; config = config->next)
604 if ((config->flags & CONFIG_HWADDR) &&
605 config->wildcard_mask != 0 &&
606 config->hwaddr_len == hw_len &&
607 (config->hwaddr_type == hw_type || config->hwaddr_type == 0) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100608 is_addr_in_context(context, config) &&
609 memcmp_masked(config->hwaddr, hwaddr, hw_len, config->wildcard_mask))
610 return config;
611
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000612 return NULL;
613}
614
Simon Kelley3be34542004-09-11 19:12:13 +0100615void dhcp_read_ethers(struct daemon *daemon)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000616{
Simon Kelley44a2a312004-03-10 20:04:35 +0000617 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000618 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100619 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100620 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000621 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100622 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100623 struct dhcp_config **up, *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100624 struct dhcp_config *config, *configs = daemon->dhcp_conf;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000625 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100626
627 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000628
629 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100630 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000631 syslog(LOG_ERR, _("failed to read %s:%m"), ETHERSFILE);
Simon Kelley3be34542004-09-11 19:12:13 +0100632 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100633 }
634
Simon Kelley849a8352006-06-09 21:02:31 +0100635 /* This can be called again on SIGHUP, so remove entries created last time round. */
636 for (up = &daemon->dhcp_conf, config = configs; config; config = tmp)
637 {
638 tmp = config->next;
639 if (config->flags & CONFIG_FROM_ETHERS)
640 {
641 *up = tmp;
642 /* cannot have a clid */
643 if (config->flags & CONFIG_NAME)
644 free(config->hostname);
645 free(config);
646 }
647 else
648 up = &config->next;
649 }
650
Simon Kelley44a2a312004-03-10 20:04:35 +0000651 while (fgets(buff, MAXDNAME, f))
652 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000653 lineno++;
654
Simon Kelley33820b72004-04-03 21:10:00 +0100655 while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000656 buff[strlen(buff)-1] = 0;
657
658 if ((*buff == '#') || (*buff == '+'))
659 continue;
660
Simon Kelley33820b72004-04-03 21:10:00 +0100661 for (ip = buff; *ip && !isspace(*ip); ip++);
662 for(; *ip && isspace(*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000663 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000664 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000665 {
666 syslog(LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
667 continue;
668 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000669
670 /* check for name or dotted-quad */
671 for (cp = ip; *cp; cp++)
672 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
673 break;
674
675 if (!*cp)
676 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000677 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000678 {
679 syslog(LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
680 continue;
681 }
682
Simon Kelley33820b72004-04-03 21:10:00 +0100683 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000684
685 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100686 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000687 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000688 }
689 else
690 {
Simon Kelley1cff1662004-03-12 08:12:58 +0000691 if (!canonicalise(ip))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000692 {
693 syslog(LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
694 continue;
695 }
696
Simon Kelley33820b72004-04-03 21:10:00 +0100697 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000698
699 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100700 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
Simon Kelley1cff1662004-03-12 08:12:58 +0000701 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000702 }
703
Simon Kelley1cff1662004-03-12 08:12:58 +0000704 if (!config)
705 {
Simon Kelley33820b72004-04-03 21:10:00 +0100706 for (config = configs; config; config = config->next)
707 if ((config->flags & CONFIG_HWADDR) &&
Simon Kelley91dccd02005-03-31 17:48:32 +0100708 config->wildcard_mask == 0 &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000709 config->hwaddr_len == ETHER_ADDR_LEN &&
710 (config->hwaddr_type == ARPHRD_ETHER || config->hwaddr_type == 0) &&
Simon Kelley33820b72004-04-03 21:10:00 +0100711 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
712 break;
713
714 if (!config)
715 {
716 if (!(config = malloc(sizeof(struct dhcp_config))))
717 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100718 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley91dccd02005-03-31 17:48:32 +0100719 config->wildcard_mask = 0;
Simon Kelley33820b72004-04-03 21:10:00 +0100720 config->next = configs;
721 configs = config;
722 }
723
724 config->flags |= flags;
725
726 if (flags & CONFIG_NAME)
727 {
728 if ((config->hostname = malloc(strlen(ip)+1)))
729 strcpy(config->hostname, ip);
730 else
731 config->flags &= ~CONFIG_NAME;
732 }
733
734 if (flags & CONFIG_ADDR)
735 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000736 }
Simon Kelley33820b72004-04-03 21:10:00 +0100737
Simon Kelleyde379512004-06-22 20:23:33 +0100738 config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
Simon Kelley33820b72004-04-03 21:10:00 +0100739 memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
Simon Kelleycdeda282006-03-16 20:16:06 +0000740 config->hwaddr_len = ETHER_ADDR_LEN;
741 config->hwaddr_type = ARPHRD_ETHER;
Simon Kelley33820b72004-04-03 21:10:00 +0100742 count++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000743 }
744
745 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100746
Simon Kelleyb8187c82005-11-26 21:46:27 +0000747 syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley3be34542004-09-11 19:12:13 +0100748
749 daemon->dhcp_conf = configs;
Simon Kelley44a2a312004-03-10 20:04:35 +0000750}
751
752void dhcp_update_configs(struct dhcp_config *configs)
753{
754 /* Some people like to keep all static IP addresses in /etc/hosts.
755 This goes through /etc/hosts and sets static addresses for any DHCP config
Simon Kelley3d8df262005-08-29 12:19:27 +0100756 records which don't have an address and whose name matches.
757 We take care to maintain the invariant that any IP address can appear
Simon Kelley849a8352006-06-09 21:02:31 +0100758 in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
759 restore the status-quo ante first. */
Simon Kelley44a2a312004-03-10 20:04:35 +0000760
Simon Kelley1ab84e22004-01-29 16:48:35 +0000761 struct dhcp_config *config;
762 struct crec *crec;
Simon Kelley849a8352006-06-09 21:02:31 +0100763
764 for (config = configs; config; config = config->next)
765 if (config->flags & CONFIG_ADDR_HOSTS)
766 config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
Simon Kelley44a2a312004-03-10 20:04:35 +0000767
Simon Kelley1ab84e22004-01-29 16:48:35 +0000768 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100769 if (!(config->flags & CONFIG_ADDR) &&
770 (config->flags & CONFIG_NAME) &&
Simon Kelley1ab84e22004-01-29 16:48:35 +0000771 (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
772 (crec->flags & F_HOSTS))
Simon Kelley33820b72004-04-03 21:10:00 +0100773 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100774 if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000775 syslog(LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
Simon Kelley3d8df262005-08-29 12:19:27 +0100776 inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
777 else
778 {
779 config->addr = crec->addr.addr.addr.addr4;
Simon Kelley849a8352006-06-09 21:02:31 +0100780 config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
Simon Kelley3d8df262005-08-29 12:19:27 +0100781 }
Simon Kelley33820b72004-04-03 21:10:00 +0100782 }
Simon Kelley1ab84e22004-01-29 16:48:35 +0000783}
Simon Kelley44a2a312004-03-10 20:04:35 +0000784
Simon Kelleybb01cb92004-12-13 20:56:23 +0000785/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
786 for this address. If it has a domain part, that must match the set domain and
787 it gets stripped. */
788char *host_from_dns(struct daemon *daemon, struct in_addr addr)
789{
790 struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
791 char *hostname = NULL;
792
793 if (lookup && (lookup->flags & F_HOSTS))
794 {
795 hostname = daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000796 strncpy(hostname, cache_get_name(lookup), 256);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100797 hostname[255] = 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000798 hostname = strip_hostname(daemon, hostname);
799 }
800
801 return hostname;
802}
803
804char *strip_hostname(struct daemon *daemon, char *hostname)
805{
806 char *dot = strchr(hostname, '.');
807 if (dot)
808 {
809 if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix))
810 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000811 syslog(LOG_WARNING, _("Ignoring DHCP host name %s because it has an illegal domain part"), hostname);
Simon Kelleybb01cb92004-12-13 20:56:23 +0000812 hostname = NULL;
813 }
814 else
815 {
816 *dot = 0; /* truncate */
817 if (strlen(hostname) == 0)
818 hostname = NULL; /* nothing left */
819 }
820 }
821 return hostname;
822}