Merge pull request #90 from rneugeba/winupstream

Winupstream
This commit is contained in:
Justin Cormack 2016-04-15 15:49:24 +01:00
commit 8eaf2976b8
39 changed files with 2369 additions and 52 deletions

View File

@ -16,7 +16,8 @@ RUN \
bind-tools \
openssh-client \
strace fuse \
util-linux
util-linux \
cifs-utils
COPY etc /etc/
RUN mkdir -p /etc/docker
@ -32,6 +33,9 @@ COPY packages/transfused/transfused /sbin/
COPY packages/transfused/etc /etc/
COPY packages/mdnstool/mdnstool /sbin/
COPY packages/mdnstool/etc /etc/
COPY packages/llmnrd/llmnrd /sbin/
COPY packages/llmnrd/etc /etc/
COPY packages/llmnrd/llmnrd.tar.gz /usr/share/src/
COPY packages/docker/bin/* /usr/bin/
COPY packages/docker/etc /etc/
COPY packages/diagnostics/diagnostics /usr/bin/
@ -45,13 +49,14 @@ COPY packages/dnsfix/etc /etc/
COPY packages/hostsettings/etc /etc/
COPY packages/hupper/hupper /bin/
COPY packages/hupper/etc /etc/
COPY packages/hvtools/hv_fcopy_daemon /sbin/
COPY packages/hvtools/hv_kvp_daemon /sbin/
COPY packages/hvtools/hv_vss_daemon /sbin/
COPY packages/hvtools/etc /etc/
COPY packages/hvtools/hv_get_dhcp_info /sbin/
COPY packages/hvtools/hv_get_dns_info /sbin/
COPY packages/hvtools/hv_set_ifconfig /sbin/
COPY packages/hvtools/hvtools.tar.gz /usr/share/src/
COPY packages/chronyd/etc /etc/
COPY packages/userns/etc /etc/
COPY packages/userns/groupadd /usr/sbin
COPY packages/userns/useradd /usr/sbin
@ -87,13 +92,13 @@ RUN \
rc-update add proxy default && \
rc-update add transfused default && \
rc-update add mdnstool default && \
rc-update add llmnrd default && \
rc-update add automount boot && \
rc-update add diagnostics default && \
rc-update add binfmt_misc sysinit && \
rc-update add dnsfix boot && \
rc-update add hostsettings boot && \
rc-update add hupper default && \
rc-update add hv_fcopy_daemon default && \
rc-update add hv_kvp_daemon default && \
rc-update add hv_vss_daemon default && \
rc-update add vsudd default

View File

@ -1,4 +1,4 @@
FROM debian:jessie
FROM ubuntu:15.10
RUN apt-get update && apt-get -y upgrade && apt-get -y install \
genisoimage \

View File

@ -2533,7 +2533,10 @@ CONFIG_NETWORK_FILESYSTEMS=y
# CONFIG_NFS_FS is not set
# CONFIG_NFSD is not set
# CONFIG_CEPH_FS is not set
# CONFIG_CIFS is not set
CONFIG_CIFS=y
CONFIG_CIFS_XATTR=y
CONFIG_CIFS_POSIX=y
CONFIG_CIFS_SMB2=y
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set

View File

@ -7,6 +7,7 @@ all:
$(MAKE) -C docker OS=Linux
$(MAKE) -C nc-vsock OS=linux
$(MAKE) -C vsudd OS=linux
$(MAKE) -C llmnrd OS=linux
arm:
$(MAKE) -C proxy OS=linux ARCH=arm
@ -26,3 +27,4 @@ clean:
$(MAKE) -C hvtools clean
$(MAKE) -C nc-vsock clean
$(MAKE) -C vsudd clean
$(MAKE) -C llmnrd clean

View File

@ -0,0 +1,73 @@
#!/sbin/openrc-run
# Copyright 1999-2007 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/cvsroot/gentoo-x86/net-misc/chrony/files/chronyd.rc,v 1.8 2007/03/22 14:32:09 tove Exp $
description="NTP daemon"
depend() {
need net
after firewall
provide ntp-client ntp-server
use dns
}
checkconfig() {
# Note that /etc/chrony/chrony.keys is *NOT* checked. This
# is because the user may have specified another key
# file, and we don't want to force the user to use that
# exact name for the key file.
if [ ! -f "${CFGFILE}" ] ; then
eerror "Please create ${CFGFILE} and the"
eerror "chrony key file (usually /etc/chrony/chrony.keys)"
eerror "by using the"
eerror ""
eerror " chrony.conf.example"
eerror " chrony.keys.example"
eerror ""
eerror "files (from the documentation directory)"
eerror "as templates."
return 1
else
# Actually, I tried it, and chrony seems to ignore the pidfile
# option. I'm going to leave it here anyway, since you never
# know if it might be handy
PIDFILE=`awk '/^ *pidfile/{print $2}' "${CFGFILE}"`
fi
return 0
}
setxtrarg() {
if [ -c /dev/rtc ]; then
grep -q '^rtcfile' "${CFGFILE}" && ARGS="${ARGS} -s"
fi
grep -q '^dumponexit$' "${CFGFILE}" && ARGS="${ARGS} -r"
return 0
}
start() {
[ -d /sys/bus/vmbus ] && exit 0
checkconfig || return $?
setxtrarg
[ -n "${PIDFILE}" ] || PIDFILE=/var/run/chronyd.pid
ebegin "Starting chronyd"
start-stop-daemon --start --quiet \
--exec /usr/sbin/chronyd \
--pidfile "${PIDFILE}" \
-- -f "${CFGFILE}" ${ARGS}
eend $? "Failed to start chronyd"
}
stop() {
[ -d /sys/bus/vmbus ] && exit 0
checkconfig || return $?
[ -n "${PIDFILE}" ] || PIDFILE=/var/run/chronyd.pid
ebegin "Stopping chronyd"
start-stop-daemon --stop --quiet \
--pidfile "${PIDFILE}"
eend $? "Failed to stop chronyd"
}

View File

@ -174,7 +174,7 @@ stop()
eoutdent
# now terminate with extreme prejudice
halt -f
poweroff -f
return 0
}

View File

@ -1,3 +1,4 @@
hv_fcopy_daemon
hv_kvp_daemon
hv_vss_daemon
/hv_fcopy_daemon
/hv_kvp_daemon
/hv_vss_daemon
/hvtools.tar.gz

View File

@ -3,8 +3,9 @@ FROM alpine:3.3
RUN apk update && apk upgrade && apk add build-base linux-headers
RUN mkdir -p /hvtools
WORKDIR /hvtools
COPY src /hvtools/
RUN tar czvf /hvtools.tar.gz hvtools
WORKDIR /hvtools
RUN make

View File

@ -2,6 +2,7 @@ all: hvtools
hvtools: Dockerfile src/*
docker build -t hvtools:build .
docker run --rm hvtools:build cat /hvtools.tar.gz > hvtools.tar.gz
docker run --rm hvtools:build cat /hvtools/hv_fcopy_daemon > hv_fcopy_daemon
docker run --rm hvtools:build cat /hvtools/hv_kvp_daemon > hv_kvp_daemon
docker run --rm hvtools:build cat /hvtools/hv_vss_daemon > hv_vss_daemon

View File

@ -1,32 +0,0 @@
#!/sbin/openrc-run
HV_DAEMON=hv_fcopy_daemon
start()
{
[ ! -d /sys/bus/vmbus ] && exit 0
ebegin "Starting Hyper-V Daemon: ${HV_DAEMON}"
[ -n "${PIDFILE}" ] || PIDFILE=/var/run/${HV_DAEMON}.pid
start-stop-daemon --start --quiet \
--background \
--exec /sbin/${HV_DAEMON} \
--make-pidfile --pidfile ${PIDFILE} \
--
eend 0
}
stop()
{
[ ! -d /sys/bus/vmbus ] && exit 0
ebegin "Stopping Hyper-V Daemon: ${HV_DAEMON}"
[ -n "${PIDFILE}" ] || PIDFILE=/var/run/${HV_DAEMON}.pid
start-stop-daemon --stop --quiet --pidfile ${PIDFILE}
eend $? "Failed to stop ${HV_DAEMON}"
}

View File

@ -2,6 +2,12 @@
HV_DAEMON=hv_kvp_daemon
depend()
{
after dev
needs networking
}
start()
{
[ ! -d /sys/bus/vmbus ] && exit 0

View File

@ -2,6 +2,11 @@
HV_DAEMON=hv_vss_daemon
depend()
{
after dev
}
start()
{
[ ! -d /sys/bus/vmbus ] && exit 0

View File

@ -58,9 +58,6 @@
# Stash away a copy of the config we got
cp $1 /etc/network/hv_config
# This is where we write the configuration
outf=/etc/network/interfaces
# Source it
. $1
@ -68,6 +65,7 @@ outf=/etc/network/interfaces
ifdown -f $DEVICE
# create a new interfaces file
outf=/etc/network/interfaces
echo "auto lo" > $outf
echo "iface lo inet loopback" >> $outf
echo "" >> $outf
@ -80,10 +78,16 @@ else
echo " address $IPADDR0" >> $outf
echo " netmask $NETMASK0" >> $outf
echo " gateway $GATEWAY" >> $outf
echo " nameserver $DNS1 $DNS2 $DNS3" >> $outf
fi
echo " hwaddress ether $HWADDR" >> $outf
# create a new resolve.conf file
outf=/etc/resolv.conf
[ -n "$DNS1" ] && echo "nameserver $DNS1" > $outf
[ -n "$DNS2" ] && echo "nameserver $DNS2" >> $outf
[ -n "$DNS3" ] && echo "nameserver $DNS3" >> $outf
[ -n "$DNS4" ] && echo "nameserver $DNS4" >> $outf
# Up the interface
ifup $DEVICE

2
alpine/packages/llmnrd/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/llmnrd
/llmnrd.tar.gz

View File

@ -0,0 +1,11 @@
FROM alpine:3.3
RUN apk update && apk upgrade && apk add alpine-sdk linux-headers
RUN mkdir -p /llmnrd
COPY src /llmnrd/
RUN tar czvf /llmnrd.tar.gz llmnrd
WORKDIR /llmnrd
RUN make

View File

@ -0,0 +1,11 @@
all: llmnrd
llmnrd: Dockerfile src/*
docker build -t llmnrd:build .
docker run --rm llmnrd:build cat /llmnrd.tar.gz > llmnrd.tar.gz
docker run --rm llmnrd:build cat /llmnrd/llmnrd > llmnrd
chmod 755 llmnrd
clean:
rm -f llmnrd llmnrd.tar.gz
docker images -q llmnrd:build | xargs docker rmi -f

View File

@ -0,0 +1,41 @@
#!/sbin/openrc-run
description="LLMNR Daemon"
depend()
{
need net
after firewall
}
start()
{
[ ! -d /sys/bus/vmbus ] && exit 0
ebegin "Starting LLMNR Daemon"
[ -n "${PIDFILE}" ] || PIDFILE=/var/run/llmnrd.pid
export HOSTNAME=$(hostname -s)
start-stop-daemon --start --quiet \
--background \
--exec /sbin/llmnrd \
--make-pidfile --pidfile ${PIDFILE} \
-- -H ${HOSTNAME} -p 5355 -6
eend $? "Failed to start LLMNR Dameon"
}
stop()
{
[ ! -d /sys/bus/vmbus ] && exit 0
ebegin "Stopping LLMNR Daemon"
[ -n "${PIDFILE}" ] || PIDFILE=/var/run/llmnrd.pid
start-stop-daemon --stop --quiet --pidfile ${PIDFILE}
eend $? "Failed to stop LLMNR Daemon"
}

View File

@ -0,0 +1,74 @@
# Makefile for llmnrd
#
# Copyright (C) 2014-2015 Tobias Klauser <tklauser@distanz.ch>
VERSION = 0.1-rc1
# llmnrd binary
D_P = llmnrd
D_OBJS = llmnr.o iface.o socket.o util.o llmnrd.o
D_LIBS = -lpthread
# llmnr-query binary
Q_P = llmnr-query
Q_OBJS = util.o llmnr-query.o
Q_LIBS =
CC = $(CROSS_COMPILE)gcc
INSTALL = install
CFLAGS ?= -W -Wall -O2
LDFLAGS ?=
ifeq ($(shell git rev-parse > /dev/null 2>&1; echo $$?), 0)
GIT_VERSION = "(git id $(shell git describe --always))"
else
GIT_VERSION =
endif
CFLAGS += -DVERSION_STRING=\"v$(VERSION)\" -DGIT_VERSION=\"$(GIT_VERSION)\"
ifeq ($(DEBUG), 1)
CFLAGS += -g -DDEBUG
endif
Q ?= @
CCQ = $(Q)echo " CC $<" && $(CC)
LDQ = $(Q)echo " LD $@" && $(CC)
prefix ?= /usr/local
BINDIR = $(prefix)/bin
SBINDIR = $(prefix)/sbin
DESTDIR =
all: $(D_P) $(Q_P)
$(D_P): $(D_OBJS)
$(LDQ) $(LDFLAGS) -o $@ $(D_OBJS) $(D_LIBS)
$(Q_P): $(Q_OBJS)
$(LDQ) $(LDFLAGS) -o $@ $(Q_OBJS) $(Q_LIBS)
%.o: %.c %.h
$(CCQ) $(CFLAGS) -o $@ -c $<
%.o: %.c
$(CCQ) $(CFLAGS) -o $@ -c $<
install_$(D_P): $(D_P)
@echo " INSTALL $(D_P)"
@$(INSTALL) -d -m 755 $(DESTDIR)$(SBINDIR)
@$(INSTALL) -m 755 $(D_P) $(DESTDIR)$(SBINDIR)/$(D_P)
install_$(Q_P): $(Q_P)
@echo " INSTALL $(Q_P)"
@$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
@$(INSTALL) -m 755 $(Q_P) $(DESTDIR)$(BINDIR)/$(Q_P)
install: install_$(D_P) install_$(Q_P)
clean:
@echo " CLEAN"
@rm -f $(D_OBJS) $(D_P)
@rm -f $(Q_OBJS) $(Q_P)

View File

@ -0,0 +1,66 @@
llmnrd - Link-Local Multicast Resolution Daemon
===============================================
llmnrd is a daemon implementing the Link-Local Multicast Name Resolution (LLMNR)
protocol according to RFC 4795. It currently only supports Linux, as it uses the
netlink kernel interface.
llmnrd will respond to name resolution queries sent by Windows clients in
networks where no DNS server is available. It supports both IPv4 and IPv6.
Installation
------------
To build and install llmnrd use the following commands:
$ make
$ sudo make install
By default, the llmnrd binary will be installed to /usr/local/sbin. To install
the binary to a different installation path, use:
$ make
$ sudo make prefix=<path> install
Cross-Compilation
-----------------
To cross-compile llmnrd for a different architecture, use the CROSS_COMPILE make
variable. To e.g. build it using the arm-linux-gnueabihf toolchain use:
$ make CROSS_COMPILE=arm-linux-gnueabihf-
When cross-compiling, you usually don't want to install the generated binary to
your root filesystem, but to the sysroot of a cross-compiled system. Use the
DESTDIR variable to change the installation destination path, e.g.
$ make DESTDIR=$HOME/sysroot/ prefix=/usr install
License
-------
llmnrd 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 of the License.
llmnrd 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.
Author
------
llmnrd is authored and maintained by Tobias Klauser <tklauser@distanz.ch>
References
----------
RFC 4795
https://tools.ietf.org/html/rfc4795
Microsoft TechNet article about LLMNR
https://technet.microsoft.com/en-us/library/bb878128.aspx
xllmnrd: An IPv6-only LLMNR responder daemon
http://www.vx68k.org/xllmnrd
https://bitbucket.org/kazssym/xllmnrd/

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMPILER_H
#define COMPILER_H
#ifdef __GNUC__
# define __noreturn __attribute__((noreturn))
# define __warn_unused_result __attribute__((warn_unused_result))
# define __packed __attribute__((packed))
# define __unused __attribute__((unused))
# ifndef offsetof
# define offsetof(a, b) __builtin_offsetof(a, b)
# endif
#else
# define __noreturn
# define __packed
# define __unused
#endif
#ifndef offsetof
# define offsetof(type, member) ((size_t) &((type *)0)->member)
#endif
#ifndef container_of
# define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member));})
#endif
#endif /* COMPILER_H */

