blob: 7ad0f301b5e05d946ebd74af9a37548d77b9f125 [file] [log] [blame] [edit]
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2002-2018 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mDNSUNP.h"
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
/* Some weird platforms derived from 4.4BSD Lite (e.g. EFI) need the ALIGN(P)
macro, usually defined in <sys/param.h> or someplace like that, to make sure the
CMSG_NXTHDR macro is well-formed. On such platforms, the symbol NEED_ALIGN_MACRO
should be set to the name of the header to include to get the ALIGN(P) macro.
*/
#ifdef NEED_ALIGN_MACRO
#include NEED_ALIGN_MACRO
#endif
/* sockaddr_dl is only referenced if we're using IP_RECVIF,
so only include the header in that case.
*/
#ifdef IP_RECVIF
#include <net/if_dl.h>
#endif
ssize_t
recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,
struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl)
{
struct msghdr msg;
struct iovec iov[1];
ssize_t n;
#ifdef CMSG_FIRSTHDR
struct cmsghdr *cmptr;
union {
struct cmsghdr cm;
char control[1024];
} control_un;
*ttl = 255; // If kernel fails to provide TTL data then assume the TTL was 255 as it should be
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
msg.msg_flags = 0;
#else
memset(&msg, 0, sizeof(msg)); /* make certain msg_accrightslen = 0 */
#endif /* CMSG_FIRSTHDR */
msg.msg_name = (char *) sa;
msg.msg_namelen = *salenptr;
iov[0].iov_base = (char *)ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if ( (n = recvmsg(fd, &msg, *flagsp)) < 0)
return(n);
*salenptr = msg.msg_namelen; /* pass back results */
if (pktp) {
/* 0.0.0.0, i/f = -1 */
/* We set the interface to -1 so that the caller can
tell whether we returned a meaningful value or
just some default. Previously this code just
set the value to 0, but I'm concerned that 0
might be a valid interface value.
*/
memset(pktp, 0, sizeof(struct my_in_pktinfo));
pktp->ipi_ifindex = -1;
}
/* end recvfrom_flags1 */
/* include recvfrom_flags2 */
#ifndef CMSG_FIRSTHDR
#warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc.
*flagsp = 0; /* pass back results */
return(n);
#else
*flagsp = msg.msg_flags; /* pass back results */
if (msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr) ||
(msg.msg_flags & MSG_CTRUNC) || pktp == NULL)
return(n);
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;
cmptr = CMSG_NXTHDR(&msg, cmptr)) {
#ifdef IP_PKTINFO
#if in_pktinfo_definition_is_missing
struct in_pktinfo
{
int ipi_ifindex;
struct in_addr ipi_spec_dst;
struct in_addr ipi_addr;
};
#endif
if (cmptr->cmsg_level == IPPROTO_IP &&
cmptr->cmsg_type == IP_PKTINFO) {
struct in_pktinfo *tmp;
struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
tmp = (struct in_pktinfo *) CMSG_DATA(cmptr);
sin->sin_family = AF_INET;
sin->sin_addr = tmp->ipi_addr;
sin->sin_port = 0;
pktp->ipi_ifindex = tmp->ipi_ifindex;
continue;
}
#endif
#ifdef IP_RECVDSTADDR
if (cmptr->cmsg_level == IPPROTO_IP &&
cmptr->cmsg_type == IP_RECVDSTADDR) {
struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
sin->sin_family = AF_INET;
sin->sin_addr = *(struct in_addr*)CMSG_DATA(cmptr);
sin->sin_port = 0;
continue;
}
#endif
#ifdef IP_RECVIF
if (cmptr->cmsg_level == IPPROTO_IP &&
cmptr->cmsg_type == IP_RECVIF) {
struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA(cmptr);
#ifndef HAVE_BROKEN_RECVIF_NAME
int nameLen = (sdl->sdl_nlen < IFI_NAME - 1) ? sdl->sdl_nlen : (IFI_NAME - 1);
strncpy(pktp->ipi_ifname, sdl->sdl_data, nameLen);
#endif
pktp->ipi_ifindex = sdl->sdl_index;
#ifdef HAVE_BROKEN_RECVIF_NAME
if (sdl->sdl_index == 0) {
pktp->ipi_ifindex = *(uint_t*)sdl;
}
#endif
assert(pktp->ipi_ifname[IFI_NAME - 1] == 0);
// null terminated because of memset above
continue;
}
#endif
#ifdef IP_RECVTTL
if (cmptr->cmsg_level == IPPROTO_IP &&
cmptr->cmsg_type == IP_RECVTTL) {
*ttl = *(u_char*)CMSG_DATA(cmptr);
continue;
}
else if (cmptr->cmsg_level == IPPROTO_IP &&
cmptr->cmsg_type == IP_TTL) { // some implementations seem to send IP_TTL instead of IP_RECVTTL
*ttl = *(int*)CMSG_DATA(cmptr);
continue;
}
#endif
#if defined(IPV6_PKTINFO) && HAVE_IPV6
if (cmptr->cmsg_level == IPPROTO_IPV6 &&
cmptr->cmsg_type == IPV6_2292_PKTINFO) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr;
struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr);
sin6->sin6_family = AF_INET6;
#ifndef NOT_HAVE_SA_LEN
sin6->sin6_len = sizeof(*sin6);
#endif
sin6->sin6_addr = ip6_info->ipi6_addr;
sin6->sin6_flowinfo = 0;
sin6->sin6_scope_id = 0;
sin6->sin6_port = 0;
pktp->ipi_ifindex = ip6_info->ipi6_ifindex;
continue;
}
#endif
#if defined(IPV6_HOPLIMIT) && HAVE_IPV6
if (cmptr->cmsg_level == IPPROTO_IPV6 &&
cmptr->cmsg_type == IPV6_2292_HOPLIMIT) {
*ttl = *(int*)CMSG_DATA(cmptr);
continue;
}
#endif
assert(0); // unknown ancillary data
}
return(n);
#endif /* CMSG_FIRSTHDR */
}
// **********************************************************************************************
// daemonize the process. Adapted from "Unix Network Programming" vol 1 by Stevens, section 12.4.
// Returns 0 on success, -1 on failure.
#ifdef NOT_HAVE_DAEMON
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/signal.h>
int daemon(int nochdir, int noclose)
{
switch (fork())
{
case -1: return (-1); // Fork failed
case 0: break; // Child -- continue
default: _exit(0); // Parent -- exit
}
if (setsid() == -1) return(-1);
signal(SIGHUP, SIG_IGN);
switch (fork()) // Fork again, primarily for reasons of Unix trivia
{
case -1: return (-1); // Fork failed
case 0: break; // Child -- continue
default: _exit(0); // Parent -- exit
}
if (!nochdir) (void)chdir("/");
umask(0);
if (!noclose)
{
int fd = open("/dev/null", O_RDWR, 0);
if (fd != -1)
{
// Avoid unnecessarily duplicating a file descriptor to itself
if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO);
if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO);
if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO);
if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
(void)close (fd);
}
}
return (0);
}
#endif /* NOT_HAVE_DAEMON */