#!/bin/bash # Copyright: 2023 Virtuozzo International GmbH # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. [ -n "${NETLIB_VERSION:-}" ] && return 0; NETLIB_VERSION="0.1"; inherit os log core; $PROGRAM 'sed'; $PROGRAM 'ipcalc'; __FD_TCP_DOWNLOAD=5; __URL_HOSTNAME=0; __URL_PORT=1; __URL_URI=2; _TMP_DIR='/tmp/'; #detect wget _HAS_WGET=1; command -v wget >/dev/null 2>&1 || __HAS_WGET=0; $PROGRAM 'sendmail'; # sendErrorMail # $1 - body # $2 - subject (optional) # $3 - mailto (optional) # $4 - mailfrom (optional) function sendErrorMail(){ : ${1:?"Missing param: email body"}; [[ -z $SENDMAIL ]] && return 0; getInfraAddresses; __NET_MAIL_SUBJECT="[JEM] Error message"; __NET_MAIL_MAILFROM="jem@${FQDN}"; [ -z "$PLATFORM_TECHMAIL_RECEPIENT" ] && __NET_MAIL_MAILTO="dev@jelastic.com" || __NET_MAIL_MAILTO=$PLATFORM_TECHMAIL_RECEPIENT; local msg=${1} body; local subject=${2:-${__NET_MAIL_SUBJECT}}; local mailto=${3:-${__NET_MAIL_MAILTO}}; local mailfrom=${4:-${__NET_MAIL_MAILFROM}}; log "Mail: ${msg}"; msg="${msg} on `hostname`" body=`printf "To: ${mailto}\nFrom: ${mailfrom}\nSubject: ${subject}\nContent-Type: text/plain; charset=UTF-8\n ${msg} \n\n The stack is:\n$(printStack)"`; [[ "${DEBUG}" -ne "0" ]] && echo ${body} || { echo ${body} | ${SENDMAIL} -i -f ${mailfrom} ${mailto}; } return ${?}; } # isLANIP # Determinate which IP belongs to LAN # $1 - ip address to check # returns: # 0 - lan ip # 1 - wan ip isLANIP() { : ${1:?"Missing param: IP"}; local ip local pip=${1} log "isLANIP ${pip}" if ! isValidIP ${pip} ; then log "Invalid IP - isLANIP ${pip}"; return 1; fi OIFS=$IFS;IFS='.';ip=(${pip});IFS=$OIFS [[ ${ip[0]} -eq 172 && ${ip[1]} -ge 16 && ${ip[1]} -le 31 ]] && return 0; [[ ${ip[0]} -eq 192 && ${ip[1]} -eq 168 ]] && return 0; [[ ${ip[0]} -eq 100 && ${ip[1]} -ge 64 && ${ip[1]} -le 127 ]] && return 0; [[ ${ip[0]} -eq 10 ]] && return 0; return 1 } # isValidURL() # param: 1 - URL to check # returns: 0 = URL is valid, 1 = URL is not valid function isValidURL() { local url=${1} #regex='[(https?|ftp|file|git|svn)://]?[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]'; regex='^((git|https?|ftps?|ssh|rsync|svn|\/):\/\/)?([-a-zA-Z0-9\.]*[-_\:\/\~a-z0-9A-Z\/\.]*)$'; [[ ${url} =~ ${regex} ]] && return 0 || return 1; } # validIP $ip # Checks if given IP is valid # $ip - IP to check # # returns 0 if IP is valid and 1 otherwise function isValidIP() { local ip=${1} stat=1; [[ "${ip}" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] && { OIFS=${IFS}; IFS='.'; ip=(${ip}); IFS=${OIFS}; [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]; stat=$?; } return ${stat}; } isValidCIDR(){ CIDR=$1 local REGEX='(((25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?))(\/([1-9]|[1-2][0-9]|3[0-2]))([^0-9.]|$)' if grep -qP "$REGEX" <<<$CIDR; then return 0 else return 1 fi } function cdr2mask (){ set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0 [ $1 -gt 1 ] && shift $1 || shift echo ${1-0}.${2-0}.${3-0}.${4-0} } # __getHostName() # param: source URL # returns: request hostname function __getHostName() { local url=${1} result; result=$($SED -re "s/(.*)\/\/([^\/]*)\/(.*)/\2/" <<< ${url}); echo ${result}; } # __getURIPath() # param: source URL # returns: request URI path function __getURIPath() { local url=${1} result; result=$($SED -re "s/(.*)\/\/([^\/]*)\/(.*)/\/\3/" <<< ${url}); echo ${result}; } # __getPort() # param: source URL # returns: request port function __getPort() { local url=${1} result; #result=$($SED -re "s/(.*)\/\/([^\/]*)\/(.*)/\2\3/" <<< ${url}); echo ${result:-80}; } # _parseURL() { # param: source URL # returns: array (0=hostname, 1=port, 2=URI) function _parseURL() { local url=$1 result=() functions=('__getHostName' '__getPort' '__getURIPath'); typeset -i c; c=0; for i in ${functions[*]} ; do result[${c}]=$(${i} ${url}); c=${c}+1; done echo ${result[*]}; } # getFile() # param: $1 - URL to download, $2 - destination directory, $3 - filename function getFile() { : ${1:?"Missing param: URL"}; local url=${1}; dest_dir=${2:-${_TMP_DIR}} tempfile=$(mktemp) destfile=${3}; local purl=($(_parseURL ${url})); eval "exec ${__FD_TCP_DOWNLOAD}<>'/dev/tcp/${purl[__URL_HOSTNAME]}/${purl[__URL_PORT]}'"; echo -e "GET ${purl[__URL_URI]} HTTP/1.1\r\nHost: ${purl[__URL_HOSTNAME]}\r\nUser-Agent: bash/${BASH_VERSION}\r\n\r\n" >&${__FD_TCP_DOWNLOAD}; content=$(cat <&${__FD_TCP_DOWNLOAD} | tee >($SED -e '1,/^.$/d' > "${tempfile}") >&1); [[ -z "${destfile}" ]] && destfile=$($SED -ne '/.*filename="\([^"]\+\)".*/{s/.*filename="\([^"]\+\)".*/\1/i;p;};h;' <<< $content); size=$($SED -ne '/.*Content-Length:\s\+[0-9]\+.*/{s/.*Content-Length:\s\+\([0-9]\+\).*/\1/i;p;};h;' <<< $content); mv "${tempfile}" "${dest_dir}${destfile}" closeFD ${__FD_TCP_DOWNLOAD}; return 0; } function getPage() { : ${1:?"Missing param: URL"}; local htmltempfile=$(mktemp) url=${1}; echo $url downloadFile ${url} ${htmltempfile}; cat "${htmltempfile}"; } # downloadFile() # param: $1 - URL to download, $2 - destination directory function wgetFile() { local url=${1} dest_dir=${2:-${_TMP_DIR}}; [[ ${__HAS_WGET} -ne 0 ]] && { wget -q -nc ${url} -P ${dest_dir} || die "Could not download file ${url}: ${!}"; return 0; } getFile ${*}; return ${?}; } function getNSAddr() { echo $($SED -rne 's/nameserver\s+(.*)/\1/;/(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/p' /etc/resolv.conf); return 0; } function getLocalNSAddr() { local NS=($(getNSAddr)) locNS=(); for i in ${NS[@]} ; do isLANIP $i && locNS=(${locNS[@]} $i); done; [ -n "${locNS}" ] && { echo "${locNS[@]}" | $SED -re 's/\s+/\n/g' | uniq ; return 0; }; return 1; } function getInfraAddresses() { # : ${1:?"Missing param: arrInfraAddr"}; # local arrResult=$1; NSIPs=( $(getLocalNSAddr) ) (( ${#NSIPs[@]} == 0 )) && { warn "Couldn't determine local nameservers" return 1; } for i in 1 2 ; do for NSIP in ${NSIPs[@]}; do local response="$(host ${NSIP})" (( $? == 0 )) || continue domain=$($SED -re '/NXDOMAIN/d;s/.*\s+(rslv[0-9]{0,3}|resolver)\.(\S+)\.$/\2/g;/arpa/d' <<< "$response" | tail -n 1); [[ -z "$domain" ]] || break 2 done sleep 2 done [ -z "${domain}" ] && return 1; ## TODO: Need to replace to the v4 code {{{ RSLV_ADDR="$(getHostIP "rslv.${domain}")"; RSLV_ADDR=${RSLV_ADDR:=${NSIP}}; CORE_ADDR="$(getHostIP "core.${domain}")"; APP_ADDR="$(getHostIP "app.${domain}")"; DB_ADDR="$(getHostIP "db.${domain}")"; GATE_ADDR="$( host gate.${domain} | sed '/alias for/d' | sed -re '/NXDOMAIN/d;s/.+\s+(([0-9]{1,3}\.){3}[0-9]{1,3})/\1/')" FQDN="$(hostname | awk -F "." '{print $1}').${domain}"; JEL_CORE_ADDR="$( host jelcore.${domain} | sed '/alias for/d' | sed -re '/NXDOMAIN/d;s/.+\s+(([0-9]{1,3}\.){3}[0-9]{1,3})/\1/')"; ## }}} return 0; } function netContainIp(){ : ${1:?"Missing param: NET"}; : ${2:?"Missing param: IP"}; local net=$1 local ip=$2 eval local $($IPCALC -p $net) local net1=$($IPCALC -n $net) local net2=$($IPCALC -n ${ip}/${PREFIX}) { [ "x$net1" != "x$net2" ] || [ -z ${PREFIX} ]; } && return 1; return 0 } function dump_active_ips() { local tmp=( $(vzlist -Ho private) ) for tmp in ${tmp[*]} do . $tmp/ve.conf &>/dev/null || continue echo "$IP_ADDRESS" done } # expand_ipv6() - expand ipv6 representation to its full 39-character notation # @stdin - stream with ipv4 and ipv6 addresses function expand_ipv6() { [ x"$VERSION_ID" != x6 ] || { grep -v ':' return } awk ' /:/{ # Expand :: empty group n = 8 - split($1, ip, ":") + 1 if (1 < n) { tmp = "" for(; n >= 1; n--) {tmp="0000:"tmp} sub(/::/, ":"tmp, $1) } # Expand hextets split($1, ip, ":") $1 = "" for(i = 1; i <= 8; i++) { tmp = "" for(n = 4 - length(ip[i]); n; n--) { tmp = "0"tmp } $1 = $1":"tmp""ip[i] } $1 = substr($1, 2) } { print }' } function dump_active_ips_expanded(){ dump_active_ips | \ sed -r 's/[[:blank:]]+/\n/g' | \ sed -nr '/^[[:blank:]]*$/!{/^0[.]0[.]0[.]0$/!{s/[/][^/]+$//; p}}' | \ expand_ipv6 | \ sort -u } dump_active_ips_vms_expanded(){ prlctl list --vmtype vm -H -i -j | \ jq -r ".[] | select(.State == \"running\" ) | .Hardware | \ (select(.net0 != null)| .net0), (select(.net1 != null)| .net1), (select(.net2 != null)| .net2) | \ select (.type == \"routed\" ) | select (.ips != null ) | .ips" |\ sed -r 's/[[:blank:]]+/\n/g' | \ sed -nr '/^[[:blank:]]*$/!{/^0[.]0[.]0[.]0$/!{s/[/][^/]+$//; p}}' | \ expand_ipv6 | \ sort -u } function inSubnet { local ip ip_a mask netmask sub sub_ip rval start end local readonly BITMASK=0xFFFFFFFF IFS=/ read sub mask <<< "$1" IFS=. read -a sub_ip <<< "$sub" IFS=. read -a ip_a <<< "$2" if [ "x$mask" == "x" ] ; then [[ "x$sub" == "x$2" ]] && echo "1" || echo "0" return 0 fi netmask=$(($BITMASK<<$((32-$mask)) & $BITMASK)) start=0 for o in "${sub_ip[@]}" ; do start=$(($start<<8 | $o)) done start=$(($start & $netmask)) end=$(($start | ~$netmask & $BITMASK)) ip=0 for o in "${ip_a[@]}" ; do ip=$(($ip<<8 | $o)) done (( $ip >= $start )) && (( $ip <= $end )) && rval=1 || rval=0 echo "${rval}" } function detect_http_protocol(){ #jelastic.user.requests.ssl.redirect.enabled : ${1:?"Missing param: URL"}; local URL="$1" URL=$(sed -r 's@^http[s]?://@@' <<< $URL) HTTP_PROTOCOL=$(curl -Is http://${URL} | sed -rn 's/^Location:[[:blank:]]+(http[s]?):.*/\1/p') HTTP_PROTOCOL=${HTTP_PROTOCOL:-http} }