View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ERR_H
#define ERR_H
static inline void *ERR_PTR(long err)
{
return (void *)err;
}
static inline long PTR_ERR(const void *ptr)
{
return (long)ptr;
}
#endif /* ERR_H */

View File

@ -0,0 +1,395 @@
/*
* Copyright (C) 2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "err.h"
#include "list.h"
#include "log.h"
#include "socket.h"
#include "util.h"
#include "iface.h"
static bool iface_running = true;
static pthread_t iface_thread;
static iface_event_handler_t iface_event_handler;
struct iface_record {
struct list_head list;
unsigned int index;
struct sockaddr_storage *addrs;
size_t size;
};
static struct list_head iface_list_head;
static pthread_mutex_t iface_list_mutex;
size_t iface_addr_lookup(unsigned int ifindex, unsigned char family,
struct sockaddr_storage *addrs, size_t addrs_size)
{
struct iface_record *rec;
size_t n = 0;
if (!addrs)
return 0;
pthread_mutex_lock(&iface_list_mutex);
list_for_each_entry(rec, &iface_list_head, list) {
if (rec->index == ifindex) {
size_t i;
for (i = 0; i < rec->size && n < addrs_size; i++) {
if (family == AF_UNSPEC || family == rec->addrs[i].ss_family) {
memcpy(&addrs[n], &rec->addrs[i], sizeof(addrs[n]));
n++;
}
}
break;
}
}
pthread_mutex_unlock(&iface_list_mutex);
return n;
}
static bool iface_record_addr_eq(const struct sockaddr_storage *addr1,
const struct sockaddr_storage *addr2)
{
int family = addr1->ss_family;
if (family != addr2->ss_family)
return false;
if (family == AF_INET) {
const struct sockaddr_in *sin1 = (const struct sockaddr_in *)addr1;
const struct sockaddr_in *sin2 = (const struct sockaddr_in *)addr2;
return memcmp(&sin1->sin_addr, &sin2->sin_addr, sizeof(sin1->sin_addr)) == 0;
} else if (family == AF_INET6) {
const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)addr1;
const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)addr2;
return memcmp(&sin1->sin6_addr, &sin2->sin6_addr, sizeof(sin1->sin6_addr)) == 0;
} else {
/* This should never happen */
log_warn("Unsupported address family: %d\n", family);
return memcmp(addr1, addr2, sizeof(*addr1));
}
}
static void iface_record_addr_add(struct iface_record *rec, struct sockaddr_storage *addr)
{
size_t i;
struct sockaddr_storage *addrs = rec->addrs;
for (i = 0; i < rec->size; i++) {
/* Address already in record? */
if (iface_record_addr_eq(&addrs[i], addr))
return;
}
addrs = xrealloc(rec->addrs, (rec->size + 1) * sizeof(*addr));
memcpy(&addrs[rec->size], addr, sizeof(*addr));
rec->addrs = addrs;
rec->size++;
}
static void iface_record_addr_del(struct iface_record *rec, struct sockaddr_storage *addr)
{
if (rec->size > 1) {
size_t i, j = 0;
struct sockaddr_storage *addrs = xmalloc((rec->size - 1) * sizeof(*addr));
for (i = 0; i < rec->size; i++) {
if (!iface_record_addr_eq(&rec->addrs[i], addr)) {
memcpy(&addrs[j], &rec->addrs[i], sizeof(addrs[j]));
j++;
}
}
if (j == i - 1) {
free(rec->addrs);
rec->addrs = addrs;
rec->size--;
} else {
char as[INET6_ADDRSTRLEN];
inet_ntop(addr->ss_family, addr + sizeof(addr->ss_family), as, sizeof(as));
log_err("Address %s to delete not found in records\n", as);
}
} else if (rec->size == 1) {
free(rec->addrs);
rec->addrs = NULL;
rec->size = 0;
}
}
static inline void fill_sockaddr_storage(struct sockaddr_storage *sst,
unsigned char family, const void *addr)
{
sst->ss_family = family;
if (family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)sst;
memcpy(&sin->sin_addr, addr, sizeof(sin->sin_addr));
} else if (family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sst;
memcpy(&sin6->sin6_addr, addr, sizeof(sin6->sin6_addr));
}
}
static void iface_addr_add(unsigned int index, unsigned char family, const void *addr)
{
struct iface_record *rec;
struct sockaddr_storage sst;
fill_sockaddr_storage(&sst, family, addr);
pthread_mutex_lock(&iface_list_mutex);
list_for_each_entry(rec, &iface_list_head, list)
if (rec->index == index)
goto add;
rec = xzalloc(sizeof(*rec));
INIT_LIST_HEAD(&rec->list);
rec->index = index;
list_add_tail(&rec->list, &iface_list_head);
add:
iface_record_addr_add(rec, &sst);
pthread_mutex_unlock(&iface_list_mutex);
}
static void iface_addr_del(unsigned int index, unsigned char family, const void *addr)
{
struct iface_record *rec;
struct sockaddr_storage sst;
fill_sockaddr_storage(&sst, family, addr);
pthread_mutex_lock(&iface_list_mutex);
list_for_each_entry(rec, &iface_list_head, list) {
if (rec->index == index) {
iface_record_addr_del(rec, &sst);
break;
}
}
pthread_mutex_unlock(&iface_list_mutex);
}
static void iface_nlmsg_change_link(const struct nlmsghdr *nlh __unused)
{
/* TODO */
}
static void iface_nlmsg_change_addr(const struct nlmsghdr *nlh)
{
struct ifaddrmsg *ifa = NLMSG_DATA(nlh);
struct rtattr *rta;
size_t rtalen = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*ifa));
unsigned char family = ifa->ifa_family;
unsigned int index = ifa->ifa_index;
char ifname[IF_NAMESIZE];
/* don't report temporary addresses */
if ((ifa->ifa_flags & (IFA_F_TEMPORARY | IFA_F_TENTATIVE)) != 0)
return;
if_indextoname(index, ifname);
rta = (struct rtattr *)((const uint8_t *)nlh + NLMSG_SPACE(sizeof(*ifa)));
for ( ; RTA_OK(rta, rtalen); rta = RTA_NEXT(rta, rtalen)) {
char addr[INET6_ADDRSTRLEN];
enum iface_event_type type;
if (rta->rta_type != IFA_ADDRESS)
continue;
if (!inet_ntop(family, RTA_DATA(rta), addr, sizeof(addr)))
strncpy(addr, "<unknown>", sizeof(addr) - 1);
if (nlh->nlmsg_type == RTM_NEWADDR) {
iface_addr_add(index, family, RTA_DATA(rta));
type = IFACE_ADD;
} else if (nlh->nlmsg_type == RTM_DELADDR) {
iface_addr_del(index, family, RTA_DATA(rta));
type = IFACE_DEL;
} else {
/* This case shouldn't occur */
continue;
}
if (iface_event_handler)
(*iface_event_handler)(type, family, index);
log_info("%s IPv%c address %s on interface %s\n",
type == IFACE_ADD ? "Added" : "Deleted",
family == AF_INET ? '4' : '6', addr, ifname);
}
}
static int iface_nlmsg_process(const struct nlmsghdr *nlh, size_t len)
{
for ( ; len > 0; nlh = NLMSG_NEXT(nlh, len)) {
struct nlmsgerr *err;
if (!NLMSG_OK(nlh, len)) {
log_err("netlink message truncated\n");
return -1;
}
switch (nlh->nlmsg_type) {
case RTM_NEWADDR:
case RTM_DELADDR:
iface_nlmsg_change_addr(nlh);
break;
case RTM_NEWLINK:
case RTM_DELLINK:
iface_nlmsg_change_link(nlh);
break;
case NLMSG_ERROR:
err = NLMSG_DATA(nlh);
log_err("netlink error: %s\n", strerror(-(err->error)));
break;
case NLMSG_DONE:
if (!NLMSG_OK(nlh, len)) {
log_err("netlink message truncated\n");
return -1;
} else
return 0;
default:
/* log_warn("Unknown netlink message type: 0x%x\n", nlh->nlmsg_type); */
break;
}
}
return 0;
}
static int iface_rtnl_enumerate(int sock, uint16_t type, unsigned char family)
{
struct {
struct nlmsghdr n;
struct rtgenmsg r;
} req;
ssize_t recvlen;
uint8_t pktbuf[8192];
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r));
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.n.nlmsg_type = type;
req.r.rtgen_family = family;
if (send(sock, &req, req.n.nlmsg_len, 0) < 0) {
log_err("Failed to send netlink enumeration message: %s\n", strerror(errno));
return -1;
}
if ((recvlen = recv(sock, pktbuf, sizeof(pktbuf), 0)) < 0) {
if (errno != EINTR)
log_err("Failed to receive netlink message: %s\n", strerror(errno));
return -1;
}
return iface_nlmsg_process((const struct nlmsghdr *)pktbuf, recvlen);
}
void iface_register_event_handler(iface_event_handler_t event_handler)
{
iface_event_handler = event_handler;
}
int iface_run(void)
{
int ret = -1;
int sock;
INIT_LIST_HEAD(&iface_list_head);
if (pthread_mutex_init(&iface_list_mutex, NULL) != 0) {
log_err("Failed to initialize interface list mutex\n");
return -1;
}
sock = socket_open_rtnl();
if (sock < 0)
return -1;
/* send RTM_GETADDR request to initially populate the interface list */
if (iface_rtnl_enumerate(sock, RTM_GETADDR, AF_INET) < 0)
return -1;
if (iface_rtnl_enumerate(sock, RTM_GETADDR, AF_INET6) < 0)
return -1;
while (iface_running) {
ssize_t recvlen;
uint8_t pktbuf[8192];
if ((recvlen = recv(sock, pktbuf, sizeof(pktbuf), 0)) < 0) {
if (errno != EINTR)
log_err("Failed to receive netlink message: %s\n", strerror(errno));
goto out;
}
if (iface_nlmsg_process((const struct nlmsghdr *)pktbuf, recvlen) < 0)
log_warn("Error processing netlink message\n");
}
pthread_mutex_destroy(&iface_list_mutex);
ret = 0;
out:
close(sock);
return ret;
}
static void* iface_run_wrapper(void *data __unused)
{
return ERR_PTR(iface_run());
}
int iface_start_thread(void)
{
if (pthread_create(&iface_thread, NULL, iface_run_wrapper, NULL) < 0) {
log_err("Failed to start interface monitoring thread\n");
return -1;
}
return 0;
}
void iface_stop(void)
{
iface_running = false;
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IFACE_H
#define IFACE_H
#include <sys/socket.h>
enum iface_event_type {
IFACE_ADD,
IFACE_DEL,
};
typedef void (*iface_event_handler_t)(enum iface_event_type, unsigned char af,
unsigned int ifindex);
void iface_register_event_handler(iface_event_handler_t event_handler);
int iface_start_thread(void);
void iface_stop(void);
size_t iface_addr_lookup(unsigned int ifindex, unsigned char family,
struct sockaddr_storage *addrs, size_t addrs_size);
#endif /* IFACE_H */

View File

@ -0,0 +1,82 @@
/*
* Simple doubly linked list, based on the Linux kernel linked list.
*
* Copyright (C) 2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIST_H
#define LIST_H
#include <stdbool.h>
#include "compiler.h"
struct list_head {
struct list_head *next, *prev;
};
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
static inline void __list_add(struct list_head *obj,
struct list_head *prev,
struct list_head *next)
{
prev->next = obj;
obj->prev = prev;
obj->next = next;
next->prev = obj;
}
static inline void list_add_tail(struct list_head *obj, struct list_head *head)
{
__list_add(obj, head->prev, head);
}
static inline void list_add_head(struct list_head *obj, struct list_head *head)
{
__list_add(obj, head, head->next);
}
static inline void list_del(struct list_head *obj)
{
obj->next->prev = obj->prev;
obj->prev->next = obj->next;
}
static inline bool list_empty(struct list_head *head)
{
return head->next == head;
}
#define list_entry(ptr, type, member) container_of(ptr, type, member)
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&(pos)->member != (head); \
(pos) = list_entry((pos)->member.next, typeof(*(pos)), member))
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&(pos)->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
#endif /* LIST_H */

View File

@ -0,0 +1,80 @@
/*
* LLMNR (RFC 4705) packet format definitions
*
* Copyright (C) 2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LLMNR_PACKET_H
#define LLMNR_PACKET_H
#include <stdint.h>
#include "compiler.h"
#define LLMNR_IPV4_MCAST_ADDR "224.0.0.252"
#define LLMNR_IPV6_MCAST_ADDR "ff02:0:0:0:0:0:1:3"
#define LLMNR_UDP_PORT 5355
/*
* LLMNR packet header (RFC 4795, section 2.1.1)
*/
struct llmnr_hdr {
uint16_t id;
uint16_t flags;
#define LLMNR_F_QR 0x8000
#define LLMNR_F_OPCODE 0x7800
#define LLMNR_F_C 0x0400
#define LLMNR_F_TC 0x0200
#define LLMNR_F_T 0x0100
#define LLMNR_F_RCODE 0x000f
uint16_t qdcount;
uint16_t ancount;
uint16_t nscount;
uint16_t arcount;
} __packed;
/* Maximum label length according to RFC 1035 */
#define LLMNR_LABEL_MAX_SIZE 63
/* TYPE values according to RFC1035, section 3.2.2 */
#define LLMNR_TYPE_A 1
#define LLMNR_TYPE_NS 2
#define LLMNR_TYPE_CNAME 5
#define LLMNR_TYPE_SOA 6
#define LLMNR_TYPE_PTR 12
#define LLMNR_TYPE_HINFO 13
#define LLMNR_TYPE_MINFO 14
#define LLMNR_TYPE_MX 15
#define LLMNR_TYPE_TXT 16
#define LLMNR_TYPE_AAAA 28 /* RFC 3596 */
/* QTYPE values according to RFC1035, section 3.2.3 */
#define LLMNR_QTYPE_A LLMNR_TYPE_A
#define LLMNR_QTYPE_AAAA LLMNR_TYPE_AAAA
#define LLMNR_QTYPE_ANY 255
/* CLASS values */
#define LLMNR_CLASS_IN 1
/* QCLASS values */
#define LLMNR_QCLASS_IN LLMNR_CLASS_IN
/* Default RR TTL in seconds (RFC 4795, section 2.8) */
#define LLMNR_TTL_DEFAULT 30
#endif /* LLMNR_PACKET_H */

View File

@ -0,0 +1,315 @@
/*
* Simple LLMNR query command.
*
* Copyright (C) 2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <getopt.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include "compiler.h"
#include "llmnr-packet.h"
#include "log.h"
#include "pkt.h"
static const char *short_ops = "c:i:I:t:T:6hV";
static const struct option long_opts[] = {
{ "count", required_argument, NULL, 'c' },
{ "interval", required_argument, NULL, 'i' },
{ "interface", required_argument, NULL, 'I' },
{ "timeout", required_argument, NULL, 't' },
{ "type", required_argument, NULL, 'T' },
{ "ipv6", no_argument, NULL, '6' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 },
};
static void __noreturn usage_and_exit(int status)
{
fprintf(stdout, "Usage: llmnr-query [OPTIONS...] NAME\n"
"Options:\n"
" -c, --count NUM number of queries to send (default: 1)\n"
" -i, --interval NUM interval between queries in ms (default: 500)\n"
" -I, --interface NAME send multicast over specified interface\n"
" -t, --timeout NUM time to wait for reply in ms (default: 1000)\n"
" -T, --type TYPE set query type; must be one of A, AAAA, ANY (default: A)\n"
" -6, --ipv6 send queries over IPv6\n"
" -h, --help show this help and exit\n"
" -V, --version show version information and exit\n");
exit(status);
}
static void __noreturn version_and_exit(void)
{
fprintf(stdout, "llmnr-query %s %s\n"
"Copyright (C) 2015 Tobias Klauser <tklauser@distanz.ch>\n"
"Licensed under the GNU General Public License, version 2\n",
VERSION_STRING, GIT_VERSION);
exit(EXIT_SUCCESS);
}
static const char *query_type(uint16_t qtype)
{
switch (qtype) {
case LLMNR_QTYPE_A:
return "A";
case LLMNR_QTYPE_AAAA:
return "AAAA";
case LLMNR_QTYPE_ANY:
return "ANY";
default:
return "<unknown>";
}
}
int main(int argc, char **argv)
{
int c, sock;
const char *query_name, *iface = NULL;
size_t query_name_len;
unsigned long i, count = 1, interval_ms = 500, timeout_ms = 1000;
uint16_t qtype = LLMNR_QTYPE_A;
bool ipv6 = false;
struct pkt *p;
while ((c = getopt_long(argc, argv, short_ops, long_opts, NULL)) != -1) {
switch (c) {
case 'c':
count = strtoul(optarg, NULL, 0);
break;
case 'i':
interval_ms = strtoul(optarg, NULL, 0);
break;
case 'I':
iface = xstrdup(optarg);
break;
case 't':
timeout_ms = strtoul(optarg, NULL, 0);
break;
case 'T':
if (xstreq("A", optarg))
qtype = LLMNR_QTYPE_A;
else if (xstreq("AAAA", optarg))
qtype = LLMNR_QTYPE_AAAA;
else if (xstreq("ANY", optarg))
qtype = LLMNR_QTYPE_ANY;
else {
printf("Invalid query type: %s\n", optarg);
usage_and_exit(EXIT_FAILURE);
}
break;
case '6':
ipv6 = true;
break;
case 'V':
version_and_exit();
case 'h':
usage_and_exit(EXIT_SUCCESS);
default:
usage_and_exit(EXIT_FAILURE);
}
}
if (optind >= argc)
usage_and_exit(EXIT_FAILURE);
query_name = argv[optind];
query_name_len = strlen(query_name);
if (query_name_len > UINT8_MAX) {
log_err("Query name too long\n");
return -1;
}
sock = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
log_err("Failed to open UDP socket: %s\n", strerror(errno));
return -1;
}
if (iface != NULL) {
unsigned int ifindex = if_nametoindex(iface);
if (ifindex == 0 && errno != 0) {
log_err("Could not get interface %s: %s\n", iface, strerror(errno));
goto err;
}
if (ipv6) {
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0) {
log_err("Failed to set interface '%s' for IPv6 multicast socket: %s\n",
iface, strerror(errno));
goto err;
}
} else {
struct ip_mreqn mreq;
memset(&mreq, 0, sizeof(mreq));
mreq.imr_ifindex = ifindex;
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0) {
log_err("Failed to set interface '%s' for IPv4 multicast socket: %s\n",
iface, strerror(errno));
goto err;
}
}
}
p = pkt_alloc(128);
log_info("LLMNR query: %s IN %s\n", query_name, query_type(qtype));
for (i = 0; i < count; i++) {
struct llmnr_hdr *hdr;
struct sockaddr_storage sst;
size_t query_pkt_len;
fd_set rfds;
struct timeval tv;
int ret;
hdr = (struct llmnr_hdr *)pkt_put(p, sizeof(*hdr));
hdr->id = htons(i % UINT16_MAX);
hdr->flags = 0;
hdr->qdcount = htons(1);
hdr->ancount = 0;
hdr->nscount = 0;
hdr->arcount = 0;
pkt_put_u8(p, (uint8_t)query_name_len);
memcpy(pkt_put(p, query_name_len), query_name, query_name_len);
pkt_put_u8(p, 0);
pkt_put_u16(p, htons(qtype));
pkt_put_u16(p, htons(LLMNR_QCLASS_IN));
memset(&sst, 0, sizeof(sst));
if (ipv6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&sst;
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(LLMNR_UDP_PORT);
if (inet_pton(AF_INET6, LLMNR_IPV6_MCAST_ADDR, &sin6->sin6_addr) != 1) {
log_err("Failed to convert IPv6 address: %s\n", strerror(errno));
break;
}
} else {
struct sockaddr_in *sin = (struct sockaddr_in *)&sst;
sin->sin_family = AF_INET;
sin->sin_port = htons(LLMNR_UDP_PORT);
if (inet_pton(AF_INET, LLMNR_IPV4_MCAST_ADDR, &sin->sin_addr) != 1) {
log_err("Failed to convert IPv4 address: %s\n", strerror(errno));
break;
}
}
query_pkt_len = pkt_len(p) - sizeof(*hdr);
if (sendto(sock, p->data, pkt_len(p), 0, (struct sockaddr *)&sst, sizeof(sst)) < 0) {
log_err("Failed to send UDP packet: %s\n", strerror(errno));
break;
}
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
/* wait up to one second for a response */
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
ret = select(sock + 1, &rfds, NULL, NULL, &tv);
if (ret < 0) {
log_err("Failed to select() on socket: %s\n", strerror(errno));
break;
} else if (ret) {
uint16_t j, ancount;
pkt_reset(p);
if (recv(sock, p->data, p->size, 0) < 0) {
log_err("Failed to receive from socket: %s\n", strerror(errno));
break;
}
hdr = (struct llmnr_hdr *)pkt_put(p, sizeof(*hdr));
ancount = htons(hdr->ancount);
if (ancount == 0) {
log_info("LLMNR response: no answer records returned\n");
continue;
}
/* skip the original query */
pkt_put(p, query_pkt_len);
for (j = 0; j < ancount; ++j) {
uint8_t nl = *pkt_put(p, 1);
char addr[INET6_ADDRSTRLEN];
uint16_t type, clss, addr_size;
uint32_t ttl;
char *name;
int af;
/* compression? */
if (nl & 0xC0) {
uint16_t ptr = (nl & 0x3F) << 8 | *pkt_put(p, 1);
name = (char *)p->data + ptr + 1;
} else
name = (char *)pkt_put(p, nl + 1);
type = htons(*(uint16_t *)pkt_put(p, sizeof(type)));
clss = htons(*(uint16_t *)pkt_put(p, sizeof(clss)));
ttl = htonl(*(uint32_t *)pkt_put(p, sizeof(ttl)));
addr_size = htons(*(uint16_t *)pkt_put(p, sizeof(addr_size)));
if (addr_size == sizeof(struct in_addr)) {
af = AF_INET;
} else if (addr_size == sizeof(struct in6_addr)) {
af = AF_INET6;
} else {
log_warn("Unexpected address size received: %d\n", addr_size);
break;
}
if (!inet_ntop(af, pkt_put(p, addr_size), addr, ARRAY_SIZE(addr)))
strncpy(addr, "<invalid>", sizeof(addr));
log_info("LLMNR response: %s IN %s %s (TTL %d)\n", name, query_type(type), addr, ttl);
}
} else
log_info("No LLMNR response received within timeout (%lu ms)\n", timeout_ms);
if (i < count - 1) {
pkt_reset(p);
usleep(interval_ms * 1000);
}
}
pkt_free(p);
err:
close(sock);
return 0;
}

