mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-21 18:11:35 +00:00
commit
8eaf2976b8
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM debian:jessie
|
||||
FROM ubuntu:15.10
|
||||
|
||||
RUN apt-get update && apt-get -y upgrade && apt-get -y install \
|
||||
genisoimage \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
73
alpine/packages/chronyd/etc/init.d/chronyd
Executable file
73
alpine/packages/chronyd/etc/init.d/chronyd
Executable 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"
|
||||
}
|
@ -174,7 +174,7 @@ stop()
|
||||
eoutdent
|
||||
|
||||
# now terminate with extreme prejudice
|
||||
halt -f
|
||||
poweroff -f
|
||||
|
||||
return 0
|
||||
}
|
||||
|
7
alpine/packages/hvtools/.gitignore
vendored
7
alpine/packages/hvtools/.gitignore
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}"
|
||||
}
|
@ -2,6 +2,12 @@
|
||||
|
||||
HV_DAEMON=hv_kvp_daemon
|
||||
|
||||
depend()
|
||||
{
|
||||
after dev
|
||||
needs networking
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
[ ! -d /sys/bus/vmbus ] && exit 0
|
||||
|
@ -2,6 +2,11 @@
|
||||
|
||||
HV_DAEMON=hv_vss_daemon
|
||||
|
||||
depend()
|
||||
{
|
||||
after dev
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
[ ! -d /sys/bus/vmbus ] && exit 0
|
||||
|
@ -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
2
alpine/packages/llmnrd/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/llmnrd
|
||||
/llmnrd.tar.gz
|
11
alpine/packages/llmnrd/Dockerfile
Normal file
11
alpine/packages/llmnrd/Dockerfile
Normal 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
|
11
alpine/packages/llmnrd/Makefile
Normal file
11
alpine/packages/llmnrd/Makefile
Normal 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
|
41
alpine/packages/llmnrd/etc/init.d/llmnrd
Executable file
41
alpine/packages/llmnrd/etc/init.d/llmnrd
Executable 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"
|
||||
}
|
74
alpine/packages/llmnrd/src/Makefile
Normal file
74
alpine/packages/llmnrd/src/Makefile
Normal 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)
|
66
alpine/packages/llmnrd/src/README
Normal file
66
alpine/packages/llmnrd/src/README
Normal 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/
|
46
alpine/packages/llmnrd/src/compiler.h
Normal file
46
alpine/packages/llmnrd/src/compiler.h
Normal 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 */
|
32
alpine/packages/llmnrd/src/err.h
Normal file
32
alpine/packages/llmnrd/src/err.h
Normal 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 */
|
395
alpine/packages/llmnrd/src/iface.c
Normal file
395
alpine/packages/llmnrd/src/iface.c
Normal 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;
|
||||
}
|
39
alpine/packages/llmnrd/src/iface.h
Normal file
39
alpine/packages/llmnrd/src/iface.h
Normal 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 */
|
82
alpine/packages/llmnrd/src/list.h
Normal file
82
alpine/packages/llmnrd/src/list.h
Normal 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 */
|
80
alpine/packages/llmnrd/src/llmnr-packet.h
Normal file
80
alpine/packages/llmnrd/src/llmnr-packet.h
Normal 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 */
|
315
alpine/packages/llmnrd/src/llmnr-query.c
Normal file
315
alpine/packages/llmnrd/src/llmnr-query.c
Normal 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;
|
||||
}
|
335
alpine/packages/llmnrd/src/llmnr.c
Normal file
335
alpine/packages/llmnrd/src/llmnr.c
Normal 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;
|
||||
}
|
29
alpine/packages/llmnrd/src/llmnr.h
Normal file
29
alpine/packages/llmnrd/src/llmnr.h
Normal 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 */
|
174
alpine/packages/llmnrd/src/llmnrd.c
Normal file
174
alpine/packages/llmnrd/src/llmnrd.c
Normal 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;
|
||||
}
|
33
alpine/packages/llmnrd/src/log.h
Normal file
33
alpine/packages/llmnrd/src/log.h
Normal 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 */
|
111
alpine/packages/llmnrd/src/pkt.h
Normal file
111
alpine/packages/llmnrd/src/pkt.h
Normal 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 */
|
193
alpine/packages/llmnrd/src/socket.c
Normal file
193
alpine/packages/llmnrd/src/socket.c
Normal 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;
|
||||
}
|
32
alpine/packages/llmnrd/src/socket.h
Normal file
32
alpine/packages/llmnrd/src/socket.h
Normal 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 */
|
70
alpine/packages/llmnrd/src/util.c
Normal file
70
alpine/packages/llmnrd/src/util.c
Normal 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;
|
||||
}
|
78
alpine/packages/llmnrd/src/util.h
Normal file
78
alpine/packages/llmnrd/src/util.h
Normal 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 */
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
```
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user