/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991, or (at your option) version 3 dated 29 June, 2007. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "dnsmasq.h" #ifdef HAVE_DHCP void dhcp_common_init(void) { /* These each hold a DHCP option max size 255 and get a terminating zero added */ daemon->dhcp_buff = safe_malloc(256); daemon->dhcp_buff2 = safe_malloc(256); daemon->dhcp_buff3 = safe_malloc(256); /* dhcp_packet is used by v4 and v6, outpacket only by v6 sizeof(struct dhcp_packet) is as good an initial size as any, even for v6 */ expand_buf(&daemon->dhcp_packet, sizeof(struct dhcp_packet)); #ifdef HAVE_DHCP6 if (daemon->dhcp6) expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet)); #endif } ssize_t recv_dhcp_packet(int fd, struct msghdr *msg) { ssize_t sz; while (1) { msg->msg_flags = 0; while ((sz = recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR); if (sz == -1) return -1; if (!(msg->msg_flags & MSG_TRUNC)) break; /* Very new Linux kernels return the actual size needed, older ones always return truncated size */ if ((size_t)sz == daemon->dhcp_packet.iov_len) { if (!expand_buf(&daemon->dhcp_packet, sz + 100)) return -1; } else { expand_buf(&daemon->dhcp_packet, sz); break; } } while ((sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR); return (msg->msg_flags & MSG_TRUNC) ? -1 : sz; } struct dhcp_netid *run_tag_if(struct dhcp_netid *tags) { struct tag_if *exprs; struct dhcp_netid_list *list; for (exprs = daemon->tag_if; exprs; exprs = exprs->next) if (match_netid(exprs->tag, tags, 1)) for (list = exprs->set; list; list = list->next) { list->list->next = tags; tags = list->list; } return tags; } struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, struct dhcp_opt *opts) { struct dhcp_netid *tagif = run_tag_if(tags); struct dhcp_opt *opt; /* flag options which are valid with the current tag set (sans context tags) */ for (opt = opts; opt; opt = opt->next) { opt->flags &= ~DHOPT_TAGOK; if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) && match_netid(opt->netid, tagif, 0)) opt->flags |= DHOPT_TAGOK; } /* now flag options which are valid, including the context tags, otherwise valid options are inhibited if we found a higher priotity one above */ if (context_tags) { struct dhcp_netid *last_tag; for (last_tag = context_tags; last_tag->next; last_tag = last_tag->next); last_tag->next = tags; tagif = run_tag_if(context_tags); for (opt = opts; opt; opt = opt->next) if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && match_netid(opt->netid, tagif, 0)) { struct dhcp_opt *tmp; for (tmp = opts; tmp; tmp = tmp->next) if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK)) break; if (!tmp) opt->flags |= DHOPT_TAGOK; } } /* now flag untagged options which are not overridden by tagged ones */ for (opt = opts; opt; opt = opt->next) if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid) { struct dhcp_opt *tmp; for (tmp = opts; tmp; tmp = tmp->next) if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK)) break; if (!tmp) opt->flags |= DHOPT_TAGOK; else if (!tmp->netid) my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt); } return tagif; } /* Is every member of check matched by a member of pool? If tagnotneeded, untagged is OK */ int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded) { struct dhcp_netid *tmp1; if (!check && !tagnotneeded) return 0; for (; check; check = check->next) { /* '#' for not is for backwards compat. */ if (check->net[0] != '!' && check->net[0] != '#') { for (tmp1 = pool; tmp1; tmp1 = tmp1->next) if (strcmp(check->net, tmp1->net) == 0) break; if (!tmp1) return 0; } else for (tmp1 = pool; tmp1; tmp1 = tmp1->next) if (strcmp((check->net)+1, tmp1->net) == 0) return 0; } return 1; } /* return domain or NULL if none. */ char *strip_hostname(char *hostname) { char *dot = strchr(hostname, '.'); if (!dot) return NULL; *dot = 0; /* truncate */ if (strlen(dot+1) != 0) return dot+1; return NULL; } void log_tags(struct dhcp_netid *netid, u32 xid) { if (netid && option_bool(OPT_LOG_OPTS)) { char *s = daemon->namebuff; for (*s = 0; netid; netid = netid->next) { /* kill dupes. */ struct dhcp_netid *n; for (n = netid->next; n; n = n->next) if (strcmp(netid->net, n->net) == 0) break; if (!n) { strncat (s, netid->net, (MAXDNAME-1) - strlen(s)); if (netid->next) strncat (s, ", ", (MAXDNAME-1) - strlen(s)); } } my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s); } } #endif