View File

@ -0,0 +1,335 @@
/*
* Copyright (C) 2014-2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/select.h>
#include "iface.h"
#include "log.h"
#include "pkt.h"
#include "socket.h"
#include "iface.h"
#include "llmnr-packet.h"
#include "llmnr.h"
static int llmnr_sock_ipv4 = -1;
static int llmnr_sock_ipv6 = -1;
static bool llmnr_running = true;
/*
* Host name in DNS name format (length octet + name + 0 byte)
*/
static char llmnr_hostname[LLMNR_LABEL_MAX_SIZE + 2];
static void llmnr_iface_event_handle(enum iface_event_type type, unsigned char af,
unsigned int ifindex)
{
switch (af) {
case AF_INET:
socket_mcast_group_ipv4(llmnr_sock_ipv4, ifindex, type == IFACE_ADD);
break;
case AF_INET6:
socket_mcast_group_ipv6(llmnr_sock_ipv6, ifindex, type == IFACE_ADD);
break;
default:
/* ignore */
break;
}
}
int llmnr_init(const char *hostname, uint16_t port, bool ipv6)
{
llmnr_hostname[0] = strlen(hostname);
strncpy(&llmnr_hostname[1], hostname, LLMNR_LABEL_MAX_SIZE);
llmnr_hostname[LLMNR_LABEL_MAX_SIZE + 1] = '\0';
log_info("Starting llmnrd on port %u, hostname %s\n", port, hostname);
llmnr_sock_ipv4 = socket_open_ipv4(port);
if (llmnr_sock_ipv4 < 0)
return -1;
if (ipv6) {
llmnr_sock_ipv6 = socket_open_ipv6(port);
if (llmnr_sock_ipv6 < 0)
return -1;
}
iface_register_event_handler(&llmnr_iface_event_handle);
return 0;
}
static bool llmnr_name_matches(const uint8_t *query)
{
uint8_t i, n = llmnr_hostname[0];
/* length */
if (query[0] != n)
return false;
/* NULL byte */
if (query[1 + n] != 0)
return false;
for (i = 1; i < llmnr_hostname[0]; i++)
if (tolower(query[i]) != tolower(llmnr_hostname[i]))
return false;
return true;
}
static void llmnr_respond(unsigned int ifindex, const struct llmnr_hdr *hdr,
const uint8_t *query, size_t query_len, int sock,
const struct sockaddr_storage *sst)
{
uint16_t qtype, qclass;
uint8_t name_len = query[0];
/* skip name length & additional '\0' byte */
const uint8_t *query_name_end = query + name_len + 2;
size_t i, n, response_len;
unsigned char family = AF_UNSPEC;
/*
* arbitrary restriction to 16 addresses per interface for the
* sake of a simple, atomic interface
*/
struct sockaddr_storage addrs[16];
struct pkt *p;
struct llmnr_hdr *r;
/* 4 bytes expected for QTYPE and QCLASS */
if ((query_len - name_len - 2) < (sizeof(qtype) + sizeof(qclass)))
return;
qtype = ntohs(*((uint16_t *)query_name_end));
qclass = ntohs(*((uint16_t *)query_name_end + 1));
/* Ony IN queries supported */
if (qclass != LLMNR_QCLASS_IN)
return;
switch (qtype) {
case LLMNR_QTYPE_A:
family = AF_INET;
break;
case LLMNR_QTYPE_AAAA:
family = AF_INET6;
break;
case LLMNR_QTYPE_ANY:
family = AF_UNSPEC;
break;
default:
return;
}
n = iface_addr_lookup(ifindex, family, addrs, ARRAY_SIZE(addrs));
/*
* This is the max response length (i.e. using all IPv6 addresses and
* not message compression). We might not use all of it.
*/
response_len = n * (1 + name_len + 1 + 2 + 2 + 4 + 2 + sizeof(struct in6_addr));
p = pkt_alloc(sizeof(*hdr) + query_len + response_len);
/* fill the LLMNR header */
r = (struct llmnr_hdr *)pkt_put(p, sizeof(*r));
r->id = hdr->id;
/* response flag */
r->flags = htons(LLMNR_F_QR);
r->qdcount = hdr->qdcount;
r->ancount = htons(n);
r->nscount = 0;
r->arcount = 0;
/* copy the original question */
memcpy(pkt_put(p, query_len), query, query_len);
/* append an RR for each address */
for (i = 0; i < n; i++) {
void *addr;
size_t addr_size;
uint16_t type;
if (addrs[i].ss_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)&addrs[i];
addr = &sin->sin_addr;
addr_size = sizeof(sin->sin_addr);
type = LLMNR_TYPE_A;
} else if (addrs[i].ss_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addrs[i];
addr = &sin6->sin6_addr;
addr_size = sizeof(sin6->sin6_addr);
type = LLMNR_TYPE_AAAA;
} else
continue;
/* NAME */
if (i == 0)
memcpy(pkt_put(p, llmnr_hostname[0] + 2), llmnr_hostname, llmnr_hostname[0] + 2);
else {
/* message compression (RFC 1035, section 4.1.3) */
uint16_t ptr = 0xC000 | (sizeof(*hdr) + query_len);
pkt_put_u16(p, ntohs(ptr));
}
/* TYPE */
pkt_put_u16(p, htons(type));
/* CLASS */
pkt_put_u16(p, htons(LLMNR_CLASS_IN));
/* TTL */
pkt_put_u32(p, htonl(LLMNR_TTL_DEFAULT));
/* RDLENGTH */
pkt_put_u16(p, htons(addr_size));
/* RDATA */
memcpy(pkt_put(p, addr_size), addr, addr_size);
}
if (sendto(sock, p->data, pkt_len(p), 0, (struct sockaddr *)sst, sizeof(*sst)) < 0)
log_err("Failed to send response: %s\n", strerror(errno));
pkt_free(p);
}
static void llmnr_packet_process(unsigned int ifindex, const uint8_t *pktbuf, size_t len,
int sock, const struct sockaddr_storage *sst)
{
const struct llmnr_hdr *hdr = (const struct llmnr_hdr *)pktbuf;
uint16_t flags, qdcount;
const uint8_t *query;
size_t query_len;
uint8_t name_len;
/* Query too short? */
if (len < sizeof(struct llmnr_hdr))
return;
flags = ntohs(hdr->flags);
qdcount = ntohs(hdr->qdcount);
/* Query invalid as per RFC 4795, section 2.1.1? */
if (((flags & (LLMNR_F_QR | LLMNR_F_OPCODE)) != 0) ||
qdcount != 1 || hdr->ancount != 0 || hdr->nscount != 0)
return;
query = pktbuf + sizeof(struct llmnr_hdr);
query_len = len - sizeof(struct llmnr_hdr);
name_len = query[0];
/* Invalid name in query? */
if (name_len == 0 || name_len >= query_len || query[1 + name_len] != 0)
return;
/* Authoritative? */
if (llmnr_name_matches(query))
llmnr_respond(ifindex, hdr, query, query_len, sock, sst);
}
static void llmnr_recv(int sock)
{
uint8_t pktbuf[2048], aux[128];
struct msghdr msg;
struct iovec io;
struct sockaddr_storage sin_r;
struct cmsghdr *cmsg;
ssize_t recvlen;
int ifindex = -1;
io.iov_base = pktbuf;
io.iov_len = sizeof(pktbuf);
memset(&msg, 0, sizeof(msg));
msg.msg_name = &sin_r;
msg.msg_namelen = sizeof(sin_r);
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_control = aux;
msg.msg_controllen = sizeof(aux);
if ((recvlen = recvmsg(sock, &msg, 0)) < 0) {
if (errno != EINTR)
log_err("Failed to receive packet: %s\n", strerror(errno));
return;
}
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo *in = (struct in_pktinfo *)CMSG_DATA(cmsg);
ifindex = in->ipi_ifindex;
} else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo *in6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
ifindex = in6->ipi6_ifindex;
}
}
if (ifindex >= 0)
llmnr_packet_process(ifindex, pktbuf, recvlen, sock,
(const struct sockaddr_storage *)&sin_r);
else
log_warn("Could not get interface of incoming packet\n");
}
int llmnr_run(void)
{
int ret = -1;
while (llmnr_running) {
fd_set rfds;
struct timeval tv;
int nfds, ret;
FD_ZERO(&rfds);
FD_SET(llmnr_sock_ipv4, &rfds);
if (llmnr_sock_ipv6 >= 0) {
FD_SET(llmnr_sock_ipv6, &rfds);
nfds = max(llmnr_sock_ipv4, llmnr_sock_ipv6) + 1;
} else
nfds = llmnr_sock_ipv4 + 1;
tv.tv_sec = 0;
tv.tv_usec = 200;
ret = select(nfds, &rfds, NULL, NULL, &tv);
if (ret < 0) {
if (errno != EINTR)
log_err("Failed to select() on socket: %s\n", strerror(errno));
goto out;
} else if (ret) {
if (FD_ISSET(llmnr_sock_ipv4, &rfds))
llmnr_recv(llmnr_sock_ipv4);
if (llmnr_sock_ipv6 >= 0 && FD_ISSET(llmnr_sock_ipv6, &rfds))
llmnr_recv(llmnr_sock_ipv6);
}
}
ret = 0;
out:
close(llmnr_sock_ipv4);
if (llmnr_sock_ipv6 >= 0)
close(llmnr_sock_ipv6);
return ret;
}
void llmnr_stop(void)
{
llmnr_running = false;
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LLMNR_H
#define LLMNR_H
#include <stdbool.h>
#include <stdint.h>
int llmnr_init(const char *hostname, uint16_t port, bool ipv6);
int llmnr_run(void);
void llmnr_stop(void);
#endif /* LLMNR_H */

View File

@ -0,0 +1,174 @@
/*
* llmnrd -- LLMNR (RFC 4705) responder daemon.
*
* Copyright (C) 2014-2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "compiler.h"
#include "log.h"
#include "util.h"
#include "iface.h"
#include "llmnr.h"
#include "llmnr-packet.h"
static const char *short_opts = "H:p:6dhV";
static const struct option long_opts[] = {
{ "hostname", required_argument, NULL, 'H' },
{ "port", required_argument, NULL, 'p' },
{ "ipv6", no_argument, NULL, '6' },
{ "daemonize", no_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 },
};
static void __noreturn usage_and_exit(int status)
{
fprintf(stdout, "Usage: llmnrd [OPTIONS]\n"
"Options:\n"
" -H, --hostname NAME set hostname to respond with (default: system hostname)\n"
" -p, --port NUM set port number to listen on (default: %d)\n"
" -6, --ipv6 enable LLMNR name resolution over IPv6\n"
" -d, --daemonize run as daemon in the background\n"
" -h, --help show this help and exit\n"
" -V, --version show version information and exit\n",
LLMNR_UDP_PORT);
exit(status);
}
static void __noreturn version_and_exit(void)
{
fprintf(stdout, "llmnrd %s %s\n"
"Copyright (C) 2014-2015 Tobias Klauser <tklauser@distanz.ch>\n"
"Licensed under the GNU General Public License, version 2\n",
VERSION_STRING, GIT_VERSION);
exit(EXIT_SUCCESS);
}
static void signal_handler(int sig)
{
switch (sig) {
case SIGINT:
case SIGQUIT:
case SIGTERM:
log_info("Interrupt received. Stopping llmnrd.\n");
iface_stop();
llmnr_stop();
break;
case SIGHUP:
default:
/* ignore */
break;
}
}
static void register_signal(int sig, void (*handler)(int))
{
sigset_t block_mask;
struct sigaction saction;
sigfillset(&block_mask);
saction.sa_handler = handler;
saction.sa_mask = block_mask;
if (sigaction(sig, &saction, NULL) != 0) {
log_err("Failed to register signal handler for %s (%d)\n",
strsignal(sig), sig);
}
}
int main(int argc, char **argv)
{
int c, ret = EXIT_FAILURE;
long num_arg;
bool daemonize = false, ipv6 = false;
char *hostname = NULL;
uint16_t port = LLMNR_UDP_PORT;
while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (c) {
case 'd':
daemonize = true;
break;
case 'H':
hostname = xstrdup(optarg);
break;
case 'p':
num_arg = strtol(optarg, NULL, 0);
if (num_arg < 0 || num_arg > UINT16_MAX) {
log_err("Invalid port number: %ld\n", num_arg);
return EXIT_FAILURE;
}
port = num_arg;
case '6':
ipv6 = true;
break;
case 'V':
version_and_exit();
case 'h':
usage_and_exit(EXIT_SUCCESS);
default:
usage_and_exit(EXIT_FAILURE);
}
}
register_signal(SIGINT, signal_handler);
register_signal(SIGQUIT, signal_handler);
register_signal(SIGTERM, signal_handler);
register_signal(SIGHUP, signal_handler);
if (!hostname) {
/* TODO: Consider hostname changing at runtime */
hostname = xmalloc(255);
if (gethostname(hostname, 255) != 0) {
log_err("Failed to get hostname");
return EXIT_FAILURE;
}
}
if (daemonize) {
if (daemon(0, 0) != 0) {
log_err("Failed to daemonize process: %s\n", strerror(errno));
return EXIT_FAILURE;
}
}
if (llmnr_init(hostname, port, ipv6) < 0)
goto out;
if (iface_start_thread() < 0)
goto out;
ret = llmnr_run();
out:
free(hostname);
return ret;
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LOG_H
#define LOG_H
#include <stdio.h>
#define log_err(fmt, args...) fprintf(stderr, "Error: " fmt, ##args)
#define log_warn(fmt, args...) fprintf(stderr, "Warning: " fmt, ##args)
#define log_info(fmt, args...) fprintf(stdout, fmt, ##args)
#ifdef DEBUG
# define log_dbg(fmt, args...) fprintf(stdout, fmt, ##args)
#else
# define log_dbg(fmt, args...)
#endif
#endif /* LOG_H */

