1124524Sume/* $KAME: rtsol.c,v 1.27 2003/10/05 00:09:36 itojun Exp $ */ 266776Skris 355163Sshin/* 455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5222732Shrs * Copyright (C) 2011 Hiroki Sato 655163Sshin * All rights reserved. 762632Skris * 855163Sshin * Redistribution and use in source and binary forms, with or without 955163Sshin * modification, are permitted provided that the following conditions 1055163Sshin * are met: 1155163Sshin * 1. Redistributions of source code must retain the above copyright 1255163Sshin * notice, this list of conditions and the following disclaimer. 1355163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1455163Sshin * notice, this list of conditions and the following disclaimer in the 1555163Sshin * documentation and/or other materials provided with the distribution. 1655163Sshin * 3. Neither the name of the project nor the names of its contributors 1755163Sshin * may be used to endorse or promote products derived from this software 1855163Sshin * without specific prior written permission. 1962632Skris * 2055163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2155163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2255163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2355163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2455163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2555163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2655163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2755163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2855163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2955163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3055163Sshin * SUCH DAMAGE. 3155163Sshin * 3255163Sshin * $FreeBSD$ 3355163Sshin */ 3455163Sshin 3555163Sshin#include <sys/param.h> 3655163Sshin#include <sys/socket.h> 3755163Sshin#include <sys/uio.h> 3866776Skris#include <sys/queue.h> 39118661Sume#include <sys/wait.h> 40118661Sume#include <sys/stat.h> 4155163Sshin 4255163Sshin#include <net/if.h> 4355163Sshin#include <net/route.h> 4455163Sshin#include <net/if_dl.h> 4555163Sshin 46222732Shrs#define __BSD_VISIBLE 1 /* IN6ADDR_LINKLOCAL_ALLROUTERS_INIT */ 4755163Sshin#include <netinet/in.h> 48222732Shrs#undef __BSD_VISIBLE 4955163Sshin#include <netinet/ip6.h> 5055163Sshin#include <netinet6/ip6_var.h> 5155163Sshin#include <netinet/icmp6.h> 5255163Sshin 5355163Sshin#include <arpa/inet.h> 5455163Sshin 55222732Shrs#include <netdb.h> 5655163Sshin#include <time.h> 57119026Sume#include <fcntl.h> 5855163Sshin#include <unistd.h> 5955163Sshin#include <stdio.h> 60253970Shrs#include <time.h> 6155163Sshin#include <err.h> 6255163Sshin#include <errno.h> 6355163Sshin#include <string.h> 6455163Sshin#include <stdlib.h> 6555163Sshin#include <syslog.h> 6655163Sshin#include "rtsold.h" 6755163Sshin 6855163Sshinstatic struct msghdr rcvmhdr; 6955163Sshinstatic struct msghdr sndmhdr; 7055163Sshinstatic struct iovec rcviov[2]; 7155163Sshinstatic struct iovec sndiov[2]; 7255163Sshinstatic struct sockaddr_in6 from; 73119026Sumestatic int rcvcmsglen; 7455163Sshin 7562632Skrisint rssock; 76225520Shrsstatic char rsid[IFNAMSIZ + 1 + sizeof(DNSINFO_ORIGIN_LABEL) + 1 + NI_MAXHOST]; 77222732Shrsstruct ifinfo_head_t ifinfo_head = 78222732Shrs TAILQ_HEAD_INITIALIZER(ifinfo_head); 7955163Sshin 80222732Shrsstatic const struct sockaddr_in6 sin6_allrouters = { 81204407Suqs .sin6_len = sizeof(sin6_allrouters), 82204407Suqs .sin6_family = AF_INET6, 83222732Shrs .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, 84204407Suqs}; 8555163Sshin 86225520Shrsstatic void call_script(const int, const char *const *, 87225520Shrs struct script_msg_head_t *); 88222732Shrsstatic size_t dname_labeldec(char *, size_t, const char *); 89173412Skevlostatic int safefile(const char *); 90222861Shrsstatic struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t); 91225520Shrsstatic int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *, 92225520Shrs struct script_msg_head_t *, struct script_msg_head_t *); 93225520Shrsstatic char *make_rsid(const char *, const char *, struct rainfo *); 94118661Sume 95222732Shrs#define _ARGS_OTHER otherconf_script, ifi->ifname 96225520Shrs#define _ARGS_RESADD resolvconf_script, "-a", rsid 97225520Shrs#define _ARGS_RESDEL resolvconf_script, "-d", rsid 98222732Shrs 99222732Shrs#define CALL_SCRIPT(name, sm_head) \ 100222732Shrs do { \ 101222732Shrs const char *const sarg[] = { _ARGS_##name, NULL }; \ 102222732Shrs call_script(sizeof(sarg), sarg, sm_head); \ 103222732Shrs } while(0) 104222732Shrs 105222732Shrs#define ELM_MALLOC(p,error_action) \ 106222732Shrs do { \ 107222732Shrs p = malloc(sizeof(*p)); \ 108222732Shrs if (p == NULL) { \ 109222732Shrs warnmsg(LOG_ERR, __func__, "malloc failed: %s", \ 110222732Shrs strerror(errno)); \ 111222732Shrs error_action; \ 112222732Shrs } \ 113222732Shrs memset(p, 0, sizeof(*p)); \ 114222732Shrs } while(0) 115222732Shrs 11655163Sshinint 117124524Sumesockopen(void) 11855163Sshin{ 119119026Sume static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL; 120119026Sume int sndcmsglen, on; 121119026Sume static u_char answer[1500]; 12255163Sshin struct icmp6_filter filt; 12355163Sshin 12462632Skris sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 125118664Sume CMSG_SPACE(sizeof(int)); 12662632Skris if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { 127118660Sume warnmsg(LOG_ERR, __func__, 128118664Sume "malloc for receive msghdr failed"); 129222732Shrs return (-1); 13062632Skris } 131118664Sume if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) { 132118660Sume warnmsg(LOG_ERR, __func__, 133118664Sume "malloc for send msghdr failed"); 134222732Shrs return (-1); 13562632Skris } 13655163Sshin if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 137118660Sume warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); 138222732Shrs return (-1); 13955163Sshin } 14055163Sshin 14155163Sshin /* specify to tell receiving interface */ 14255163Sshin on = 1; 14362632Skris if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, 144118664Sume sizeof(on)) < 0) { 145118660Sume warnmsg(LOG_ERR, __func__, "IPV6_RECVPKTINFO: %s", 146118664Sume strerror(errno)); 14762632Skris exit(1); 14862632Skris } 14955163Sshin 150222732Shrs /* specify to tell value of hoplimit field of received IP6 hdr */ 15155163Sshin on = 1; 15262632Skris if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, 153118664Sume sizeof(on)) < 0) { 154118660Sume warnmsg(LOG_ERR, __func__, "IPV6_RECVHOPLIMIT: %s", 155118664Sume strerror(errno)); 15662632Skris exit(1); 15762632Skris } 15855163Sshin 15955163Sshin /* specfiy to accept only router advertisements on the socket */ 16055163Sshin ICMP6_FILTER_SETBLOCKALL(&filt); 16155163Sshin ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); 16255163Sshin if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 163118664Sume sizeof(filt)) == -1) { 164118660Sume warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s", 165118664Sume strerror(errno)); 16655163Sshin return(-1); 16755163Sshin } 16855163Sshin 16955163Sshin /* initialize msghdr for receiving packets */ 17055163Sshin rcviov[0].iov_base = (caddr_t)answer; 17155163Sshin rcviov[0].iov_len = sizeof(answer); 17255163Sshin rcvmhdr.msg_name = (caddr_t)&from; 17355163Sshin rcvmhdr.msg_iov = rcviov; 17455163Sshin rcvmhdr.msg_iovlen = 1; 17555163Sshin rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; 17655163Sshin 17755163Sshin /* initialize msghdr for sending packets */ 17855163Sshin sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); 17955163Sshin sndmhdr.msg_iov = sndiov; 18055163Sshin sndmhdr.msg_iovlen = 1; 18155163Sshin sndmhdr.msg_control = (caddr_t)sndcmsgbuf; 18262632Skris sndmhdr.msg_controllen = sndcmsglen; 18355163Sshin 184222732Shrs return (rssock); 18555163Sshin} 18655163Sshin 18755163Sshinvoid 188222732Shrssendpacket(struct ifinfo *ifi) 18955163Sshin{ 190118664Sume struct in6_pktinfo *pi; 191118664Sume struct cmsghdr *cm; 192118664Sume int hoplimit = 255; 193204407Suqs ssize_t i; 194119026Sume struct sockaddr_in6 dst; 19555163Sshin 196119026Sume dst = sin6_allrouters; 197222732Shrs dst.sin6_scope_id = ifi->linkid; 198119026Sume 199119026Sume sndmhdr.msg_name = (caddr_t)&dst; 200222732Shrs sndmhdr.msg_iov[0].iov_base = (caddr_t)ifi->rs_data; 201222732Shrs sndmhdr.msg_iov[0].iov_len = ifi->rs_datalen; 20255163Sshin 20355163Sshin cm = CMSG_FIRSTHDR(&sndmhdr); 20455163Sshin /* specify the outgoing interface */ 20555163Sshin cm->cmsg_level = IPPROTO_IPV6; 20655163Sshin cm->cmsg_type = IPV6_PKTINFO; 20755163Sshin cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 208254462Shrs pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); 20955163Sshin memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ 210222732Shrs pi->ipi6_ifindex = ifi->sdl->sdl_index; 21155163Sshin 21255163Sshin /* specify the hop limit of the packet */ 213118664Sume cm = CMSG_NXTHDR(&sndmhdr, cm); 214118664Sume cm->cmsg_level = IPPROTO_IPV6; 215118664Sume cm->cmsg_type = IPV6_HOPLIMIT; 216118664Sume cm->cmsg_len = CMSG_LEN(sizeof(int)); 217118664Sume memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); 21855163Sshin 219118664Sume warnmsg(LOG_DEBUG, __func__, 220118664Sume "send RS on %s, whose state is %d", 221222732Shrs ifi->ifname, ifi->state); 22255163Sshin i = sendmsg(rssock, &sndmhdr, 0); 223222732Shrs if (i < 0 || (size_t)i != ifi->rs_datalen) { 22455163Sshin /* 22555163Sshin * ENETDOWN is not so serious, especially when using several 22655163Sshin * network cards on a mobile node. We ignore it. 22755163Sshin */ 22855163Sshin if (errno != ENETDOWN || dflag > 0) 229118660Sume warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s", 230222732Shrs ifi->ifname, strerror(errno)); 23155163Sshin } 23255163Sshin 23355163Sshin /* update counter */ 234222732Shrs ifi->probes++; 23555163Sshin} 23655163Sshin 23755163Sshinvoid 23855163Sshinrtsol_input(int s) 23955163Sshin{ 240253376Skevlo char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; 241222732Shrs int l, ifindex = 0, *hlimp = NULL; 242222732Shrs ssize_t msglen; 24355163Sshin struct in6_pktinfo *pi = NULL; 24455163Sshin struct ifinfo *ifi = NULL; 245222732Shrs struct ra_opt *rao = NULL; 246119026Sume struct icmp6_hdr *icp; 247118661Sume struct nd_router_advert *nd_ra; 248119026Sume struct cmsghdr *cm; 249222861Shrs struct rainfo *rai; 250222732Shrs char *raoptp; 251222732Shrs char *p; 252222732Shrs struct in6_addr *addr; 253222732Shrs struct nd_opt_hdr *ndo; 254222732Shrs struct nd_opt_rdnss *rdnss; 255222732Shrs struct nd_opt_dnssl *dnssl; 256222732Shrs size_t len; 257222732Shrs char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1]; 258222732Shrs char dname[NI_MAXHOST]; 259253970Shrs struct timespec now; 260253970Shrs struct timespec lifetime; 261222861Shrs int newent_rai; 262222861Shrs int newent_rao; 26355163Sshin 264119026Sume /* get message. namelen and controllen must always be initialized. */ 265119026Sume rcvmhdr.msg_namelen = sizeof(from); 266119026Sume rcvmhdr.msg_controllen = rcvcmsglen; 267222732Shrs if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) { 268118660Sume warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); 26955163Sshin return; 27055163Sshin } 27155163Sshin 27255163Sshin /* extract optional information via Advanced API */ 273118664Sume for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); cm; 274118664Sume cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { 27555163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 27655163Sshin cm->cmsg_type == IPV6_PKTINFO && 27755163Sshin cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { 278254462Shrs pi = (struct in6_pktinfo *)(void *)(CMSG_DATA(cm)); 27955163Sshin ifindex = pi->ipi6_ifindex; 28055163Sshin } 28155163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 28255163Sshin cm->cmsg_type == IPV6_HOPLIMIT && 28355163Sshin cm->cmsg_len == CMSG_LEN(sizeof(int))) 284254462Shrs hlimp = (int *)(void *)CMSG_DATA(cm); 28555163Sshin } 28655163Sshin 28755163Sshin if (ifindex == 0) { 288118664Sume warnmsg(LOG_ERR, __func__, 289118664Sume "failed to get receiving interface"); 29055163Sshin return; 29155163Sshin } 29255163Sshin if (hlimp == NULL) { 293118664Sume warnmsg(LOG_ERR, __func__, 294118664Sume "failed to get receiving hop limit"); 29555163Sshin return; 29655163Sshin } 29755163Sshin 298222732Shrs if ((size_t)msglen < sizeof(struct nd_router_advert)) { 299118906Sume warnmsg(LOG_INFO, __func__, 300222732Shrs "packet size(%zd) is too short", msglen); 30155163Sshin return; 30255163Sshin } 30355163Sshin 30455163Sshin icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; 30555163Sshin 30655163Sshin if (icp->icmp6_type != ND_ROUTER_ADVERT) { 307118906Sume /* 308118906Sume * this should not happen because we configured a filter 309118906Sume * that only passes RAs on the receiving socket. 310118906Sume */ 311118660Sume warnmsg(LOG_ERR, __func__, 312118664Sume "invalid icmp type(%d) from %s on %s", icp->icmp6_type, 313118664Sume inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 314225520Shrs sizeof(ntopbuf)), 315118664Sume if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 31655163Sshin return; 31755163Sshin } 31855163Sshin 31955163Sshin if (icp->icmp6_code != 0) { 320118906Sume warnmsg(LOG_INFO, __func__, 321118664Sume "invalid icmp code(%d) from %s on %s", icp->icmp6_code, 322118664Sume inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 323225520Shrs sizeof(ntopbuf)), 324118664Sume if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 32555163Sshin return; 32655163Sshin } 32755163Sshin 32855163Sshin if (*hlimp != 255) { 329118906Sume warnmsg(LOG_INFO, __func__, 330118664Sume "invalid RA with hop limit(%d) from %s on %s", 331118664Sume *hlimp, 332118664Sume inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 333225520Shrs sizeof(ntopbuf)), 334118664Sume if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 33555163Sshin return; 33655163Sshin } 33755163Sshin 33855163Sshin if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { 339118906Sume warnmsg(LOG_INFO, __func__, 340118664Sume "invalid RA with non link-local source from %s on %s", 341118664Sume inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 342225520Shrs sizeof(ntopbuf)), 343118664Sume if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 34455163Sshin return; 34555163Sshin } 34655163Sshin 34755163Sshin /* xxx: more validation? */ 34855163Sshin 34955163Sshin if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) { 350118906Sume warnmsg(LOG_INFO, __func__, 351118664Sume "received RA from %s on an unexpected IF(%s)", 352118664Sume inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 353225520Shrs sizeof(ntopbuf)), 354118664Sume if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 35555163Sshin return; 35655163Sshin } 35755163Sshin 358118660Sume warnmsg(LOG_DEBUG, __func__, 359118664Sume "received RA from %s on %s, state is %d", 360225520Shrs inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), 361118664Sume ifi->ifname, ifi->state); 36255163Sshin 363118661Sume nd_ra = (struct nd_router_advert *)icp; 364118661Sume 365118661Sume /* 366118661Sume * Process the "O bit." 367118661Sume * If the value of OtherConfigFlag changes from FALSE to TRUE, the 368118661Sume * host should invoke the stateful autoconfiguration protocol, 369118661Sume * requesting information. 370118661Sume * [RFC 2462 Section 5.5.3] 371118661Sume */ 372118661Sume if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) && 373118661Sume !ifi->otherconfig) { 374118661Sume warnmsg(LOG_DEBUG, __func__, 375118661Sume "OtherConfigFlag on %s is turned on", ifi->ifname); 376118661Sume ifi->otherconfig = 1; 377222732Shrs CALL_SCRIPT(OTHER, NULL); 378118661Sume } 379253970Shrs clock_gettime(CLOCK_MONOTONIC_FAST, &now); 380222861Shrs newent_rai = 0; 381222861Shrs rai = find_rainfo(ifi, &from); 382222861Shrs if (rai == NULL) { 383222861Shrs ELM_MALLOC(rai, exit(1)); 384222861Shrs rai->rai_ifinfo = ifi; 385222861Shrs TAILQ_INIT(&rai->rai_ra_opt); 386225520Shrs rai->rai_saddr.sin6_family = AF_INET6; 387225520Shrs rai->rai_saddr.sin6_len = sizeof(rai->rai_saddr); 388222861Shrs memcpy(&rai->rai_saddr.sin6_addr, &from.sin6_addr, 389222861Shrs sizeof(rai->rai_saddr.sin6_addr)); 390222861Shrs newent_rai = 1; 391222861Shrs } 392222732Shrs 393222732Shrs#define RA_OPT_NEXT_HDR(x) (struct nd_opt_hdr *)((char *)x + \ 394222732Shrs (((struct nd_opt_hdr *)x)->nd_opt_len * 8)) 395222732Shrs /* Process RA options. */ 396222732Shrs warnmsg(LOG_DEBUG, __func__, "Processing RA"); 397222732Shrs raoptp = (char *)icp + sizeof(struct nd_router_advert); 398222732Shrs while (raoptp < (char *)icp + msglen) { 399222732Shrs ndo = (struct nd_opt_hdr *)raoptp; 400222732Shrs warnmsg(LOG_DEBUG, __func__, "ndo = %p", raoptp); 401222732Shrs warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_type = %d", 402222732Shrs ndo->nd_opt_type); 403222732Shrs warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_len = %d", 404222732Shrs ndo->nd_opt_len); 405222732Shrs 406222732Shrs switch (ndo->nd_opt_type) { 407222732Shrs case ND_OPT_RDNSS: 408222732Shrs rdnss = (struct nd_opt_rdnss *)raoptp; 409222732Shrs 410222732Shrs /* Optlen sanity check (Section 5.3.1 in RFC 6106) */ 411222732Shrs if (rdnss->nd_opt_rdnss_len < 3) { 412222732Shrs warnmsg(LOG_INFO, __func__, 413222732Shrs "too short RDNSS option" 414222732Shrs "in RA from %s was ignored.", 415222732Shrs inet_ntop(AF_INET6, &from.sin6_addr, 416225520Shrs ntopbuf, sizeof(ntopbuf))); 417222732Shrs break; 418222732Shrs } 419222732Shrs 420254462Shrs addr = (struct in6_addr *)(void *)(raoptp + sizeof(*rdnss)); 421222732Shrs while ((char *)addr < (char *)RA_OPT_NEXT_HDR(raoptp)) { 422222732Shrs if (inet_ntop(AF_INET6, addr, ntopbuf, 423225520Shrs sizeof(ntopbuf)) == NULL) { 424222732Shrs warnmsg(LOG_INFO, __func__, 425222732Shrs "an invalid address in RDNSS option" 426222732Shrs " in RA from %s was ignored.", 427222732Shrs inet_ntop(AF_INET6, &from.sin6_addr, 428225520Shrs ntopbuf, sizeof(ntopbuf))); 429222732Shrs addr++; 430222732Shrs continue; 431222732Shrs } 432222732Shrs if (IN6_IS_ADDR_LINKLOCAL(addr)) 433222732Shrs /* XXX: % has to be escaped here */ 434222732Shrs l = snprintf(nsbuf, sizeof(nsbuf), 435222732Shrs "%s%c%s", ntopbuf, 436222732Shrs SCOPE_DELIMITER, 437222732Shrs ifi->ifname); 438222732Shrs else 439222732Shrs l = snprintf(nsbuf, sizeof(nsbuf), 440222732Shrs "%s", ntopbuf); 441222732Shrs if (l < 0 || (size_t)l >= sizeof(nsbuf)) { 442222732Shrs warnmsg(LOG_ERR, __func__, 443222732Shrs "address copying error in " 444222732Shrs "RDNSS option: %d.", l); 445222732Shrs addr++; 446222732Shrs continue; 447222732Shrs } 448222732Shrs warnmsg(LOG_DEBUG, __func__, "nsbuf = %s", 449222732Shrs nsbuf); 450222732Shrs 451222861Shrs newent_rao = 0; 452222861Shrs rao = find_raopt(rai, ndo->nd_opt_type, nsbuf, 453222861Shrs strlen(nsbuf)); 454222861Shrs if (rao == NULL) { 455222861Shrs ELM_MALLOC(rao, break); 456222861Shrs rao->rao_type = ndo->nd_opt_type; 457222861Shrs rao->rao_len = strlen(nsbuf); 458222861Shrs rao->rao_msg = strdup(nsbuf); 459222861Shrs if (rao->rao_msg == NULL) { 460222861Shrs warnmsg(LOG_ERR, __func__, 461222861Shrs "strdup failed: %s", 462222861Shrs strerror(errno)); 463222861Shrs free(rao); 464222861Shrs addr++; 465222861Shrs continue; 466222861Shrs } 467222861Shrs newent_rao = 1; 468222732Shrs } 469222732Shrs /* Set expiration timer */ 470222861Shrs memset(&rao->rao_expire, 0, 471222861Shrs sizeof(rao->rao_expire)); 472222732Shrs memset(&lifetime, 0, sizeof(lifetime)); 473222861Shrs lifetime.tv_sec = 474222861Shrs ntohl(rdnss->nd_opt_rdnss_lifetime); 475253970Shrs TS_ADD(&now, &lifetime, &rao->rao_expire); 476222732Shrs 477222861Shrs if (newent_rao) 478222861Shrs TAILQ_INSERT_TAIL(&rai->rai_ra_opt, 479222861Shrs rao, rao_next); 480222732Shrs addr++; 481222732Shrs } 482222732Shrs break; 483222732Shrs case ND_OPT_DNSSL: 484222732Shrs dnssl = (struct nd_opt_dnssl *)raoptp; 485222732Shrs 486222732Shrs /* Optlen sanity check (Section 5.3.1 in RFC 6106) */ 487222732Shrs if (dnssl->nd_opt_dnssl_len < 2) { 488222732Shrs warnmsg(LOG_INFO, __func__, 489222732Shrs "too short DNSSL option" 490222732Shrs "in RA from %s was ignored.", 491222732Shrs inet_ntop(AF_INET6, &from.sin6_addr, 492225520Shrs ntopbuf, sizeof(ntopbuf))); 493222732Shrs break; 494222732Shrs } 495222732Shrs 496222732Shrs /* 497222732Shrs * Ensure NUL-termination in DNSSL in case of 498222732Shrs * malformed field. 499222732Shrs */ 500222732Shrs p = (char *)RA_OPT_NEXT_HDR(raoptp); 501222732Shrs *(p - 1) = '\0'; 502222732Shrs 503222732Shrs p = raoptp + sizeof(*dnssl); 504222732Shrs while (1 < (len = dname_labeldec(dname, sizeof(dname), 505222732Shrs p))) { 506222732Shrs /* length == 1 means empty string */ 507222732Shrs warnmsg(LOG_DEBUG, __func__, "dname = %s", 508222732Shrs dname); 509222732Shrs 510222861Shrs newent_rao = 0; 511222861Shrs rao = find_raopt(rai, ndo->nd_opt_type, dname, 512222861Shrs strlen(dname)); 513222861Shrs if (rao == NULL) { 514222861Shrs ELM_MALLOC(rao, break); 515222861Shrs rao->rao_type = ndo->nd_opt_type; 516222861Shrs rao->rao_len = strlen(dname); 517222861Shrs rao->rao_msg = strdup(dname); 518222861Shrs if (rao->rao_msg == NULL) { 519222861Shrs warnmsg(LOG_ERR, __func__, 520222861Shrs "strdup failed: %s", 521222861Shrs strerror(errno)); 522222861Shrs free(rao); 523222861Shrs addr++; 524222861Shrs continue; 525222861Shrs } 526222861Shrs newent_rao = 1; 527222732Shrs } 528222732Shrs /* Set expiration timer */ 529222861Shrs memset(&rao->rao_expire, 0, 530222861Shrs sizeof(rao->rao_expire)); 531222732Shrs memset(&lifetime, 0, sizeof(lifetime)); 532222861Shrs lifetime.tv_sec = 533222861Shrs ntohl(dnssl->nd_opt_dnssl_lifetime); 534253970Shrs TS_ADD(&now, &lifetime, &rao->rao_expire); 535222732Shrs 536222861Shrs if (newent_rao) 537222861Shrs TAILQ_INSERT_TAIL(&rai->rai_ra_opt, 538222861Shrs rao, rao_next); 539222732Shrs p += len; 540222732Shrs } 541222732Shrs break; 542222732Shrs default: 543222732Shrs /* nothing to do for other options */ 544222732Shrs break; 545222732Shrs } 546222732Shrs raoptp = (char *)RA_OPT_NEXT_HDR(raoptp); 547222732Shrs } 548222861Shrs if (newent_rai) 549222861Shrs TAILQ_INSERT_TAIL(&ifi->ifi_rainfo, rai, rai_next); 550222861Shrs 551222732Shrs ra_opt_handler(ifi); 55255163Sshin ifi->racnt++; 55355163Sshin 554118664Sume switch (ifi->state) { 555118664Sume case IFS_IDLE: /* should be ignored */ 556118664Sume case IFS_DELAY: /* right? */ 557118664Sume break; 558118664Sume case IFS_PROBE: 559118664Sume ifi->state = IFS_IDLE; 560118664Sume ifi->probes = 0; 561118664Sume rtsol_timer_update(ifi); 562118664Sume break; 56355163Sshin } 56455163Sshin} 565118661Sume 566222732Shrsstatic char resstr_ns_prefix[] = "nameserver "; 567222732Shrsstatic char resstr_sh_prefix[] = "search "; 568222732Shrsstatic char resstr_nl[] = "\n"; 569222732Shrsstatic char resstr_sp[] = " "; 570222732Shrs 571222732Shrsint 572222732Shrsra_opt_handler(struct ifinfo *ifi) 573222732Shrs{ 574222732Shrs struct ra_opt *rao; 575222861Shrs struct rainfo *rai; 576222732Shrs struct script_msg *smp1, *smp2, *smp3; 577253970Shrs struct timespec now; 578225520Shrs struct script_msg_head_t sm_rdnss_head = 579225520Shrs TAILQ_HEAD_INITIALIZER(sm_rdnss_head); 580225520Shrs struct script_msg_head_t sm_dnssl_head = 581225520Shrs TAILQ_HEAD_INITIALIZER(sm_dnssl_head); 582225520Shrs 583222732Shrs int dcount, dlen; 584222732Shrs 585222732Shrs dcount = 0; 586222732Shrs dlen = strlen(resstr_sh_prefix) + strlen(resstr_nl); 587253970Shrs clock_gettime(CLOCK_MONOTONIC_FAST, &now); 588222732Shrs 589222861Shrs /* 590222861Shrs * All options from multiple RAs with the same or different 591222861Shrs * source addresses on a single interface will be gathered and 592222861Shrs * handled, not overridden. [RFC 4861 6.3.4] 593222861Shrs */ 594222861Shrs TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) { 595222861Shrs TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) { 596222861Shrs switch (rao->rao_type) { 597222861Shrs case ND_OPT_RDNSS: 598253970Shrs if (TS_CMP(&now, &rao->rao_expire, >)) { 599222861Shrs warnmsg(LOG_INFO, __func__, 600222861Shrs "expired rdnss entry: %s", 601222861Shrs (char *)rao->rao_msg); 602222861Shrs break; 603222861Shrs } 604222861Shrs ELM_MALLOC(smp1, continue); 605222861Shrs ELM_MALLOC(smp2, goto free1); 606222861Shrs ELM_MALLOC(smp3, goto free2); 607222861Shrs smp1->sm_msg = resstr_ns_prefix; 608222861Shrs TAILQ_INSERT_TAIL(&sm_rdnss_head, smp1, 609222861Shrs sm_next); 610222861Shrs smp2->sm_msg = rao->rao_msg; 611222861Shrs TAILQ_INSERT_TAIL(&sm_rdnss_head, smp2, 612222861Shrs sm_next); 613222861Shrs smp3->sm_msg = resstr_nl; 614222861Shrs TAILQ_INSERT_TAIL(&sm_rdnss_head, smp3, 615222861Shrs sm_next); 616222861Shrs ifi->ifi_rdnss = IFI_DNSOPT_STATE_RECEIVED; 617222861Shrs 618222732Shrs break; 619222861Shrs case ND_OPT_DNSSL: 620253970Shrs if (TS_CMP(&now, &rao->rao_expire, >)) { 621222861Shrs warnmsg(LOG_INFO, __func__, 622222861Shrs "expired dnssl entry: %s", 623222861Shrs (char *)rao->rao_msg); 624222861Shrs break; 625222861Shrs } 626222861Shrs dcount++; 627222861Shrs /* Check resolv.conf(5) restrictions. */ 628222861Shrs if (dcount > 6) { 629222861Shrs warnmsg(LOG_INFO, __func__, 630222861Shrs "dnssl entry exceeding maximum count (%d>6)" 631222861Shrs ": %s", dcount, (char *)rao->rao_msg); 632222861Shrs break; 633222861Shrs } 634222861Shrs if (256 < dlen + strlen(rao->rao_msg) + 635222861Shrs strlen(resstr_sp)) { 636222861Shrs warnmsg(LOG_INFO, __func__, 637222861Shrs "dnssl entry exceeding maximum length " 638222861Shrs "(>256): %s", (char *)rao->rao_msg); 639222861Shrs break; 640222861Shrs } 641222861Shrs ELM_MALLOC(smp1, continue); 642222861Shrs ELM_MALLOC(smp2, goto free1); 643222861Shrs if (TAILQ_EMPTY(&sm_dnssl_head)) { 644222861Shrs ELM_MALLOC(smp3, goto free2); 645222861Shrs smp3->sm_msg = resstr_sh_prefix; 646222861Shrs TAILQ_INSERT_TAIL(&sm_dnssl_head, smp3, 647222861Shrs sm_next); 648222861Shrs } 649222861Shrs smp1->sm_msg = rao->rao_msg; 650222861Shrs TAILQ_INSERT_TAIL(&sm_dnssl_head, smp1, 651222861Shrs sm_next); 652222861Shrs smp2->sm_msg = resstr_sp; 653222861Shrs TAILQ_INSERT_TAIL(&sm_dnssl_head, smp2, 654222861Shrs sm_next); 655222861Shrs dlen += strlen(rao->rao_msg) + 656222861Shrs strlen(resstr_sp); 657222732Shrs break; 658222861Shrs 659222861Shrs ifi->ifi_dnssl = IFI_DNSOPT_STATE_RECEIVED; 660222861Shrs default: 661222732Shrs break; 662222732Shrs } 663222861Shrs continue; 664222732Shrsfree2: 665222861Shrs free(smp2); 666222732Shrsfree1: 667222861Shrs free(smp1); 668222861Shrs } 669225520Shrs /* Call the script for each information source. */ 670225520Shrs if (uflag) 671225520Shrs ra_opt_rdnss_dispatch(ifi, rai, &sm_rdnss_head, 672225520Shrs &sm_dnssl_head); 673222732Shrs } 674225520Shrs /* Call the script for each interface. */ 675225520Shrs if (!uflag) 676225520Shrs ra_opt_rdnss_dispatch(ifi, NULL, &sm_rdnss_head, 677225520Shrs &sm_dnssl_head); 678225520Shrs return (0); 679225520Shrs} 680225520Shrs 681225520Shrschar * 682225520Shrsmake_rsid(const char *ifname, const char *origin, struct rainfo *rai) 683225520Shrs{ 684225520Shrs char hbuf[NI_MAXHOST]; 685225520Shrs 686225520Shrs if (rai == NULL) 687225520Shrs sprintf(rsid, "%s:%s", ifname, origin); 688225520Shrs else { 689225520Shrs if (!IN6_IS_ADDR_LINKLOCAL(&rai->rai_saddr.sin6_addr)) 690225520Shrs return (NULL); 691225520Shrs if (getnameinfo((struct sockaddr *)&rai->rai_saddr, 692225520Shrs rai->rai_saddr.sin6_len, hbuf, sizeof(hbuf), NULL, 0, 693225520Shrs NI_NUMERICHOST) != 0) 694225520Shrs return (NULL); 695225520Shrs sprintf(rsid, "%s:%s:[%s]", ifname, origin, hbuf); 696225520Shrs } 697225520Shrs warnmsg(LOG_DEBUG, __func__, "rsid = [%s]", rsid); 698225520Shrs return (rsid); 699225520Shrs} 700225520Shrs 701225520Shrsint 702225520Shrsra_opt_rdnss_dispatch(struct ifinfo *ifi, 703225520Shrs struct rainfo *rai, 704225520Shrs struct script_msg_head_t *sm_rdnss_head, 705225520Shrs struct script_msg_head_t *sm_dnssl_head) 706225520Shrs{ 707225520Shrs const char *r; 708225520Shrs struct script_msg *smp1; 709225520Shrs int error; 710225520Shrs 711225520Shrs error = 0; 712222732Shrs /* Add \n for DNSSL list. */ 713225520Shrs if (!TAILQ_EMPTY(sm_dnssl_head)) { 714225520Shrs ELM_MALLOC(smp1, goto ra_opt_rdnss_freeit); 715222732Shrs smp1->sm_msg = resstr_nl; 716225520Shrs TAILQ_INSERT_TAIL(sm_dnssl_head, smp1, sm_next); 717222732Shrs } 718225520Shrs TAILQ_CONCAT(sm_rdnss_head, sm_dnssl_head, sm_next); 719222732Shrs 720225520Shrs if (rai != NULL && uflag) 721225520Shrs r = make_rsid(ifi->ifname, DNSINFO_ORIGIN_LABEL, rai); 722225520Shrs else 723225520Shrs r = make_rsid(ifi->ifname, DNSINFO_ORIGIN_LABEL, NULL); 724225520Shrs if (r == NULL) { 725225520Shrs warnmsg(LOG_ERR, __func__, "make_rsid() failed. " 726225520Shrs "Script was not invoked."); 727225520Shrs error = 1; 728225520Shrs goto ra_opt_rdnss_freeit; 729225520Shrs } 730225520Shrs if (!TAILQ_EMPTY(sm_rdnss_head)) 731225520Shrs CALL_SCRIPT(RESADD, sm_rdnss_head); 732222861Shrs else if (ifi->ifi_rdnss == IFI_DNSOPT_STATE_RECEIVED || 733222861Shrs ifi->ifi_dnssl == IFI_DNSOPT_STATE_RECEIVED) { 734222732Shrs CALL_SCRIPT(RESDEL, NULL); 735222861Shrs ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO; 736222861Shrs ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO; 737222861Shrs } 738222732Shrs 739225520Shrsra_opt_rdnss_freeit: 740222732Shrs /* Clear script message queue. */ 741225520Shrs if (!TAILQ_EMPTY(sm_rdnss_head)) { 742225520Shrs while ((smp1 = TAILQ_FIRST(sm_rdnss_head)) != NULL) { 743225520Shrs TAILQ_REMOVE(sm_rdnss_head, smp1, sm_next); 744222732Shrs free(smp1); 745222732Shrs } 746222732Shrs } 747225520Shrs if (!TAILQ_EMPTY(sm_dnssl_head)) { 748225520Shrs while ((smp1 = TAILQ_FIRST(sm_dnssl_head)) != NULL) { 749225520Shrs TAILQ_REMOVE(sm_dnssl_head, smp1, sm_next); 750222861Shrs free(smp1); 751222861Shrs } 752222861Shrs } 753225520Shrs return (error); 754222732Shrs} 755222732Shrs 756222861Shrsstatic struct ra_opt * 757222861Shrsfind_raopt(struct rainfo *rai, int type, void *msg, size_t len) 758222861Shrs{ 759222861Shrs struct ra_opt *rao; 760222861Shrs 761222861Shrs TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) { 762222861Shrs if (rao->rao_type == type && 763222861Shrs rao->rao_len == strlen(msg) && 764222861Shrs memcmp(rao->rao_msg, msg, len) == 0) 765222861Shrs break; 766222861Shrs } 767222861Shrs 768222861Shrs return (rao); 769222861Shrs} 770222861Shrs 771118661Sumestatic void 772225520Shrscall_script(const int argc, const char *const argv[], 773225520Shrs struct script_msg_head_t *sm_head) 774118661Sume{ 775222732Shrs const char *scriptpath; 776222732Shrs int fd[2]; 777222732Shrs int error; 778118661Sume pid_t pid, wpid; 779118661Sume 780222732Shrs if ((scriptpath = argv[0]) == NULL) 781118661Sume return; 782118661Sume 783222732Shrs fd[0] = fd[1] = -1; 784222732Shrs if (sm_head != NULL && !TAILQ_EMPTY(sm_head)) { 785222732Shrs error = pipe(fd); 786222732Shrs if (error) { 787222732Shrs warnmsg(LOG_ERR, __func__, 788222732Shrs "failed to create a pipe: %s", strerror(errno)); 789222732Shrs return; 790222732Shrs } 791222732Shrs } 792222732Shrs 793118661Sume /* launch the script */ 794118661Sume pid = fork(); 795118661Sume if (pid < 0) { 796118661Sume warnmsg(LOG_ERR, __func__, 797118661Sume "failed to fork: %s", strerror(errno)); 798118661Sume return; 799222732Shrs } else if (pid) { /* parent */ 800118661Sume int wstatus; 801118661Sume 802222732Shrs if (fd[0] != -1) { /* Send message to the child if any. */ 803222732Shrs ssize_t len; 804222732Shrs struct script_msg *smp; 805222732Shrs 806222732Shrs close(fd[0]); 807222732Shrs TAILQ_FOREACH(smp, sm_head, sm_next) { 808222732Shrs len = strlen(smp->sm_msg); 809222732Shrs warnmsg(LOG_DEBUG, __func__, 810222732Shrs "write to child = %s(%zd)", 811222732Shrs smp->sm_msg, len); 812222732Shrs if (write(fd[1], smp->sm_msg, len) != len) { 813222732Shrs warnmsg(LOG_ERR, __func__, 814222732Shrs "write to child failed: %s", 815222732Shrs strerror(errno)); 816222732Shrs break; 817222732Shrs } 818222732Shrs } 819222732Shrs close(fd[1]); 820222732Shrs } 821118661Sume do { 822118661Sume wpid = wait(&wstatus); 823118661Sume } while (wpid != pid && wpid > 0); 824118661Sume 825118661Sume if (wpid < 0) 826118661Sume warnmsg(LOG_ERR, __func__, 827118661Sume "wait: %s", strerror(errno)); 828222732Shrs else 829118661Sume warnmsg(LOG_DEBUG, __func__, 830118661Sume "script \"%s\" terminated", scriptpath); 831222732Shrs } else { /* child */ 832222732Shrs int nullfd; 833222732Shrs char **_argv; 834118661Sume 835118661Sume if (safefile(scriptpath)) { 836118661Sume warnmsg(LOG_ERR, __func__, 837118661Sume "script \"%s\" cannot be executed safely", 838118661Sume scriptpath); 839118661Sume exit(1); 840118661Sume } 841222732Shrs nullfd = open("/dev/null", O_RDWR); 842222732Shrs if (nullfd < 0) { 843222732Shrs warnmsg(LOG_ERR, __func__, 844222732Shrs "open /dev/null: %s", strerror(errno)); 845222732Shrs exit(1); 846222732Shrs } 847222732Shrs if (fd[0] != -1) { /* Receive message from STDIN if any. */ 848222732Shrs close(fd[1]); 849222732Shrs if (fd[0] != STDIN_FILENO) { 850222732Shrs /* Connect a pipe read-end to child's STDIN. */ 851222732Shrs if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) { 852222732Shrs warnmsg(LOG_ERR, __func__, 853222732Shrs "dup2 STDIN: %s", strerror(errno)); 854222732Shrs exit(1); 855222732Shrs } 856222732Shrs close(fd[0]); 857222732Shrs } 858222732Shrs } else 859222732Shrs dup2(nullfd, STDIN_FILENO); 860222732Shrs 861222732Shrs dup2(nullfd, STDOUT_FILENO); 862222732Shrs dup2(nullfd, STDERR_FILENO); 863222732Shrs if (nullfd > STDERR_FILENO) 864222732Shrs close(nullfd); 865118661Sume 866222732Shrs _argv = malloc(sizeof(*_argv) * argc); 867222732Shrs if (_argv == NULL) { 868222732Shrs warnmsg(LOG_ERR, __func__, 869222732Shrs "malloc: %s", strerror(errno)); 870222732Shrs exit(1); 871118661Sume } 872222732Shrs memcpy(_argv, argv, (size_t)argc); 873222732Shrs execv(scriptpath, (char *const *)_argv); 874118661Sume warnmsg(LOG_ERR, __func__, "child: exec failed: %s", 875118661Sume strerror(errno)); 876222732Shrs exit(1); 877118661Sume } 878118661Sume 879118661Sume return; 880118661Sume} 881118661Sume 882118661Sumestatic int 883124524Sumesafefile(const char *path) 884118661Sume{ 885118661Sume struct stat s; 886118661Sume uid_t myuid; 887118661Sume 888118661Sume /* no setuid */ 889118661Sume if (getuid() != geteuid()) { 890118661Sume warnmsg(LOG_NOTICE, __func__, 891118661Sume "setuid'ed execution not allowed\n"); 892118661Sume return (-1); 893118661Sume } 894118661Sume 895118661Sume if (lstat(path, &s) != 0) { 896118661Sume warnmsg(LOG_NOTICE, __func__, "lstat failed: %s", 897118661Sume strerror(errno)); 898118661Sume return (-1); 899118661Sume } 900118661Sume 901118661Sume /* the file must be owned by the running uid */ 902118661Sume myuid = getuid(); 903118661Sume if (s.st_uid != myuid) { 904118661Sume warnmsg(LOG_NOTICE, __func__, 905118661Sume "%s has invalid owner uid\n", path); 906118661Sume return (-1); 907118661Sume } 908118661Sume 909118661Sume switch (s.st_mode & S_IFMT) { 910118661Sume case S_IFREG: 911118661Sume break; 912118661Sume default: 913118661Sume warnmsg(LOG_NOTICE, __func__, 914118661Sume "%s is an invalid file type 0x%o\n", 915118661Sume path, (s.st_mode & S_IFMT)); 916118661Sume return (-1); 917118661Sume } 918118661Sume 919118661Sume return (0); 920118661Sume} 921222732Shrs 922222732Shrs/* Decode domain name label encoding in RFC 1035 Section 3.1 */ 923222732Shrsstatic size_t 924222732Shrsdname_labeldec(char *dst, size_t dlen, const char *src) 925222732Shrs{ 926222732Shrs size_t len; 927222732Shrs const char *src_origin; 928222732Shrs const char *src_last; 929222732Shrs const char *dst_origin; 930222732Shrs 931222732Shrs src_origin = src; 932222732Shrs src_last = strchr(src, '\0'); 933222732Shrs dst_origin = dst; 934222732Shrs memset(dst, '\0', dlen); 935222732Shrs while (src && (len = (uint8_t)(*src++) & 0x3f) && 936273414Sdelphij (src + len) <= src_last && 937273414Sdelphij (dst - dst_origin < (ssize_t)dlen)) { 938222732Shrs if (dst != dst_origin) 939222732Shrs *dst++ = '.'; 940222732Shrs warnmsg(LOG_DEBUG, __func__, "labellen = %zd", len); 941222732Shrs memcpy(dst, src, len); 942222732Shrs src += len; 943222732Shrs dst += len; 944222732Shrs } 945222732Shrs *dst = '\0'; 946222732Shrs 947222732Shrs /* 948222732Shrs * XXX validate that domain name only contains valid characters 949222732Shrs * for two reasons: 1) correctness, 2) we do not want to pass 950222732Shrs * possible malicious, unescaped characters like `` to a script 951222732Shrs * or program that could be exploited that way. 952222732Shrs */ 953222732Shrs 954222732Shrs return (src - src_origin); 955222732Shrs} 956