#!/bin/bash # # script: rc.library.source # # Library used by nfsd, ntpd, rpc, samba, nginx, sshd, avahidaemon, show_interfaces # # Bergware - created for Unraid OS, December 2023 # Bergware - updated May 2025 WIREGUARD="/etc/wireguard" NETWORK_INI="/var/local/emhttp/network.ini" NETWORK_EXTRA="/boot/config/network-extra.cfg" var(){ if [[ $# -eq 3 ]]; then [[ -r "$3" ]] && sed -n "/^\[$1\]\$/,/^\[/p" "$3" | grep -Pom1 "^$2=\"\K[^\"]+" elif [[ $# -eq 2 ]]; then [[ -r "$2" ]] && grep -Pom1 "^$1=\"\K[^\"]+" "$2" fi } ipv(){ local t=${1//[^:]} [[ ${#t} -le 1 ]] && echo 4 || echo 6 } this(){ local MAP ADDR case $CALLER in 'avahi') grep -Pom1 "^$1=\K.*" $CONF ;; 'smb') grep -Pom1 "^$1 = \K.*" $CONF ;; 'ntp'|'ssh') grep -Po "^$1 \K\S+" $CONF | tr '\n' ' ' | sed 's/ $//' ;; 'nfs') grep -Pom1 "^RPC_NFSD_OPTS=\"$OPTIONS \K[^\"]+" $NFS ;; 'rpc') grep -Pom1 "^RPCBIND_OPTS=\"\K[^\"]+" $RPC ;; 'nginx') grep -Po "^NGINX_BIND=\"\K[^\"]+" ${INI%.*} 2>/dev/null ;; esac } scan(){ grep -Pom1 "^$1=\"?\K[^\"]+" $2 } good(){ local TAG BAD for TAG in ${BIND[@]}; do [[ -z $1 || $1 == $TAG || ${1:0:4} == fe80 ]] && BAD=1 && break done echo $BAD } show(){ case $# in 1) ip -br addr show scope global primary -deprecated to $1 2>/dev/null | awk '{gsub("@.+","",$1);print $1;exit}' ;; 2) ip -br addr show scope global primary -deprecated $1 $2 2>/dev/null | awk '{$1=$2="";print;exit}' | sed -r 's/ metric [0-9]+//g' ;; esac } unmask(){ if [[ $CALLER != smb ]]; then # remove netmask echo ${1/\/*} elif [[ $(ipv $1) == 4 ]]; then # replace netmask echo ${1/\/32/\/24} elif [[ -z $DENY6 ]]; then # IPv6 only when netbios is disabled echo ${1/\/128/\/64} fi } remove(){ local i ADDR for ADDR in $@; do ADDR=$(unmask $ADDR) [[ -z $ADDR ]] && continue for i in ${!BIND[@]}; do [[ $ADDR == ${BIND[$i]} ]] && unset 'BIND[i]' done done } isname(){ [[ -z ${1//[^.:]} || ${1//[^.:]} == . ]] && return 0 || return 1 } add_name(){ local NET for NET in $include_interfaces; do if $(isname $NET); then # NET is an interface name, validate [[ -n $(show dev $NET) && -z $(good $NET) ]] && BIND+=($NET) else # NET is an IP address, convert to name NET=$(show $NET) [[ -n $NET && -z $(good $NET) ]] && BIND+=($NET) fi done for NET in $exclude_interfaces; do if $(isname $NET); then # NET is an interface name remove "$NET" else # NET is an IP address, convert to name remove "$(show $NET)" fi done } add_addr(){ local NET MAP ADDR for NET in $include_interfaces; do if $(isname $NET); then # NET is an interface name, get IP addresses MAP="$(show dev $NET)" else # NET is an IP address, validate MAP="$(show to $NET)" fi for ADDR in $MAP; do ADDR=$(unmask $ADDR) [[ -z $(good $ADDR) ]] && BIND+=($ADDR) || continue [[ $(ipv $ADDR) == 4 ]] && IPV4=yes || IPV6=yes done done for NET in $exclude_interfaces; do if $(isname $NET); then # NET is an interface name, get IP addresses remove "$(show dev $NET)" else # NET is an IP address remove "$(show to $NET)" fi done } check(){ # quick check [[ -n $BIND ]] && return 0; # preset return values BIND=(); IPV4=no; IPV6=no; FAMILY=any; # read active interfaces (including wireguard tunnels) while IFS=$'\n' read -r NET; do NET=($NET) if [[ ${NET:0:2} == wg ]]; then # skip wireguard tunnel if NTP or VPN type [[ $CALLER == ntp || $(grep -Pom1 '^TYPE:1="\K[^"]+' $WIREGUARD/$NET.cfg) == 8 ]] && continue fi for ADDR in ${NET[@]/$NET}; do ADDR=$(unmask $ADDR) if [[ "ntp avahi show" =~ "$CALLER" ]]; then [[ -z $(good $NET) ]] && BIND+=($NET) else [[ -z $(good $ADDR) ]] && BIND+=($ADDR) || continue fi [[ $(ipv $ADDR) == 4 ]] && IPV4=yes || IPV6=yes done done <<< $(ip -br addr show scope global primary -deprecated | awk '$1~"^(br|bond|eth|wlan|wg)[0-9]+(.[0-9]+)?" && $3!="" {gsub("@.+","",$1);$2="";print}' | sed -r 's/ metric [0-9]+//g' | sort) # add loopback interface if [[ "smb nfs" =~ "$CALLER" ]]; then [[ $IPV4 == yes ]] && BIND+=(127.0.0.1) [[ $IPV6 == yes ]] && BIND+=(::1) fi # add user defined interfaces if [[ -f $NETWORK_EXTRA && $CALLER != ntp ]]; then . <(fromdos <$NETWORK_EXTRA) [[ "avahi show" =~ "$CALLER" ]] && add_name || add_addr fi if [[ $CALLER == ssh ]]; then # BIND stays array BIND=(${BIND[@]}) [[ $IPV4 == yes && $IPV6 == no ]] && FAMILY=inet [[ $IPV6 == yes && $IPV4 == no ]] && FAMILY=inet6 else # convert array to string BIND=${BIND[@]} [[ $CALLER == avahi ]] && BIND=${BIND// /,} fi return 0 }