View File

@ -0,0 +1,111 @@
/*
* Packet buffer structure and utilities.
*
* Copyright (C) 2015 Tobias Klauser <tklauser@distanz.ch>
*
* Based on pkt_buff.h from the netsniff-ng toolkit which is:
*
* Copyright (C) 2012 Christoph Jaeger
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PKT_H
#define PKT_H
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include "log.h"
#include "util.h"
struct pkt {
uint8_t *data;
uint8_t *tail;
size_t size;
};
static inline bool pkt_invariant(struct pkt *p)
{
return p && (p->data <= p->tail);
}
static inline struct pkt *pkt_alloc(size_t size)
{
struct pkt *p = xmalloc(sizeof(*p) + size);
uint8_t *data = (uint8_t *)p + sizeof(*p);
p->data = p->tail = data;
p->size = size;
return p;
}
static inline void pkt_free(struct pkt *p)
{
free(p);
}
static inline void pkt_reset(struct pkt *p)
{
assert(pkt_invariant(p));
p->tail = p->data;
}
static inline size_t pkt_len(struct pkt *p)
{
assert(pkt_invariant(p));
return p->tail - p->data;
}
static inline uint8_t *pkt_put(struct pkt *p, size_t len)
{
uint8_t *data;
assert(pkt_invariant(p));
if (pkt_len(p) + len <= p->size) {
data = p->tail;
p->tail += len;
} else {
/* grow packet */
size_t new_size = p->size + len - pkt_len(p);
struct pkt *np = xrealloc(p, sizeof(*np) + new_size);
log_dbg("Reallocating packet from %zu to %zu bytes\n", p->size, new_size);
data = (uint8_t *)np + sizeof(*np);
np->data = data;
np->tail = data + pkt_len(p);
}
return data;
}
#define DEFINE_PKT_PUT(__bitwidth) \
static inline void pkt_put_u##__bitwidth(struct pkt *p, uint##__bitwidth##_t val) \
{ \
uint##__bitwidth##_t *data = (uint##__bitwidth##_t *)pkt_put(p, sizeof(val)); \
*data = val; \
}
DEFINE_PKT_PUT(8)
DEFINE_PKT_PUT(16)
DEFINE_PKT_PUT(32)
#endif /* PKT_H */

View File

@ -0,0 +1,193 @@
/*
* Copyright (C) 2014-2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "llmnr-packet.h"
#include "log.h"
#include "socket.h"
static const int YES = 1;
int socket_open_ipv4(uint16_t port)
{
int sock;
struct sockaddr_in sa;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
log_err("Failed to open UDP socket: %s\n", strerror(errno));
return -1;
}
/* pass pktinfo struct on received packets */
if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &YES, sizeof(YES)) < 0) {
log_err("Failed to set IPv4 packet info socket option: %s\n", strerror(errno));
goto err;
}
/* bind the socket */
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(port);
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
log_err("Failed to bind() socket: %s\n", strerror(errno));
goto err;
}
return sock;
err:
close(sock);
return -1;
}
int socket_open_ipv6(uint16_t port)
{
int sock, opt_pktinfo;
struct sockaddr_in6 sa;
sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock < 0) {
log_err("Failed to open UDP socket: %s\n", strerror(errno));
return -1;
}
/* pass pktinfo struct on received packets */
#if defined(IPV6_RECVPKTINFO)
opt_pktinfo = IPV6_RECVPKTINFO;
#elif defined(IPV6_PKTINFO)
opt_pktinfo = IPV6_PKTINFO;
#endif
if (setsockopt(sock, IPPROTO_IPV6, opt_pktinfo, &YES, sizeof(YES)) < 0) {
log_err("Failed to set IPv6 packet info socket option: %s\n", strerror(errno));
goto err;
}
/* IPv6 only socket */
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &YES, sizeof(YES)) < 0) {
log_err("Failed to set IPv6 only socket option: %s\n", strerror(errno));
goto err;
}
/* bind the socket */
memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
sa.sin6_port = htons(port);
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
log_err("Failed to bind() socket: %s\n", strerror(errno));
goto err;
}
return sock;
err:
close(sock);
return -1;
}
int socket_open_rtnl(void)
{
int sock;
struct sockaddr_nl sa;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0) {
log_err("Failed to open netlink route socket: %s\n", strerror(errno));
return -1;
}
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
/*
* listen for following events:
* - network interface create/delete/up/down
* - IPv4 address add/delete
* - IPv6 address add/delete
*/
sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
log_err("Failed to bind() netlink socket: %s\n", strerror(errno));
goto err;
}
return sock;
err:
close(sock);
return -1;
}
int socket_mcast_group_ipv4(int sock, unsigned int ifindex, bool join)
{
struct ip_mreqn mreq;
char ifname[IF_NAMESIZE];
/* silently ignore, we might not be listening on an IPv4 socket */
if (sock < 0)
return -1;
memset(&mreq, 0, sizeof(mreq));
mreq.imr_ifindex = ifindex;
mreq.imr_address.s_addr = INADDR_ANY;
inet_pton(AF_INET, LLMNR_IPV4_MCAST_ADDR, &mreq.imr_multiaddr);
if (setsockopt(sock, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP,
&mreq, sizeof(mreq)) < 0) {
log_err("Failed to join IPv4 multicast group on interface %s: %s\n",
if_indextoname(ifindex, ifname), strerror(errno));
return -1;
}
return 0;
}
int socket_mcast_group_ipv6(int sock, unsigned int ifindex, bool join)
{
struct ipv6_mreq mreq6;
char ifname[IF_NAMESIZE];
/* silently ignore, we might not be listening on an IPv6 socket */
if (sock < 0)
return -1;
memset(&mreq6, 0, sizeof(mreq6));
mreq6.ipv6mr_interface = ifindex;
inet_pton(AF_INET6, LLMNR_IPV6_MCAST_ADDR, &mreq6.ipv6mr_multiaddr);
if (setsockopt(sock, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP,
&mreq6, sizeof(mreq6)) < 0) {
log_err("Failed to join IPv6 multicast group on interface %s: %s\n",
if_indextoname(ifindex, ifname), strerror(errno));
return -1;
}
return 0;
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2014-2015 Tobias Klauser <tklauser@distanz.ch>
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SOCKET_H
#define SOCKET_H
#include <stdbool.h>
#include <stdint.h>
int socket_open_ipv4(uint16_t port);
int socket_open_ipv6(uint16_t port);
int socket_open_rtnl(void);
int socket_mcast_group_ipv4(int sock, unsigned int ifindex, bool join);
int socket_mcast_group_ipv6(int sock, unsigned int ifindex, bool join);
#endif /* SOCKET_H */

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2014-2015 Tobias Klauser <tklauser@distanz.ch>
* Copyright (C) 2009-2012 Daniel Borkmann
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include "util.h"
void *xmalloc(size_t size)
{
void *ptr;
if (size == 0)
panic("malloc: size 0\n");
ptr = malloc(size);
if (!ptr)
panic("malloc: out of memory\n");
return ptr;
}
void *xzalloc(size_t size)
{
void *ptr = xmalloc(size);
memset(ptr, 0, size);
return ptr;
}
void *xrealloc(void *ptr, size_t size)
{
void *newptr;
if (size == 0)
panic("realloc: size 0\n");
newptr = realloc(ptr, size);
if (!newptr) {
free(ptr);
panic("realloc: out of memory\n");
}
return newptr;
}
char *xstrdup(const char *s)
{
size_t len = strlen(s) + 1;
char *ret = xmalloc(len);
memcpy(ret, s, len);
return ret;
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2014-2015 Tobias Klauser <tklauser@distanz.ch>
* Copyright (C) 2009-2012 Daniel Borkmann
*
* This file is part of llmnrd.
*
* llmnrd 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 of the License.
*
* llmnrd 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 llmnrd. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UTIL_H
#define UTIL_H
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "compiler.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/*
* min()/max() macros with strict type-checking.
* Taken from linux/kernel.h
*/
#undef min
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
#undef max
#define max(x, y) ({ \
typeof(x) _max1 = (x); \
typeof(y) _max2 = (y); \
(void) (&_max1 == &_max2); \
_max1 > _max2 ? _max1 : _max2; })
static inline void __noreturn panic(const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
vfprintf(stderr, fmt, vl);
va_end(vl);
exit(EXIT_FAILURE);
}
void *xmalloc(size_t size) __warn_unused_result;
void *xzalloc(size_t size) __warn_unused_result;
void *xrealloc(void *ptr, size_t size) __warn_unused_result;
char *xstrdup(const char *s) __warn_unused_result;
static inline bool xstreq(const char *str1, const char *str2)
{
size_t n = strlen(str1);
if (n != strlen(str2))
return false;
if (strncmp(str1, str2, n) != 0)
return false;
return true;
}
#endif /* UTIL_H */

View File

@ -10,8 +10,6 @@ depend()
start()
{
[ -d /sys/bus/vmbus ] && exit 0
ebegin "Starting mDNS server"
PIDFILE=/var/run/mdnstool.pid
@ -30,8 +28,6 @@ start()
stop()
{
[ -d /sys/bus/vmbus ] && exit 0
ebegin "Stopping mDNS server"
PIDFILE=/var/run/mdnstool.pid

View File

@ -6,7 +6,7 @@ license: packages.lua Dockerfile license.sh
run: license
rm -rf output
docker run -it -v /etc:/hostetc -v /lib:/lib -v $(PWD)/output:/output license
docker run -it -v /etc:/hostetc -v /usr:/hostusr -v /lib:/lib -v $(PWD)/output:/output license
clean:
rm -rf output

View File

@ -5,5 +5,5 @@ kernel and packages
```
docker build -t license .
docker run -it -v /etc:/hostetc -v /lib:/lib -v $PWD/output:/output license
docker run -it -v /etc:/hostetc -v /usr/:/hostusr -v /lib:/lib -v $PWD/output:/output license
```

View File

@ -82,4 +82,7 @@ cd /output/aufs-util
git checkout "$AUFS_TOOLS_COMMIT"
rm -rf .git
cp /hostusr/share/src/* /output
cp /hostetc/init.d/chronyd /output
printf "All source code now in output/ directory\n"