diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4005d50 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,54 @@ +#cmake_minimum_required(VERSION 3.5) +project(verify-policy) + +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) +include(Version) +include(Package) + +add_definitions(-D_GNU_SOURCE) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_C_STANDARD 11) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RelWithDebInfo) +endif() + +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set (CMAKE_INSTALL_PREFIX "/home/mesasoft/tfe" CACHE PATH "default install path" FORCE ) +endif() + +# Global compile options +option(ENABLE_PIC "Generate position independent code (necessary for shared libraries)" TRUE) +option(ENABLE_WARNING_ALL "Enable all optional warnings which are desirable for normal code" TRUE) +option(ENABLE_SANITIZE_ADDRESS "Enable AddressSanitizer" FALSE) +option(ENABLE_SANITIZE_THREAD "Enable ThreadSanitizer" FALSE) + +if(ENABLE_WARNING_ALL) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") +endif() + +if(ENABLE_SANITIZE_ADDRESS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan") +elseif(ENABLE_SANITIZE_THREAD) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -fno-omit-frame-pointer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread -fno-omit-frame-pointer") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan") +endif() + +if(ENABLE_SANITIZE_ADDRESS AND ENABLE_SANITIZE_THREAD) + message(WARNING "Both ENABLE_SANITIZE_ADDRESS and ENABLE_SANITIZE_THREAD set, only ENABLE_SANITIZE_ADDRESS effected.") +endif() + +add_custom_target("install-program" COMMAND ${CMAKE_COMMAND} ARGS -DCOMPONENT=Program -P cmake_install.cmake) +add_custom_target("install-profile" COMMAND ${CMAKE_COMMAND} ARGS -DCOMPONENT=Profile -P cmake_install.cmake) + +enable_testing() +#add_subdirectory(conf) +#add_subdirectory(resource) +add_subdirectory(vendor) +add_subdirectory(common) +add_subdirectory(platform) +add_subdirectory(scan) diff --git a/autorevision.sh b/autorevision.sh new file mode 100644 index 0000000..3baa179 --- /dev/null +++ b/autorevision.sh @@ -0,0 +1,1268 @@ +#!/bin/sh + +# Copyright (c) 2012 - 2016 dak180 and contributors. See +# https://opensource.org/licenses/mit-license.php or the included +# COPYING.md for licence terms. +# +# autorevision - extracts metadata about the head version from your +# repository. + +# Usage message. +arUsage() { + cat > "/dev/stderr" << EOF +usage: autorevision {-t output-type | -s symbol} [-o cache-file [-f] ] [-V] + Options include: + -t output-type = specify output type + -s symbol = specify symbol output + -o cache-file = specify cache file location + -f = force the use of cache data + -U = check for untracked files in svn + -V = emit version and exit + -? = help message + +The following are valid output types: + clojure = clojure file + c = C/C++ file + h = Header for use with c/c++ + hpp = Alternate C++ header strings with namespace + ini = INI file + java = Java file + javaprop = Java properties file + js = javascript file + json = JSON file + lua = Lua file + m4 = m4 file + matlab = matlab file + octave = octave file + php = PHP file + pl = Perl file + py = Python file + rpm = rpm file + scheme = scheme file + sh = Bash sytax + swift = Swift file + tex = (La)TeX file + xcode = Header useful for populating info.plist files + cmake = CMake file + + +The following are valid symbols: + VCS_TYPE + VCS_BASENAME + VCS_UUID + VCS_NUM + VCS_DATE + VCS_BRANCH + VCS_TAG + VCS_TICK + VCS_EXTRA + VCS_FULL_HASH + VCS_SHORT_HASH + VCS_WC_MODIFIED + VCS_ACTION_STAMP +EOF + exit 1 +} + +# Config +ARVERSION="&&ARVERSION&&" +TARGETFILE="/dev/stdout" +while getopts ":t:o:s:VfU" OPTION; do + case "${OPTION}" in + t) + AFILETYPE="${OPTARG}" + ;; + o) + CACHEFILE="${OPTARG}" + ;; + f) + CACHEFORCE="1" + ;; + s) + VAROUT="${OPTARG}" + ;; + U) + UNTRACKEDFILES="1" + ;; + V) + echo "autorevision ${ARVERSION}" + exit 0 + ;; + ?) + # If an unknown flag is used (or -?): + arUsage + ;; + esac +done + +if [ ! -z "${VAROUT}" ] && [ ! -z "${AFILETYPE}" ]; then + # If both -s and -t are specified: + echo "error: Improper argument combination." 1>&2 + exit 1 +elif [ -z "${VAROUT}" ] && [ -z "${AFILETYPE}" ]; then + # If neither -s or -t are specified: + arUsage +elif [ -z "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then + # If -f is specified without -o: + arUsage +elif [ ! -f "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then + # If we are forced to use the cache but it does not exist. + echo "error: Cache forced but no cache found." 1>&2 + exit 1 +fi + +# Make sure that the path we are given is one we can source +# (dash, we are looking at you). +if [ ! -z "${CACHEFILE}" ] && ! echo "${CACHEFILE}" | grep -q '^\.*/'; then + CACHEFILE="./${CACHEFILE}" +fi + +GENERATED_HEADER="Generated by autorevision - do not hand-hack!" + +# Functions to extract data from different repo types. +# For git repos +# shellcheck disable=SC2039,SC2164,SC2155 +gitRepo() { + local oldPath="${PWD}" + + cd "$(git rev-parse --show-toplevel)" + + VCS_TYPE="git" + + VCS_BASENAME="$(basename "${PWD}")" + + VCS_UUID="$(git rev-list --max-parents=0 --date-order --reverse HEAD 2>/dev/null | sed -n 1p)" + if [ -z "${VCS_UUID}" ]; then + VCS_UUID="$(git rev-list --topo-order HEAD | tail -n 1)" + fi + + # Is the working copy clean? + test -z "$(git status --untracked-files=normal --porcelain)" + VCS_WC_MODIFIED="${?}" + + # Enumeration of changesets + VCS_NUM="$(git rev-list --count HEAD 2>/dev/null)" + if [ -z "${VCS_NUM}" ]; then + echo "warning: Counting the number of revisions may be slower due to an outdated git version less than 1.7.2.3. If something breaks, please update it." 1>&2 + VCS_NUM="$(git rev-list HEAD | wc -l)" + fi + + # This may be a git-svn remote. If so, report the Subversion revision. + if [ -z "$(git config svn-remote.svn.url 2>/dev/null)" ]; then + # The full revision hash + VCS_FULL_HASH="$(git rev-parse HEAD)" + + # The short hash + VCS_SHORT_HASH="$(echo "${VCS_FULL_HASH}" | cut -b 1-7)" + else + # The git-svn revision number + VCS_FULL_HASH="$(git svn find-rev HEAD)" + VCS_SHORT_HASH="${VCS_FULL_HASH}" + fi + + # Current branch + VCS_BRANCH="$(git rev-parse --symbolic-full-name --verify "$(git name-rev --name-only --no-undefined HEAD 2>/dev/null)" 2>/dev/null | sed -e 's:refs/heads/::' | sed -e 's:refs/::')" + + # Cache the description + local DESCRIPTION="$(git describe --long --tags 2>/dev/null)" + + # Current or last tag ancestor (empty if no tags) + VCS_TAG="$(echo "${DESCRIPTION}" | sed -e "s:-g${VCS_SHORT_HASH}\$::" -e 's:-[0-9]*$::')" + + # Distance to last tag or an alias of VCS_NUM if there is no tag + if [ ! -z "${DESCRIPTION}" ]; then + VCS_TICK="$(echo "${DESCRIPTION}" | sed -e "s:${VCS_TAG}-::" -e "s:-g${VCS_SHORT_HASH}::")" + else + VCS_TICK="${VCS_NUM}" + fi + + # Date of the current commit + VCS_DATE="$(TZ=UTC git show -s --date=iso-strict-local --pretty=format:%ad | sed -e 's|+00:00|Z|')" + if [ -z "${VCS_DATE}" ]; then + echo "warning: Action stamps require git version 2.7+." 1>&2 + VCS_DATE="$(git log -1 --pretty=format:%ci | sed -e 's: :T:' -e 's: ::' -e 's|+00:00|Z|')" + local ASdis="1" + fi + + # Action Stamp + if [ -z "${ASdis}" ]; then + VCS_ACTION_STAMP="${VCS_DATE}!$(git show -s --pretty=format:%cE)" + else + VCS_ACTION_STAMP="" + fi + + cd "${oldPath}" +} + +# For hg repos +# shellcheck disable=SC2039,SC2164 +hgRepo() { + local oldPath="${PWD}" + + cd "$(hg root)" + + VCS_TYPE="hg" + + VCS_BASENAME="$(basename "${PWD}")" + + VCS_UUID="$(hg log -r "0" -l 1 --template '{node}\n')" + + # Is the working copy clean? + test -z "$(hg status -duram)" + VCS_WC_MODIFIED="${?}" + + # Enumeration of changesets + VCS_NUM="$(hg id -n | tr -d '+')" + + # The full revision hash + VCS_FULL_HASH="$(hg log -r "${VCS_NUM}" -l 1 --template '{node}\n')" + + # The short hash + VCS_SHORT_HASH="$(hg id -i | tr -d '+')" + + # Current bookmark (bookmarks are roughly equivalent to git's branches) + # or branch if no bookmark + VCS_BRANCH="$(hg id -B | cut -d ' ' -f 1)" + # Fall back to the branch if there are no bookmarks + if [ -z "${VCS_BRANCH}" ]; then + VCS_BRANCH="$(hg id -b)" + fi + + # Current or last tag ancestor (excluding auto tags, empty if no tags) + VCS_TAG="$(hg log -r "${VCS_NUM}" -l 1 --template '{latesttag}\n' 2>/dev/null | sed -e 's:qtip::' -e 's:tip::' -e 's:qbase::' -e 's:qparent::' -e "s:$(hg --config 'extensions.color=' --config 'extensions.mq=' --color never qtop 2>/dev/null)::" | cut -d ' ' -f 1)" + + # Distance to last tag or an alias of VCS_NUM if there is no tag + if [ ! -z "${VCS_TAG}" ]; then + VCS_TICK="$(hg log -r "${VCS_NUM}" -l 1 --template '{latesttagdistance}\n' 2>/dev/null)" + else + VCS_TICK="${VCS_NUM}" + fi + + # Date of the current commit + VCS_DATE="$(hg log -r "${VCS_NUM}" -l 1 --template '{date|isodatesec}\n' 2>/dev/null | sed -e 's: :T:' -e 's: ::' -e 's|+00:00|Z|')" + + # Action Stamp + VCS_ACTION_STAMP="$(TZ=UTC hg log -r "${VCS_NUM}" -l 1 --template '{date|localdate|rfc3339date}\n' 2>/dev/null | sed -e 's|+00:00|Z|')!$(hg log -r "${VCS_NUM}" -l 1 --template '{author|email}\n' 2>/dev/null)" + + cd "${oldPath}" +} + +# For bzr repos +# shellcheck disable=SC2039,SC2164 +bzrRepo() { + local oldPath="${PWD}" + + cd "$(bzr root)" + + VCS_TYPE="bzr" + + VCS_BASENAME="$(basename "${PWD}")" + + # Currently unimplemented because more investigation is needed. + VCS_UUID="" + + # Is the working copy clean? + bzr version-info --custom --template='{clean}\n' | grep -q '1' + VCS_WC_MODIFIED="${?}" + + # Enumeration of changesets + VCS_NUM="$(bzr revno)" + + # The full revision hash + VCS_FULL_HASH="$(bzr version-info --custom --template='{revision_id}\n')" + + # The short hash + VCS_SHORT_HASH="${VCS_NUM}" + + # Nick of the current branch + VCS_BRANCH="$(bzr nick)" + + # Current or last tag ancestor (excluding auto tags, empty if no tags) + VCS_TAG="$(bzr tags --sort=time | sed '/?$/d' | tail -n1 | cut -d ' ' -f1)" + + # Distance to last tag or an alias of VCS_NUM if there is no tag + if [ ! -z "${VCS_TAG}" ]; then + VCS_TICK="$(bzr log --line -r "tag:${VCS_TAG}.." | tail -n +2 | wc -l | sed -e 's:^ *::')" + else + VCS_TICK="${VCS_NUM}" + fi + + # Date of the current commit + VCS_DATE="$(bzr version-info --custom --template='{date}\n' | sed -e 's: :T:' -e 's: ::')" + + # Action Stamp + # Currently unimplemented because more investigation is needed. + VCS_ACTION_STAMP="" + + cd "${oldPath}" +} + +# For svn repos +# shellcheck disable=SC2039,SC2164,SC2155 +svnRepo() { + local oldPath="${PWD}" + + VCS_TYPE="svn" + + case "${PWD}" in + /*trunk*|/*branches*|/*tags*) + local fn="${PWD}" + while [ "$(basename "${fn}")" != 'trunk' ] && [ "$(basename "${fn}")" != 'branches' ] && [ "$(basename "${fn}")" != 'tags' ] && [ "$(basename "${fn}")" != '/' ]; do + local fn="$(dirname "${fn}")" + done + local fn="$(dirname "${fn}")" + if [ "${fn}" = '/' ]; then + VCS_BASENAME="$(basename "${PWD}")" + else + VCS_BASENAME="$(basename "${fn}")" + fi + ;; + *) VCS_BASENAME="$(basename "${PWD}")" ;; + esac + + VCS_UUID="$(svn info --xml | sed -n -e 's:::' -e 's:::p')" + + # Cache svnversion output + local SVNVERSION="$(svnversion)" + + # Is the working copy clean? + echo "${SVNVERSION}" | grep -q "M" + case "${?}" in + 0) + VCS_WC_MODIFIED="1" + ;; + 1) + if [ ! -z "${UNTRACKEDFILES}" ]; then + # `svnversion` does not detect untracked files and `svn status` is really slow, so only run it if we really have to. + if [ -z "$(svn status)" ]; then + VCS_WC_MODIFIED="0" + else + VCS_WC_MODIFIED="1" + fi + else + VCS_WC_MODIFIED="0" + fi + ;; + esac + + # Enumeration of changesets + VCS_NUM="$(echo "${SVNVERSION}" | cut -d : -f 1 | sed -e 's:M::' -e 's:S::' -e 's:P::')" + + # The full revision hash + VCS_FULL_HASH="${SVNVERSION}" + + # The short hash + VCS_SHORT_HASH="${VCS_NUM}" + + # Current branch + case "${PWD}" in + /*trunk*|/*branches*|/*tags*) + local lastbase="" + local fn="${PWD}" + while : + do + base="$(basename "${fn}")" + if [ "${base}" = 'trunk' ]; then + VCS_BRANCH='trunk' + break + elif [ "${base}" = 'branches' ] || [ "${base}" = 'tags' ]; then + VCS_BRANCH="${lastbase}" + break + elif [ "${base}" = '/' ]; then + VCS_BRANCH="" + break + fi + local lastbase="${base}" + local fn="$(dirname "${fn}")" + done + ;; + *) VCS_BRANCH="" ;; + esac + + # Current or last tag ancestor (empty if no tags). But "current + # tag" can't be extracted reliably because Subversion doesn't + # have tags the way other VCSes do. + VCS_TAG="" + VCS_TICK="" + + # Date of the current commit + VCS_DATE="$(svn info --xml | sed -n -e 's:::' -e 's:::p')" + + # Action Stamp + VCS_ACTION_STAMP="${VCS_DATE}!$(svn log --xml -l 1 -r "${VCS_SHORT_HASH}" | sed -n -e 's:::' -e 's:::p')" + + cd "${oldPath}" +} + + +# Functions to output data in different formats. +# For bash output +shOutput() { + cat > "${TARGETFILE}" << EOF +# ${GENERATED_HEADER} + +VCS_TYPE="${VCS_TYPE}" +VCS_BASENAME="${VCS_BASENAME}" +VCS_UUID="${VCS_UUID}" +VCS_NUM="${VCS_NUM}" +VCS_DATE="${VCS_DATE}" +VCS_BRANCH="${VCS_BRANCH}" +VCS_TAG="${VCS_TAG}" +VCS_TICK="${VCS_TICK}" +VCS_EXTRA="${VCS_EXTRA}" + +VCS_ACTION_STAMP="${VCS_ACTION_STAMP}" +VCS_FULL_HASH="${VCS_FULL_HASH}" +VCS_SHORT_HASH="${VCS_SHORT_HASH}" + +VCS_WC_MODIFIED="${VCS_WC_MODIFIED}" + +# end +EOF +} + +# For source C output +cOutput() { + cat > "${TARGETFILE}" << EOF +/* ${GENERATED_HEADER} */ + +const char *VCS_TYPE = "${VCS_TYPE}"; +const char *VCS_BASENAME = "${VCS_BASENAME}"; +const char *VCS_UUID = "${VCS_UUID}"; +const int VCS_NUM = ${VCS_NUM}; +const char *VCS_DATE = "${VCS_DATE}"; +const char *VCS_BRANCH = "${VCS_BRANCH}"; +const char *VCS_TAG = "${VCS_TAG}"; +const int VCS_TICK = ${VCS_TICK}; +const char *VCS_EXTRA = "${VCS_EXTRA}"; + +const char *VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"; +const char *VCS_FULL_HASH = "${VCS_FULL_HASH}"; +const char *VCS_SHORT_HASH = "${VCS_SHORT_HASH}"; + +const int VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; + +/* end */ +EOF +} + +# For header output +hOutput() { + cat > "${TARGETFILE}" << EOF +/* ${GENERATED_HEADER} */ +#ifndef AUTOREVISION_H +#define AUTOREVISION_H + +#define VCS_TYPE "${VCS_TYPE}" +#define VCS_BASENAME "${VCS_BASENAME}" +#define VCS_UUID "${VCS_UUID}" +#define VCS_NUM ${VCS_NUM} +#define VCS_DATE "${VCS_DATE}" +#define VCS_BRANCH "${VCS_BRANCH}" +#define VCS_TAG "${VCS_TAG}" +#define VCS_TICK ${VCS_TICK} +#define VCS_EXTRA "${VCS_EXTRA}" + +#define VCS_ACTION_STAMP "${VCS_ACTION_STAMP}" +#define VCS_FULL_HASH "${VCS_FULL_HASH}" +#define VCS_SHORT_HASH "${VCS_SHORT_HASH}" + +#define VCS_WC_MODIFIED ${VCS_WC_MODIFIED} + +#endif + +/* end */ +EOF +} + +# A header output for use with xcode to populate info.plist strings +xcodeOutput() { + cat > "${TARGETFILE}" << EOF +/* ${GENERATED_HEADER} */ +#ifndef AUTOREVISION_H +#define AUTOREVISION_H + +#define VCS_TYPE ${VCS_TYPE} +#define VCS_BASENAME ${VCS_BASENAME} +#define VCS_UUID ${VCS_UUID} +#define VCS_NUM ${VCS_NUM} +#define VCS_DATE ${VCS_DATE} +#define VCS_BRANCH ${VCS_BRANCH} +#define VCS_TAG ${VCS_TAG} +#define VCS_TICK ${VCS_TICK} +#define VCS_EXTRA ${VCS_EXTRA} + +#define VCS_ACTION_STAMP ${VCS_ACTION_STAMP} +#define VCS_FULL_HASH ${VCS_FULL_HASH} +#define VCS_SHORT_HASH ${VCS_SHORT_HASH} + +#define VCS_WC_MODIFIED ${VCS_WC_MODIFIED} + +#endif + +/* end */ +EOF +} + +# For Swift output +swiftOutput() { + case "${VCS_WC_MODIFIED}" in + 0) VCS_WC_MODIFIED="false" ;; + 1) VCS_WC_MODIFIED="true" ;; + esac + # For values that may not exist depending on the type of repo we + # have read from, set them to `nil` when they are empty. + if [ -z "${VCS_UUID}" ]; then + VCS_UUID="nil" + else + VCS_UUID="\"${VCS_UUID}\"" + fi + if [ -z "${VCS_TAG}" ]; then + VCS_TAG="nil" + else + VCS_TAG="\"${VCS_TAG}\"" + fi + : "${VCS_TICK:="nil"}" + if [ -z "${VCS_EXTRA}" ]; then + VCS_EXTRA="nil" + else + VCS_EXTRA="\"${VCS_EXTRA}\"" + fi + if [ -z "${VCS_ACTION_STAMP}" ]; then + VCS_ACTION_STAMP="nil" + else + VCS_ACTION_STAMP="\"${VCS_ACTION_STAMP}\"" + fi + cat > "${TARGETFILE}" << EOF +/* ${GENERATED_HEADER} */ + +let VCS_TYPE = "${VCS_TYPE}" +let VCS_BASENAME = "${VCS_BASENAME}" +let VCS_UUID: String? = ${VCS_UUID} +let VCS_NUM: Int = ${VCS_NUM} +let VCS_DATE = "${VCS_DATE}" +let VCS_BRANCH: String = "${VCS_BRANCH}" +let VCS_TAG: String? = ${VCS_TAG} +let VCS_TICK: Int? = ${VCS_TICK} +let VCS_EXTRA: String? = ${VCS_EXTRA} + +let VCS_ACTION_STAMP: String? = ${VCS_ACTION_STAMP} +let VCS_FULL_HASH: String = "${VCS_FULL_HASH}" +let VCS_SHORT_HASH: String = "${VCS_SHORT_HASH}" + +let VCS_WC_MODIFIED: Bool = ${VCS_WC_MODIFIED} + +/* end */ +EOF +} + +# For Python output +pyOutput() { + case "${VCS_WC_MODIFIED}" in + 0) VCS_WC_MODIFIED="False" ;; + 1) VCS_WC_MODIFIED="True" ;; + esac + cat > "${TARGETFILE}" << EOF +# ${GENERATED_HEADER} + +VCS_TYPE = "${VCS_TYPE}" +VCS_BASENAME = "${VCS_BASENAME}" +VCS_UUID = "${VCS_UUID}" +VCS_NUM = ${VCS_NUM} +VCS_DATE = "${VCS_DATE}" +VCS_BRANCH = "${VCS_BRANCH}" +VCS_TAG = "${VCS_TAG}" +VCS_TICK = ${VCS_TICK} +VCS_EXTRA = "${VCS_EXTRA}" + +VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}" +VCS_FULL_HASH = "${VCS_FULL_HASH}" +VCS_SHORT_HASH = "${VCS_SHORT_HASH}" + +VCS_WC_MODIFIED = ${VCS_WC_MODIFIED} + +# end +EOF +} + +# For Perl output +plOutput() { + cat << EOF +# ${GENERATED_HEADER} + +\$VCS_TYPE = '${VCS_TYPE}'; +\$VCS_BASENAME = '${VCS_BASENAME}'; +\$VCS_UUID = '${VCS_UUID}'; +\$VCS_NUM = ${VCS_NUM}; +\$VCS_DATE = '${VCS_DATE}'; +\$VCS_BRANCH = '${VCS_BRANCH}'; +\$VCS_TAG = '${VCS_TAG}'; +\$VCS_TICK = ${VCS_TICK}; +\$VCS_EXTRA = '${VCS_EXTRA}'; + +\$VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}'; +\$VCS_FULL_HASH = '${VCS_FULL_HASH}'; +\$VCS_SHORT_HASH = '${VCS_SHORT_HASH}'; + +\$VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; + +# end +1; +EOF +} + +# For lua output +luaOutput() { + case "${VCS_WC_MODIFIED}" in + 0) VCS_WC_MODIFIED="false" ;; + 1) VCS_WC_MODIFIED="true" ;; + esac + cat > "${TARGETFILE}" << EOF +-- ${GENERATED_HEADER} + +VCS_TYPE = "${VCS_TYPE}" +VCS_BASENAME = "${VCS_BASENAME}" +VCS_UUID = "${VCS_UUID}" +VCS_NUM = ${VCS_NUM} +VCS_DATE = "${VCS_DATE}" +VCS_BRANCH = "${VCS_BRANCH}" +VCS_TAG = "${VCS_TAG}" +VCS_TICK = ${VCS_TICK} +VCS_EXTRA = "${VCS_EXTRA}" + +VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}" +VCS_FULL_HASH = "${VCS_FULL_HASH}" +VCS_SHORT_HASH = "${VCS_SHORT_HASH}" + +VCS_WC_MODIFIED = ${VCS_WC_MODIFIED} + +-- end +EOF +} + +# For php output +phpOutput() { + case "${VCS_WC_MODIFIED}" in + 0) VCS_WC_MODIFIED="false" ;; + 1) VCS_WC_MODIFIED="true" ;; + esac + cat > "${TARGETFILE}" << EOF + "${VCS_TYPE}", + "VCS_BASENAME" => "${VCS_BASENAME}", + "VCS_UUID" => "${VCS_UUID}", + "VCS_NUM" => ${VCS_NUM}, + "VCS_DATE" => "${VCS_DATE}", + "VCS_BRANCH" => "${VCS_BRANCH}", + "VCS_TAG" => "${VCS_TAG}", + "VCS_TICK" => ${VCS_TICK}, + "VCS_EXTRA" => "${VCS_EXTRA}", + "VCS_ACTION_STAMP" => "${VCS_ACTION_STAMP}", + "VCS_FULL_HASH" => "${VCS_FULL_HASH}", + "VCS_SHORT_HASH" => "${VCS_SHORT_HASH}", + "VCS_WC_MODIFIED" => ${VCS_WC_MODIFIED} +); + +# end +?> +EOF +} + +# For ini output +iniOutput() { + case "${VCS_WC_MODIFIED}" in + 0) VCS_WC_MODIFIED="false" ;; + 1) VCS_WC_MODIFIED="true" ;; + esac + cat > "${TARGETFILE}" << EOF +; ${GENERATED_HEADER} +[VCS] +VCS_TYPE = "${VCS_TYPE}" +VCS_BASENAME = "${VCS_BASENAME}" +VCS_UUID = "${VCS_UUID}" +VCS_NUM = ${VCS_NUM} +VCS_DATE = "${VCS_DATE}" +VCS_BRANCH = "${VCS_BRANCH}" +VCS_TAG = "${VCS_TAG}" +VCS_TICK = ${VCS_TICK} +VCS_EXTRA = "${VCS_EXTRA}" +VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}" +VCS_FULL_HASH = "${VCS_FULL_HASH}" +VCS_SHORT_HASH = "${VCS_SHORT_HASH}" +VCS_WC_MODIFIED = ${VCS_WC_MODIFIED} +; end +EOF +} + +# For javascript output +jsOutput() { + case "${VCS_WC_MODIFIED}" in + 1) VCS_WC_MODIFIED="true" ;; + 0) VCS_WC_MODIFIED="false" ;; + esac + cat > "${TARGETFILE}" << EOF +/** ${GENERATED_HEADER} */ + +var autorevision = { + VCS_TYPE: "${VCS_TYPE}", + VCS_BASENAME: "${VCS_BASENAME}", + VCS_UUID: "${VCS_UUID}", + VCS_NUM: ${VCS_NUM}, + VCS_DATE: "${VCS_DATE}", + VCS_BRANCH: "${VCS_BRANCH}", + VCS_TAG: "${VCS_TAG}", + VCS_TICK: ${VCS_TICK}, + VCS_EXTRA: "${VCS_EXTRA}", + + VCS_ACTION_STAMP: "${VCS_ACTION_STAMP}", + VCS_FULL_HASH: "${VCS_FULL_HASH}", + VCS_SHORT_HASH: "${VCS_SHORT_HASH}", + + VCS_WC_MODIFIED: ${VCS_WC_MODIFIED} +}; + +/** Node.js compatibility */ +if (typeof module !== 'undefined') { + module.exports = autorevision; +} + +/** end */ +EOF +} + +# For JSON output +jsonOutput() { + case "${VCS_WC_MODIFIED}" in + 1) VCS_WC_MODIFIED="true" ;; + 0) VCS_WC_MODIFIED="false" ;; + esac + cat > "${TARGETFILE}" << EOF +{ + "_comment": "${GENERATED_HEADER}", + "VCS_TYPE": "${VCS_TYPE}", + "VCS_BASENAME": "${VCS_BASENAME}", + "VCS_UUID": "${VCS_UUID}", + "VCS_NUM": ${VCS_NUM}, + "VCS_DATE": "${VCS_DATE}", + "VCS_BRANCH":"${VCS_BRANCH}", + "VCS_TAG": "${VCS_TAG}", + "VCS_TICK": ${VCS_TICK}, + "VCS_EXTRA": "${VCS_EXTRA}", + + "VCS_ACTION_STAMP": "${VCS_ACTION_STAMP}", + "VCS_FULL_HASH": "${VCS_FULL_HASH}", + "VCS_SHORT_HASH": "${VCS_SHORT_HASH}", + + "VCS_WC_MODIFIED": ${VCS_WC_MODIFIED} +} +EOF +} + +# For Java output +javaOutput() { + case "${VCS_WC_MODIFIED}" in + 1) VCS_WC_MODIFIED="true" ;; + 0) VCS_WC_MODIFIED="false" ;; + esac + cat > "${TARGETFILE}" << EOF +/* ${GENERATED_HEADER} */ + +public class autorevision { + public static final String VCS_TYPE = "${VCS_TYPE}"; + public static final String VCS_BASENAME = "${VCS_BASENAME}"; + public static final String VCS_UUID = "${VCS_UUID}"; + public static final long VCS_NUM = ${VCS_NUM}; + public static final String VCS_DATE = "${VCS_DATE}"; + public static final String VCS_BRANCH = "${VCS_BRANCH}"; + public static final String VCS_TAG = "${VCS_TAG}"; + public static final long VCS_TICK = ${VCS_TICK}; + public static final String VCS_EXTRA = "${VCS_EXTRA}"; + + public static final String VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"; + public static final String VCS_FULL_HASH = "${VCS_FULL_HASH}"; + public static final String VCS_SHORT_HASH = "${VCS_SHORT_HASH}"; + + public static final boolean VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; +} +EOF +} + +# For Java properties output +javapropOutput() { + case "${VCS_WC_MODIFIED}" in + 1) VCS_WC_MODIFIED="true" ;; + 0) VCS_WC_MODIFIED="false" ;; + esac + cat > "${TARGETFILE}" << EOF +# ${GENERATED_HEADER} + +VCS_TYPE=${VCS_TYPE} +VCS_BASENAME=${VCS_BASENAME} +VCS_UUID=${VCS_UUID} +VCS_NUM=${VCS_NUM} +VCS_DATE=${VCS_DATE} +VCS_BRANCH=${VCS_BRANCH} +VCS_TAG=${VCS_TAG} +VCS_TICK=${VCS_TICK} +VCS_EXTRA=${VCS_EXTRA} + +VCS_ACTION_STAMP=${VCS_ACTION_STAMP} +VCS_FULL_HASH=${VCS_FULL_HASH} +VCS_SHORT_HASH=${VCS_SHORT_HASH} + +VCS_WC_MODIFIED=${VCS_WC_MODIFIED} +EOF +} + +# For m4 output +m4Output() { + cat > "${TARGETFILE}" << EOF +dnl ${GENERATED_HEADER} +define(\`VCS_TYPE', \`${VCS_TYPE}')dnl +define(\`VCS_BASENAME', \`${VCS_BASENAME}')dnl +define(\`VCS_UUID', \`${VCS_UUID}')dnl +define(\`VCS_NUM', \`${VCS_NUM}')dnl +define(\`VCS_DATE', \`${VCS_DATE}')dnl +define(\`VCS_BRANCH', \`${VCS_BRANCH}')dnl +define(\`VCS_TAG', \`${VCS_TAG}')dnl +define(\`VCS_TICK', \`${VCS_TICK}')dnl +define(\`VCS_EXTRA', \`${VCS_EXTRA}')dnl +define(\`VCS_ACTIONSTAMP', \`${VCS_ACTION_STAMP}')dnl +define(\`VCS_FULLHASH', \`${VCS_FULL_HASH}')dnl +define(\`VCS_SHORTHASH', \`${VCS_SHORT_HASH}')dnl +define(\`VCS_WC_MODIFIED', \`${VCS_WC_MODIFIED}')dnl +EOF +} + +# For (La)TeX output +texOutput() { + case "${VCS_WC_MODIFIED}" in + 0) VCS_WC_MODIFIED="false" ;; + 1) VCS_WC_MODIFIED="true" ;; + esac + cat > "${TARGETFILE}" << EOF +% ${GENERATED_HEADER} +\def \vcsType {${VCS_TYPE}} +\def \vcsBasename {${VCS_BASENAME}} +\def \vcsUUID {${VCS_UUID}} +\def \vcsNum {${VCS_NUM}} +\def \vcsDate {${VCS_DATE}} +\def \vcsBranch {${VCS_BRANCH}} +\def \vcsTag {${VCS_TAG}} +\def \vcsTick {${VCS_TICK}} +\def \vcsExtra {${VCS_EXTRA}} +\def \vcsACTIONSTAMP {${VCS_ACTION_STAMP}} +\def \vcsFullHash {${VCS_FULL_HASH}} +\def \vcsShortHash {${VCS_SHORT_HASH}} +\def \vcsWCModified {${VCS_WC_MODIFIED}} +\endinput +EOF +} + +# For scheme output +schemeOutput() { + case "${VCS_WC_MODIFIED}" in + 0) VCS_WC_MODIFIED="#f" ;; + 1) VCS_WC_MODIFIED="#t" ;; + esac + cat > "${TARGETFILE}" << EOF +;; ${GENERATED_HEADER} +(define VCS_TYPE "${VCS_TYPE}") +(define VCS_BASENAME "${VCS_BASENAME}") +(define VCS_UUID "${VCS_UUID}") +(define VCS_NUM ${VCS_NUM}) +(define VCS_DATE "${VCS_DATE}") +(define VCS_BRANCH "${VCS_BRANCH}") +(define VCS_TAG "${VCS_TAG}") +(define VCS_TICK ${VCS_TICK}) +(define VCS_EXTRA "${VCS_EXTRA}") + +(define VCS_ACTION_STAMP "${VCS_ACTION_STAMP}") +(define VCS_FULL_HASH "${VCS_FULL_HASH}") +(define VCS_SHORT_HASH "${VCS_SHORT_HASH}") + +(define VCS_WC_MODIFIED ${VCS_WC_MODIFIED}) +;; end +EOF +} + +# For clojure output +clojureOutput() { + case "${VCS_WC_MODIFIED}" in + 0) VCS_WC_MODIFIED="false" ;; + 1) VCS_WC_MODIFIED="true" ;; + esac + cat > "${TARGETFILE}" << EOF +;; ${GENERATED_HEADER} +(def VCS_TYPE "${VCS_TYPE}") +(def VCS_BASENAME "${VCS_BASENAME}") +(def VCS_UUID "${VCS_UUID}") +(def VCS_NUM ${VCS_NUM}) +(def VCS_DATE "${VCS_DATE}") +(def VCS_BRANCH "${VCS_BRANCH}") +(def VCS_TAG "${VCS_TAG}") +(def VCS_TICK ${VCS_TICK}) +(def VCS_EXTRA "${VCS_EXTRA}") + +(def VCS_ACTION_STAMP "${VCS_ACTION_STAMP}") +(def VCS_FULL_HASH "${VCS_FULL_HASH}") +(def VCS_SHORT_HASH "${VCS_SHORT_HASH}") + +(def VCS_WC_MODIFIED ${VCS_WC_MODIFIED}) +;; end +EOF +} + +# For rpm spec file output +rpmOutput() { + cat > "${TARGETFILE}" << EOF +# ${GENERATED_HEADER} +$([ "${VCS_TYPE}" ] && echo "%define vcs_type ${VCS_TYPE}") +$([ "${VCS_BASENAME}" ] && echo "%define vcs_basename ${VCS_BASENAME}") +$([ "${VCS_UUID}" ] && echo "%define vcs_uuid ${VCS_UUID}") +$([ "${VCS_NUM}" ] && echo "%define vcs_num ${VCS_NUM}") +$([ "${VCS_DATE}" ] && echo "%define vcs_date ${VCS_DATE}") +$([ "${VCS_BRANCH}" ] && echo "%define vcs_branch ${VCS_BRANCH}") +$([ "${VCS_TAG}" ] && echo "%define vcs_tag ${VCS_TAG}") +$([ "${VCS_TICK}" ] && echo "%define vcs_tick ${VCS_TICK}") +$([ "${VCS_EXTRA}" ] && echo "%define vcs_extra ${VCS_EXTRA}") + +$([ "${VCS_ACTION_STAMP}" ] && echo "%define vcs_action_stamp ${VCS_ACTION_STAMP}") +$([ "${VCS_FULL_HASH}" ] && echo "%define vcs_full_hash ${VCS_FULL_HASH}") +$([ "${VCS_SHORT_HASH}" ] && echo "%define vcs_short_hash ${VCS_SHORT_HASH}") + +$([ "${VCS_WC_MODIFIED}" ] && echo "%define vcs_wc_modified ${VCS_WC_MODIFIED}") +# end +EOF +} + +# shellcheck disable=SC2155,SC2039 +hppOutput() { + local NAMESPACE="$(echo "${VCS_BASENAME}" | sed -e 's:_::g' | tr '[:lower:]' '[:upper:]')" + cat > "${TARGETFILE}" << EOF +/* ${GENERATED_HEADER} */ + +#ifndef ${NAMESPACE}_AUTOREVISION_H +#define ${NAMESPACE}_AUTOREVISION_H + +#include + +namespace $(echo "${NAMESPACE}" | tr '[:upper:]' '[:lower:]') +{ + const std::string VCS_TYPE = "${VCS_TYPE}"; + const std::string VCS_BASENAME = "${VCS_BASENAME}"; + const std::string VCS_UUID = "${VCS_UUID}"; + const int VCS_NUM = ${VCS_NUM}; + const std::string VCS_DATE = "${VCS_DATE}"; + const std::string VCS_BRANCH = "${VCS_BRANCH}"; + const std::string VCS_TAG = "${VCS_TAG}"; + const int VCS_TICK = ${VCS_TICK}; + const std::string VCS_EXTRA = "${VCS_EXTRA}"; + + const std::string VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"; + const std::string VCS_FULL_HASH = "${VCS_FULL_HASH}"; + const std::string VCS_SHORT_HASH = "${VCS_SHORT_HASH}"; + + const int VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; +} + +#endif + +/* end */ +EOF +} + +matlabOutput() { + case "${VCS_WC_MODIFIED}" in + 0) VCS_WC_MODIFIED="FALSE" ;; + 1) VCS_WC_MODIFIED="TRUE" ;; + esac + cat > "${TARGETFILE}" << EOF +% ${GENERATED_HEADER} + +VCS_TYPE = '${VCS_TYPE}'; +VCS_BASENAME = '${VCS_BASENAME}'; +VCS_UUID = '${VCS_UUID}'; +VCS_NUM = ${VCS_NUM}; +VCS_DATE = '${VCS_DATE}'; +VCS_BRANCH = '${VCS_BRANCH}'; +VCS_TAG = '${VCS_TAG}'; +VCS_TICK = ${VCS_TICK}; +VCS_EXTRA = '${VCS_EXTRA}'; + +VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}'; +VCS_FULL_HASH = '${VCS_FULL_HASH}'; +VCS_SHORT_HASH = '${VCS_SHORT_HASH}'; + +VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; + +% end +EOF +} + +octaveOutput() { + cat > "${TARGETFILE}" << EOF +% ${GENERATED_HEADER} + +VCS_TYPE = '${VCS_TYPE}'; +VCS_BASENAME = '${VCS_BASENAME}'; +VCS_UUID = '${VCS_UUID}'; +VCS_NUM = ${VCS_NUM}; +VCS_DATE = '${VCS_DATE}'; +VCS_BRANCH = '${VCS_BRANCH}'; +VCS_TAG = '${VCS_TAG}'; +VCS_TICK = ${VCS_TICK}; +VCS_EXTRA = '${VCS_EXTRA}'; + +VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}'; +VCS_FULL_HASH = '${VCS_FULL_HASH}'; +VCS_SHORT_HASH = '${VCS_SHORT_HASH}'; + +VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; + +% end +EOF +} + +cmakeOutput() { + cat > "${TARGETFILE}" << EOF +# ${GENERATED_HEADER} + +set(VCS_TYPE ${VCS_TYPE}) +set(VCS_BASENAME ${VCS_BASENAME}) +set(VCS_UUID ${VCS_UUID}) +set(VCS_NUM ${VCS_NUM}) +set(VCS_DATE ${VCS_DATE}) +set(VCS_BRANCH ${VCS_BRANCH}) +set(VCS_TAG ${VCS_TAG}) +set(VCS_TICK ${VCS_TICK}) +set(VCS_EXTRA ${VCS_EXTRA}) + +set(VCS_ACTION_STAMP ${VCS_ACTION_STAMP}) +set(VCS_FULL_HASH ${VCS_FULL_HASH}) +set(VCS_SHORT_HASH ${VCS_SHORT_HASH}) + +set(VCS_WC_MODIFIED ${VCS_WC_MODIFIED}) + +# end +EOF +} + + +# Helper functions +# Count path segments +# shellcheck disable=SC2039 +pathSegment() { + local pathz="${1}" + local depth="0" + + if [ ! -z "${pathz}" ]; then + # Continue until we are at / or there are no path separators left. + while [ ! "${pathz}" = "/" ] && [ ! "${pathz}" = "$(echo "${pathz}" | sed -e 's:/::')" ]; do + pathz="$(dirname "${pathz}")" + depth="$((depth+1))" + done + fi + echo "${depth}" +} + +# Largest of four numbers +# shellcheck disable=SC2039 +multiCompare() { + local larger="${1}" + local numA="${2}" + local numB="${3}" + local numC="${4}" + + [ "${numA}" -gt "${larger}" ] && larger="${numA}" + [ "${numB}" -gt "${larger}" ] && larger="${numB}" + [ "${numC}" -gt "${larger}" ] && larger="${numC}" + echo "${larger}" +} + +# Test for repositories +# shellcheck disable=SC2155,SC2039 +repoTest() { + REPONUM="0" + if [ ! -z "$(git rev-parse HEAD 2>/dev/null)" ]; then + local gitPath="$(git rev-parse --show-toplevel)" + local gitDepth="$(pathSegment "${gitPath}")" + REPONUM="$((REPONUM+1))" + else + local gitDepth="0" + fi + if [ ! -z "$(hg root 2>/dev/null)" ]; then + local hgPath="$(hg root 2>/dev/null)" + local hgDepth="$(pathSegment "${hgPath}")" + REPONUM="$((REPONUM+1))" + else + local hgDepth="0" + fi + if [ ! -z "$(bzr root 2>/dev/null)" ]; then + local bzrPath="$(bzr root 2>/dev/null)" + local bzrDepth="$(pathSegment "${bzrPath}")" + REPONUM="$((REPONUM+1))" + else + local bzrDepth="0" + fi + if [ ! -z "$(svn info 2>/dev/null)" ]; then + local stringz="" + local stringx="" + local svnPath="$(svn info --xml | sed -n -e "s:${stringz}::" -e "s:${stringx}::p")" + # An old enough svn will not be able give us a path; default + # to 1 for that case. + if [ -z "${svnPath}" ]; then + local svnDepth="1" + else + local svnDepth="$(pathSegment "${svnPath}")" + fi + REPONUM="$((REPONUM+1))" + else + local svnDepth="0" + fi + + # Do not do more work then we have to. + if [ "${REPONUM}" = "0" ]; then + return + fi + + # Figure out which repo is the deepest and use it. + local wonRepo="$(multiCompare "${gitDepth}" "${hgDepth}" "${bzrDepth}" "${svnDepth}")" + if [ "${wonRepo}" = "${gitDepth}" ]; then + gitRepo + elif [ "${wonRepo}" = "${hgDepth}" ]; then + hgRepo + elif [ "${wonRepo}" = "${bzrDepth}" ]; then + bzrRepo + elif [ "${wonRepo}" = "${svnDepth}" ]; then + svnRepo + fi +} + + + +# Detect which repos we are in and gather data. +# shellcheck source=/dev/null +if [ -f "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then + # When requested only read from the cache to populate our symbols. + . "${CACHEFILE}" +else + # If a value is not set through the environment set VCS_EXTRA to nothing. + : "${VCS_EXTRA:=""}" + repoTest + + if [ -f "${CACHEFILE}" ] && [ "${REPONUM}" = "0" ]; then + # We are not in a repo; try to use a previously generated cache to populate our symbols. + . "${CACHEFILE}" + # Do not overwrite the cache if we know we are not going to write anything new. + CACHEFORCE="1" + elif [ "${REPONUM}" = "0" ]; then + echo "error: No repo or cache detected." 1>&2 + exit 1 + fi +fi + + +# -s output is handled here. +if [ ! -z "${VAROUT}" ]; then + if [ "${VAROUT}" = "VCS_TYPE" ]; then + echo "${VCS_TYPE}" + elif [ "${VAROUT}" = "VCS_BASENAME" ]; then + echo "${VCS_BASENAME}" + elif [ "${VAROUT}" = "VCS_NUM" ]; then + echo "${VCS_NUM}" + elif [ "${VAROUT}" = "VCS_DATE" ]; then + echo "${VCS_DATE}" + elif [ "${VAROUT}" = "VCS_BRANCH" ]; then + echo "${VCS_BRANCH}" + elif [ "${VAROUT}" = "VCS_TAG" ]; then + echo "${VCS_TAG}" + elif [ "${VAROUT}" = "VCS_TICK" ]; then + echo "${VCS_TICK}" + elif [ "${VAROUT}" = "VCS_FULL_HASH" ]; then + echo "${VCS_FULL_HASH}" + elif [ "${VAROUT}" = "VCS_SHORT_HASH" ]; then + echo "${VCS_SHORT_HASH}" + elif [ "${VAROUT}" = "VCS_WC_MODIFIED" ]; then + echo "${VCS_WC_MODIFIED}" + elif [ "${VAROUT}" = "VCS_ACTION_STAMP" ]; then + echo "${VCS_ACTION_STAMP}" + else + echo "error: Not a valid output symbol." 1>&2 + exit 1 + fi +fi + + +# Detect requested output type and use it. +if [ ! -z "${AFILETYPE}" ]; then + if [ "${AFILETYPE}" = "c" ]; then + cOutput + elif [ "${AFILETYPE}" = "h" ]; then + hOutput + elif [ "${AFILETYPE}" = "xcode" ]; then + xcodeOutput + elif [ "${AFILETYPE}" = "swift" ]; then + swiftOutput + elif [ "${AFILETYPE}" = "sh" ]; then + shOutput + elif [ "${AFILETYPE}" = "py" ] || [ "${AFILETYPE}" = "python" ]; then + pyOutput + elif [ "${AFILETYPE}" = "pl" ] || [ "${AFILETYPE}" = "perl" ]; then + plOutput + elif [ "${AFILETYPE}" = "lua" ]; then + luaOutput + elif [ "${AFILETYPE}" = "php" ]; then + phpOutput + elif [ "${AFILETYPE}" = "ini" ]; then + iniOutput + elif [ "${AFILETYPE}" = "js" ]; then + jsOutput + elif [ "${AFILETYPE}" = "json" ]; then + jsonOutput + elif [ "${AFILETYPE}" = "java" ]; then + javaOutput + elif [ "${AFILETYPE}" = "javaprop" ]; then + javapropOutput + elif [ "${AFILETYPE}" = "tex" ]; then + texOutput + elif [ "${AFILETYPE}" = "m4" ]; then + m4Output + elif [ "${AFILETYPE}" = "scheme" ]; then + schemeOutput + elif [ "${AFILETYPE}" = "clojure" ]; then + clojureOutput + elif [ "${AFILETYPE}" = "rpm" ]; then + rpmOutput + elif [ "${AFILETYPE}" = "hpp" ]; then + hppOutput + elif [ "${AFILETYPE}" = "matlab" ]; then + matlabOutput + elif [ "${AFILETYPE}" = "octave" ]; then + octaveOutput + elif [ "${AFILETYPE}" = "cmake" ]; then + cmakeOutput + else + echo "error: Not a valid output type." 1>&2 + exit 1 + fi +fi + + +# If requested, make a cache file. +if [ ! -z "${CACHEFILE}" ] && [ ! "${CACHEFORCE}" = "1" ]; then + TARGETFILE="${CACHEFILE}.tmp" + shOutput + + # Check to see if there have been any actual changes. + if [ ! -f "${CACHEFILE}" ]; then + mv -f "${CACHEFILE}.tmp" "${CACHEFILE}" + elif cmp -s "${CACHEFILE}.tmp" "${CACHEFILE}"; then + rm -f "${CACHEFILE}.tmp" + else + mv -f "${CACHEFILE}.tmp" "${CACHEFILE}" + fi +fi diff --git a/cmake/Package.cmake b/cmake/Package.cmake new file mode 100644 index 0000000..d1e8880 --- /dev/null +++ b/cmake/Package.cmake @@ -0,0 +1,37 @@ +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CPACK_PACKAGE_NAME "verify-policy-debug") +else() + set(CPACK_PACKAGE_NAME "verify-policy") +endif() + +message(STATUS "Package: ${CPACK_PACKAGE_NAME}") + +set(CPACK_PACKAGE_VENDOR "MESASOFT") +set(CPACK_PACKAGE_VERSION_MAJOR "${VERIFY_POLIC_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${VERIFY_POLIC_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${VERIFY_POLIC_VERSION_PATCH}.${VERIFY_POLIC_DESCRIBE}") +set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) + +# RPM Build +set(CPACK_GENERATOR "RPM") +set(CPACK_RPM_AUTO_GENERATED_FILE_NAME ON) +set(CPACK_RPM_FILE_NAME "RPM-DEFAULT") +set(CPACK_RPM_PACKAGE_AUTOREQPROV "no") +set(CPACK_RPM_PACKAGE_RELEASE_DIST on) +set(CPACK_RPM_DEBUGINFO_PACKAGE on) +#set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PostInstall.in) +#set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PostUninstall.in) +#set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PreUninstall.in) + +# Must uninstall the debug package before install release package +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CPACK_RPM_PACKAGE_CONFLICTS "verify-policy") +else() + set(CPACK_RPM_PACKAGE_CONFLICTS "verify-policy-debug") +endif() + +# setup %config(noreplace) +set(CPACK_RPM_USER_FILELIST "%config(noreplace) ${CMAKE_INSTALL_PREFIX}/conf/pangu/pangu_pxy.conf" + "%config(noreplace) ${CMAKE_INSTALL_PREFIX}/conf/tfe/decrypt_mirror.conf" + "%config(noreplace) ${CMAKE_INSTALL_PREFIX}/conf/tfe/tfe.conf") +include(CPack) diff --git a/cmake/PostInstall.in b/cmake/PostInstall.in new file mode 100644 index 0000000..d52334e --- /dev/null +++ b/cmake/PostInstall.in @@ -0,0 +1,2 @@ +%systemd_post mrenv.service mrzcpd.service mrtunnat.service +/sbin/ldconfig \ No newline at end of file diff --git a/cmake/PostUninstall.in b/cmake/PostUninstall.in new file mode 100644 index 0000000..d24e6f5 --- /dev/null +++ b/cmake/PostUninstall.in @@ -0,0 +1,2 @@ +%systemd_postun_with_restart mrenv.service mrzcpd.service mrtunnat.service +/sbin/ldconfig \ No newline at end of file diff --git a/cmake/PreUninstall.in b/cmake/PreUninstall.in new file mode 100644 index 0000000..4dbf922 --- /dev/null +++ b/cmake/PreUninstall.in @@ -0,0 +1 @@ +%systemd_preun mrenv.service mrzcpd.service mrtunnat.service \ No newline at end of file diff --git a/cmake/Version.cmake b/cmake/Version.cmake new file mode 100644 index 0000000..0456017 --- /dev/null +++ b/cmake/Version.cmake @@ -0,0 +1,38 @@ + +# Using autorevision.sh to generate version information + +set(__SOURCE_AUTORESIVISION ${CMAKE_SOURCE_DIR}/autorevision.sh) +set(__AUTORESIVISION ${CMAKE_BINARY_DIR}/autorevision.sh) +set(__VERSION_CACHE ${CMAKE_SOURCE_DIR}/version.txt) +set(__VERSION_CONFIG ${CMAKE_BINARY_DIR}/version.cmake) + +file(COPY ${__SOURCE_AUTORESIVISION} DESTINATION ${CMAKE_BINARY_DIR} + FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + +# execute autorevision.sh to generate version information +execute_process(COMMAND ${__AUTORESIVISION} -t cmake -o ${__VERSION_CACHE} OUTPUT_FILE ${__VERSION_CONFIG}) +include(${__VERSION_CONFIG}) + +# extract major, minor, patch version from git tag +string(REGEX REPLACE "^v([0-9]+)\\..*" "\\1" VERIFY_POLICY_VERSION_MAJOR "${VCS_TAG}") +string(REGEX REPLACE "^v[0-9]+\\.([0-9]+).*" "\\1" VERIFY_POLIC_VERSION_MINOR "${VCS_TAG}") +string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERIFY_POLIC_VERSION_PATCH "${VCS_TAG}") + +if(NOT VERIFY_POLIC_VERSION_MAJOR) + set(VERIFY_POLIC_VERSION_MAJOR 1) +endif() + +if(NOT VERIFY_POLIC_VERSION_MINOR) + set(VERIFY_POLIC_VERSION_MINOR 0) +endif() + +if(NOT VERIFY_POLIC_VERSION_PATCH) + set(VERIFY_POLIC_VERSION_PATCH 0) +endif() + +set(VERIFY_POLIC_VERSION "${VERIFY_POLIC_VERSION_MAJOR}.${VERIFY_POLIC_VERSION_MINOR}.${VERIFY_POLIC_VERSION_PATCH}") + +# print information +message(STATUS "Welcome to Verify Policy Engine, Version: ${VERIFY_POLIC_VERSION}") +add_definitions(-DVERIFY_POLIC_VERSION=${VERIFY_POLIC_VERSION}) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000..eadaa1a --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(common src/verify_policy_logging.cpp) +target_include_directories(common PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(common PUBLIC MESA_handle_logger) + diff --git a/common/include/verify_policy.h b/common/include/verify_policy.h new file mode 100644 index 0000000..5a96090 --- /dev/null +++ b/common/include/verify_policy.h @@ -0,0 +1,79 @@ +/************************************************************************* + > File Name: verify_policy.h + > Author: + > Mail: + > Created Time: 2019年08月23日 星期五 18时06分03秒 + ************************************************************************/ + +#ifndef _VERIFY_POLICY_H +#define _VERIFY_POLICY_H + +#include +#include "verify_policy_utils.h" + +enum scan_table +{ + PXY_CTRL_IP, + PXY_CTRL_HTTP_URL, + PXY_CTRL_HTTP_FQDN, + PXY_CTRL_HTTP_REQ_HDR, + PXY_CTRL_HTTP_REQ_BODY, + PXY_CTRL_HTTP_RES_HDR, + PXY_CTRL_HTTP_RES_BODY, + PXY_CTRL_SUBSCRIBE_ID, + __SCAN_TABLE_MAX +}; + +enum http_ev_bit_number +{ + IP_BITNUM = 0, + URL_BITNUM, + FQDN_BITNUM, + REQ_HDR_BITNUM, + RESP_HDR_BITNUM, + CONTENT_BITNUM, + SUBSCRIBE_ID +}; + +enum tfe_http_event +{ + EV_HTTP_IP = 1ULL << IP_BITNUM, + EV_HTTP_URL = 1ULL << URL_BITNUM, + EV_HTTP_FQDN = 1ULL << FQDN_BITNUM, + EV_HTTP_REQ_HDR = 1ULL << REQ_HDR_BITNUM, + EV_HTTP_RESP_HDR = 1ULL << RESP_HDR_BITNUM, + EV_HTTP_CONTENT = 1ULL << CONTENT_BITNUM, + EV_HTTP_SUBSCRIBE_ID = 1ULL << SUBSCRIBE_ID, +}; + +struct verify_proxy_thread +{ + int id; + pthread_t pid; + evutil_socket_t accept_fd; + pthread_attr_t *attr; + struct evhttp *http; + struct event_base *base; + void * (*routine)(void *); +}; + +struct verify_proxy +{ + char name[VERIFY_SYMBOL_MAX]; + void * logger; + unsigned int log_level; + unsigned int nr_work_threads; + unsigned int listen_port; + struct verify_proxy_thread *work_threads[TFE_THREAD_MAX]; +}; + +extern struct verify_proxy * g_verify_proxy; + +void * pangu_http_ctx_new(unsigned int thread_id); + +void http_scan(const char * value, enum tfe_http_event events, + const unsigned char * body_frag, size_t frag_size, void *pme); + +char *web_json_table_add(void *pme); + +#endif diff --git a/common/include/verify_policy_logging.h b/common/include/verify_policy_logging.h new file mode 100644 index 0000000..b2b7d2f --- /dev/null +++ b/common/include/verify_policy_logging.h @@ -0,0 +1,51 @@ +/************************************************************************* + > File Name: logging.h + > Author: + > Mail: + > Created Time: 2018年06月18日 星期一 22时45分58秒 + ************************************************************************/ + +#ifndef _LOGGING_H +#define _LOGGING_H + +#define MODULE_NAME "verify_policy" + +#define RLOG_LV_DEBUG 10 +#define RLOG_LV_INFO 20 +#define RLOG_LV_FATAL 30 + +typedef struct RTLogInit2Data_ { + int debug_switch; + + int run_log_level; + + char run_log_path[256]; + + void *run_log_handle; +} RTLogInit2Data; + +extern RTLogInit2Data logging_sc_lid; + +/* The maximum length of the log message */ +#define RT_LOG_MAX_LOG_MSG_LEN 2048 + +extern void mesa_logging_print(int log_level, const char *module, const char *msg); + +#define mesa_log(x, y, z, ...) do { \ + char _sc_log_msg[RT_LOG_MAX_LOG_MSG_LEN] = ""; \ + char *_sc_log_temp = _sc_log_msg; \ + if ( !x ) \ + { } else { \ + snprintf(_sc_log_temp, \ + (RT_LOG_MAX_LOG_MSG_LEN - \ + (_sc_log_temp - _sc_log_msg)), \ + __VA_ARGS__); \ + mesa_logging_print(y, z, _sc_log_msg); \ + } \ + } while(0) + +#define mesa_runtime_log(level, module, ...) mesa_log(logging_sc_lid.debug_switch, level, module, __VA_ARGS__) + +extern void * verify_syslog_init(const char *config); + +#endif diff --git a/common/include/verify_policy_utils.h b/common/include/verify_policy_utils.h new file mode 100644 index 0000000..d5fc219 --- /dev/null +++ b/common/include/verify_policy_utils.h @@ -0,0 +1,54 @@ +#ifndef __RT_COMMON_H__ +#define __RT_COMMON_H__ + +#include +#define EVAL_TM_STYLE "%Y-%m-%d" + +#define VERIFY_SYMBOL_MAX 64 +#define VERIFY_STRING_MAX 2048 +#define TFE_THREAD_MAX 128 + +/** Alway treated the expr as true */ +#ifndef likely +#define likely(expr) __builtin_expect(!!(expr), 1) +#endif + +/** Alway treated the expr as false */ +#ifndef unlikely +#define unlikely(expr) __builtin_expect(!!(expr), 0) +#endif + +#ifndef FOREVER +#define FOREVER for(;;) +#endif + +#ifdef SOCK_NONBLOCK +#define EVUTIL_SOCK_NONBLOCK SOCK_NONBLOCK +#else +#define EVUTIL_SOCK_NONBLOCK 0x4000000 +#endif +#ifdef SOCK_CLOEXEC +#define EVUTIL_SOCK_CLOEXEC SOCK_CLOEXEC +#else +#define EVUTIL_SOCK_CLOEXEC 0x80000000 +#endif +#ifdef EFD_NONBLOCK +#define EVUTIL_EFD_NONBLOCK EFD_NONBLOCK +#else +#define EVUTIL_EFD_NONBLOCK 0x4000 +#endif +#ifdef EFD_CLOEXEC +#define EVUTIL_EFD_CLOEXEC EFD_CLOEXEC +#else +#define EVUTIL_EFD_CLOEXEC 0x8000 +#endif + +#define __rt_always_inline__ __attribute__((always_inline)) inline + +#define ALLOC(type, number) ((type *)calloc(sizeof(type), number)) +#define FREE(p) {free(*p);*p=NULL;} + +#define CHECK_OR_EXIT(condition, fmt, ...) \ +do { if(!(condition)) { mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, fmt, ##__VA_ARGS__); exit(EXIT_FAILURE); } } while(0) \ + +#endif diff --git a/common/src/verify_policy_logging.cpp b/common/src/verify_policy_logging.cpp new file mode 100644 index 0000000..44fce95 --- /dev/null +++ b/common/src/verify_policy_logging.cpp @@ -0,0 +1,55 @@ +/************************************************************************* + > File Name: logging.c + > Author: + > Mail: + > Created Time: 2018年06月18日 星期一 22时45分43秒 + ************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "verify_policy_logging.h" +#include "MESA_prof_load.h" +#include "MESA_handle_logger.h" + +RTLogInit2Data logging_sc_lid; + +void mesa_logging_print(int log_level, const char *module, const char *msg) +{ + MESA_handle_runtime_log(logging_sc_lid.run_log_handle, log_level, (const char *)module, msg); + return; +} + +void * verify_syslog_init(const char *config) +{ + char run_log_path[256] = {0}; + + MESA_load_profile_int_def(config, (const char *)"SYSTEM",(const char *)"DEBUG_SWITCH", + &logging_sc_lid.debug_switch, 1); + MESA_load_profile_int_def(config, (const char *)"SYSTEM",(const char *)"RUN_LOG_LEVEL", + &logging_sc_lid.run_log_level, 10); + MESA_load_profile_string_def(config, (const char *)"SYSTEM",(const char *)"RUN_LOG_PATH", + logging_sc_lid.run_log_path, 128, NULL); + + snprintf(run_log_path, 255, "%s/%s", logging_sc_lid.run_log_path, "verify_policy.log"); + + logging_sc_lid.run_log_handle = MESA_create_runtime_log_handle(run_log_path, logging_sc_lid.run_log_level); + if(logging_sc_lid.run_log_handle == NULL){ + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Create log runtime_log_handle error, init failed!"); + goto finish; + }else{ + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Log module initialization"); + } + return logging_sc_lid.run_log_handle; +finish: + return NULL; +} + + diff --git a/conf/verify-policy.conf b/conf/verify-policy.conf new file mode 100644 index 0000000..bec95e4 --- /dev/null +++ b/conf/verify-policy.conf @@ -0,0 +1,54 @@ +[SYSTEM] +#1:print on screen, 0:don't +DEBUG_SWITCH = 1 +#10:DEBUG, 20:INFO, 30:FATAL +RUN_LOG_LEVEL = 10 +RUN_LOG_PATH = ./logs +[CONFIG] +#Number of running threads +thread-nu = 4 + +[maat] +# 0:json 1: redis 2: iris +maat_input_mode=0 +table_info=resource/pangu/table_info.conf +json_cfg_file=resource/pangu/pangu_http.json +stat_file=log/pangu_scan.status +full_cfg_dir=pangu_policy/ +inc_cfg_dir=pangu_policy/ + +maat_redis_server=192.168.10.31 +maat_redis_port=6379 +maat_redis_db_index=0 +effect_interval_s=1 +accept_tags={"tags":[{"tag":"location","value":"Astana"}]} + +[NTC_MAAT] +#Configure the load mode, +#0: using the configuration distribution network +#1: using local json +#2: using Redis reads +maat_json_switch=2 +#When the loading mode is sent to the network, set the scanning configuration modification interval (s). +effective_interval=1 +#Specify the location of the configuration library table file +table_info=./conf/table_info.conf +#Incremental profile path +inc_cfg_dir=./rule/inc/index +#Full profile path +full_cfg_dir=./rule/full/index +#Json file path when json schema is used +pxy_obj_keyring=./conf/pxy_obj_keyring.json +[LIBEVENT] +#Local monitor port number, default is 9991 +port = 9991 +[CERTSTORE_REDIS] +#The Redis server IP address and port number where the certificate is stored locally +ip = 127.0.0.1 +port = 6379 +[MAAT_REDIS] +#Maat monitors the Redsi server IP address and port number +ip = 192.168.11.243 +port = 6379 +dbindex = 4 + diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt new file mode 100644 index 0000000..e965f06 --- /dev/null +++ b/platform/CMakeLists.txt @@ -0,0 +1,13 @@ +add_executable(verify-policy src/verify_policy.cpp) + +#target_include_directories(verify-policy PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) + +target_link_libraries(verify-policy common pangu-http) +target_link_libraries(verify-policy pthread dl + libevent-static + MESA_handle_logger + MESA_prof_load + cjson + MESA_htable + MESA_field_stat) + diff --git a/platform/src/verify_policy.cpp b/platform/src/verify_policy.cpp new file mode 100644 index 0000000..c18bef4 --- /dev/null +++ b/platform/src/verify_policy.cpp @@ -0,0 +1,492 @@ +/************************************************************************* + > File Name: verify-policy.cpp + > Author: + > Mail: + > Created Time: 2019年08月23日 星期五 14时41分17秒 + ************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "verify_policy.h" +#include "MESA_prof_load.h" +#include "MESA_handle_logger.h" +#include "verify_policy_logging.h" + +struct verify_proxy * g_verify_proxy = NULL; + +struct keyword_obj +{ + enum scan_table condition_type; + char *condition_scope; + char *keyword; +}; + +struct verify_policy_query +{ + enum scan_table object_type; + int addr_type; + + char *clientIp1; + unsigned int clientPort1; + char *serverIp1; + unsigned int serverPort1; + + struct keyword_obj keywords[16]; +}; + +#if 0 +#ifdef VERIFY_POLIC_VERSION +char *git_version = VERIFY_POLIC_VERSION; +#else +char *default_version = "1.1.1"; +#endif +#endif +const char *default_version = "1.1.1"; +const char * version() +{ + return default_version; +} + +extern int pangu_policy_init(struct verify_proxy * verify, const char* profile_path); + +static int verify_policy_init(struct verify_proxy * verify, const char *profile) +{ + int xret = -1; + + xret = MESA_load_profile_uint_nodef(profile, "CONFIG", "thread-nu", &(verify->nr_work_threads)); + if (xret < 0){ + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Reading the number of running threads failed"); + } + xret = MESA_load_profile_short_nodef(profile, "LISTEN", "port", (short *)&(verify->listen_port)); + if (xret < 0){ + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Listen Port invalid"); + } + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Listen Port %d", verify->listen_port); + return xret; +} + +enum scan_table verify_type_str2idx(const char *action_str) +{ + const char * table_name[__SCAN_TABLE_MAX]; + table_name[PXY_CTRL_IP] = "ip"; + table_name[PXY_CTRL_HTTP_URL] = "url"; + table_name[PXY_CTRL_HTTP_FQDN] = "fqdn"; + table_name[PXY_CTRL_HTTP_REQ_HDR] = "req_hdr"; + table_name[PXY_CTRL_HTTP_REQ_BODY] = "keywords"; + table_name[PXY_CTRL_HTTP_RES_HDR] = "res_hdr"; + table_name[PXY_CTRL_HTTP_RES_BODY] = "keywords"; + table_name[PXY_CTRL_SUBSCRIBE_ID] = "subscribeid"; + + size_t i = 0; + + for (i = 0; i < sizeof(table_name) / sizeof(const char *); i++) + { + if (0 == strcasecmp(action_str, table_name[i])) + break; + } + return (enum scan_table)i; +} + +struct verify_policy_query *get_query_from_request(const char *data) +{ + int c_num = 0, i = 0; + char buff[VERIFY_STRING_MAX], *p = NULL;; + + cJSON* data_json = cJSON_Parse(data); + if(data_json == NULL) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "invalid policy parameter"); + return NULL; + } + struct verify_policy_query *query_ctx = ALLOC(struct verify_policy_query, 1); + + cJSON* item = NULL, *subitem = NULL; + item = cJSON_GetObjectItem(data_json,"objectType"); + if(item && item->type==cJSON_String) + { + query_ctx->object_type =verify_type_str2idx(item->valuestring); + } + item=cJSON_GetObjectItem(data_json,"addrType"); + if(item && item->type==cJSON_Number) + { + query_ctx->addr_type = item->valueint; + } + item = cJSON_GetObjectItem(data_json,"clientIp1"); + if(item && item->type==cJSON_String) + { + query_ctx->clientIp1 =strdup(item->valuestring); + } + item = cJSON_GetObjectItem(data_json,"serverIp1"); + if(item && item->type==cJSON_String) + { + query_ctx->serverIp1 =strdup(item->valuestring); + } + item = cJSON_GetObjectItem(data_json,"clientPort1"); + if(item && item->type==cJSON_String) + { + query_ctx->clientPort1 =atoi(item->valuestring); + } + item = cJSON_GetObjectItem(data_json,"serverPort1"); + if(item && item->type==cJSON_String) + { + query_ctx->serverPort1 =atoi(item->valuestring); + } + p = buff; + p += snprintf(p, sizeof(buff) - (p - buff), "Query key objectType:%d, addrType:%d, clientIp1:%s, serverIp1:%s, clientPort1:%d, serverPort1:%d", + query_ctx->object_type, query_ctx->addr_type, query_ctx->clientIp1, query_ctx->serverIp1, query_ctx->clientPort1, query_ctx->serverPort1); + item = cJSON_GetObjectItem(data_json,"keywordObj"); + if(item && item->type==cJSON_Array) + { + c_num=cJSON_GetArraySize(item); + for (subitem = item->child; subitem != NULL; subitem = subitem->next) + { + item = cJSON_GetObjectItem(subitem, "conditionScope"); + if(item && item->type==cJSON_String) + { + query_ctx->keywords[i].condition_scope =strdup(item->valuestring); + query_ctx->keywords[i].condition_type = verify_type_str2idx(item->valuestring); + } + item = cJSON_GetObjectItem(subitem, "keywords"); + if(item && item->type==cJSON_String) + { + query_ctx->keywords[i].keyword =strdup(item->valuestring); + } + i++; + } + } + for (i = 0; i < c_num; i++) + { + p += snprintf(p, sizeof(buff) - (p - buff), ", conditionScope:%s, keywords:%s", query_ctx->keywords[i].condition_scope, query_ctx->keywords[i].keyword); + } + *p = '\0'; + mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "%s", buff); + return query_ctx; +} + +char *verify_policy_scan(struct verify_policy_query *policy_query, int thread_id) +{ + int c_num = 0; char *policy_payload= NULL; + void * ctx = pangu_http_ctx_new(thread_id); + + for (c_num = 0; policy_query->keywords[c_num].keyword != NULL; c_num++) + { + struct keyword_obj *key_obj = &policy_query->keywords[c_num]; + + if (key_obj->condition_scope == NULL) + key_obj->condition_type = policy_query->object_type; + + switch(key_obj->condition_type) + { + case PXY_CTRL_IP: + http_scan(key_obj->keyword, EV_HTTP_IP, NULL, 0, ctx); + break; + case PXY_CTRL_SUBSCRIBE_ID: + http_scan(key_obj->keyword, EV_HTTP_SUBSCRIBE_ID, NULL, 0, ctx); + case PXY_CTRL_HTTP_URL: + http_scan(key_obj->keyword, EV_HTTP_URL, NULL, 0, ctx); + break; + case PXY_CTRL_HTTP_FQDN: + http_scan(key_obj->keyword, EV_HTTP_FQDN, NULL, 0, ctx); + break; + case PXY_CTRL_HTTP_REQ_HDR: + http_scan(key_obj->keyword, EV_HTTP_REQ_HDR, NULL, 0, ctx); + break; + case PXY_CTRL_HTTP_RES_HDR: + http_scan(key_obj->keyword, EV_HTTP_RESP_HDR, NULL, 0, ctx); + break; + case PXY_CTRL_HTTP_REQ_BODY: + case PXY_CTRL_HTTP_RES_BODY: + http_scan(key_obj->keyword, EV_HTTP_CONTENT, NULL, 0, ctx); + break; + default: + break; + } + } + policy_payload = web_json_table_add(ctx); + + return policy_payload; +} + +static int +evhttp_socket_send(struct evhttp_request *req, char *sendbuf) +{ + struct evbuffer *evb = NULL; + + /* This holds the content we're sending. */ + evb = evbuffer_new(); + + if (sendbuf[0] == '\0' && req == NULL){ + goto err; + } + evhttp_add_header(evhttp_request_get_output_headers(req), + "Content-Type", "text/html"); + evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "keep-alive"); + evbuffer_add_printf(evb, "%s", sendbuf); + evhttp_send_reply(req, HTTP_OK, "OK", evb); + goto done; + +err: + evhttp_send_error(req, HTTP_NOTFOUND, "Document was not found"); +done: + evbuffer_free(evb); + return 0; +} + +void evhttp_request_cb(struct evhttp_request *evh_req, void *arg) +{ + char *policy_payload= NULL; + struct evbuffer * evbuf_body = NULL; + char *input = NULL; ssize_t inputlen=0; + struct verify_policy_query *policy_query = NULL; + + struct verify_proxy_thread *thread_ctx = (struct verify_proxy_thread *)arg; + + if (evhttp_request_get_command(evh_req) != EVHTTP_REQ_POST) + { + mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "FAILED (post type)"); + goto error; + } + evbuf_body = evhttp_request_get_input_buffer(evh_req); + if (!evbuf_body || 0==(inputlen = evbuffer_get_length(evbuf_body)) ||!(input = (char *)evbuffer_pullup(evbuf_body,inputlen))) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to get post data information."); + goto error; + } + policy_query = get_query_from_request(input); + if (policy_query == NULL) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Data parsing failed."); + goto error; + } + + policy_payload = verify_policy_scan(policy_query, thread_ctx->id); + if (policy_payload) + { + mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "%s", policy_payload); + evhttp_socket_send(evh_req, policy_payload); + free(policy_payload); + } + goto finish; + +error: + evhttp_send_error(evh_req, HTTP_BADREQUEST, 0); +finish: + return; +} + +void * verify_policy_thread(void * arg) +{ + struct evhttp_bound_socket *bound = NULL; + struct verify_proxy_thread *thread_ctx = (struct verify_proxy_thread *)arg; + + thread_ctx->base = event_base_new(); + if (! thread_ctx->base) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Can'thread_ctx allocate event base"); + goto finish; + } + thread_ctx->http = evhttp_new(thread_ctx->base); + if (!thread_ctx->http) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "couldn'thread_ctx create evhttp. Exiting."); + goto error; + } + + evhttp_set_cb(thread_ctx->http, "/v1/policy/verification", evhttp_request_cb, thread_ctx); + + bound = evhttp_accept_socket_with_handle(thread_ctx->http, thread_ctx->accept_fd); + if (bound != NULL) + { + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Bound(%p) to port %d - Awaiting connections ... ", bound, + g_verify_proxy->listen_port); + } + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Work thread %u is run...", thread_ctx->id); + + event_base_dispatch(thread_ctx->base); +error: + event_base_free(thread_ctx->base); +finish: + return NULL; +} + +static int +evutil_fast_socket_nonblocking(evutil_socket_t fd) +{ +#ifdef _WIN32 + return evutil_make_socket_nonblocking(fd); +#else + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + return -1; + } + return 0; +#endif +} + +static int +evutil_fast_socket_closeonexec(evutil_socket_t fd) +{ +#if !defined(_WIN32) && defined(EVENT__HAVE_SETFD) + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { + return -1; + } +#endif + return 0; +} + +evutil_socket_t +evutil_socket_(int domain, int type, int protocol) +{ + evutil_socket_t r; +#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) + r = socket(domain, type, protocol); + if (r >= 0) + return r; + else if ((type & (SOCK_NONBLOCK|SOCK_CLOEXEC)) == 0) + return -1; +#endif +#define SOCKET_TYPE_MASK (~(EVUTIL_SOCK_NONBLOCK|EVUTIL_SOCK_CLOEXEC)) + r = socket(domain, type & SOCKET_TYPE_MASK, protocol); + if (r < 0) + return -1; + if (type & EVUTIL_SOCK_NONBLOCK) { + if (evutil_fast_socket_nonblocking(r) < 0) { + evutil_closesocket(r); + return -1; + } + } + if (type & EVUTIL_SOCK_CLOEXEC) { + if (evutil_fast_socket_closeonexec(r) < 0) { + evutil_closesocket(r); + return -1; + } + } + return r; +} + +static evutil_socket_t +evhttp_listen_socket_byuser(const struct sockaddr *sa, int socklen, + unsigned flags, int backlog) +{ + evutil_socket_t fd; + int on = 1; + int family = sa ? sa->sa_family : AF_UNSPEC; + int socktype = SOCK_STREAM | EVUTIL_SOCK_NONBLOCK; + + if (flags & LEV_OPT_CLOSE_ON_EXEC) + socktype |= EVUTIL_SOCK_CLOEXEC; + + fd = evutil_socket_(family, socktype, 0); + if (fd == -1) + return fd; + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on))<0) + goto err; + if (flags & LEV_OPT_REUSEABLE) { + if (evutil_make_listen_socket_reuseable(fd) < 0) + goto err; + } + if (flags & LEV_OPT_REUSEABLE_PORT) { + if (evutil_make_listen_socket_reuseable_port(fd) < 0){ + goto err; + } + } + if (sa) { + if (bind(fd, sa, socklen)<0) + goto err; + } + if (listen(fd, backlog) == -1) { + goto err; + } + return fd; +err: + evutil_closesocket(fd); + return fd; +} + +int pangu_policy_work_thread_run(struct verify_proxy * verify) +{ + int xret = 0; + unsigned int tid = 0; + struct verify_proxy_thread *thread_ctx = NULL; + + struct sockaddr_in sin; + memset(&sin, 0, sizeof(struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = htons(verify->listen_port); + evutil_socket_t accept_fd = evhttp_listen_socket_byuser((struct sockaddr*)&sin, sizeof(struct sockaddr_in),LEV_OPT_REUSEABLE_PORT|LEV_OPT_CLOSE_ON_FREE, -1); + if (accept_fd < 0) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Could not create a listen!"); + goto finish; + } + + for (tid = 0; tid < verify->nr_work_threads; tid++) + { + verify->work_threads[tid] = ALLOC(struct verify_proxy_thread, 1); + thread_ctx = verify->work_threads[tid]; + thread_ctx->id = tid; + thread_ctx->accept_fd =accept_fd; + thread_ctx->routine = verify_policy_thread; + + if (pthread_create(&thread_ctx->pid, thread_ctx->attr, thread_ctx->routine, thread_ctx)) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "%s", strerror(errno)); + goto finish; + } + if (pthread_detach(thread_ctx->pid)) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "%s", strerror(errno)); + goto finish; + } + } + FOREVER{ + sleep(1); + } +finish: + return xret; +} + +int main(int argc, char * argv[]) +{ + const char * main_profile = "./conf/verify_policy.conf"; + + int ret = 0, opt = 0; + while ((opt = getopt(argc, argv, "v")) != -1) + { + switch (opt) + { + case 'v': + fprintf(stderr, "Tango Frontend Engine, Version: %s\n", version()); + return 0; + default: + break; + } + } + g_verify_proxy = ALLOC(struct verify_proxy, 1); + assert(g_verify_proxy); + strcpy(g_verify_proxy->name, "verify_policy"); + + g_verify_proxy->logger = verify_syslog_init(main_profile); + CHECK_OR_EXIT(g_verify_proxy->logger != NULL, "Failed at init log module. Exit."); + + ret = verify_policy_init(g_verify_proxy, main_profile); + CHECK_OR_EXIT(ret == 0, "Failed at loading profile %s, Exit.", main_profile); + + ret = pangu_policy_init(g_verify_proxy, main_profile); + CHECK_OR_EXIT(ret == 0, "Failed at init panggu module, Exit."); + + ret = pangu_policy_work_thread_run(g_verify_proxy); + + return ret; +} + diff --git a/scan/CMakeLists.txt b/scan/CMakeLists.txt new file mode 100644 index 0000000..ddc69a5 --- /dev/null +++ b/scan/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(pangu-http src/pangu_http.cpp) +target_include_directories(pangu-http PUBLIC ${CMAKE_CURRENT_LIST_DIR}/incluce) +target_link_libraries(pangu-http PUBLIC common pthread cjson maatframe) + + + diff --git a/scan/include/pangu_http.h b/scan/include/pangu_http.h new file mode 100644 index 0000000..537d958 --- /dev/null +++ b/scan/include/pangu_http.h @@ -0,0 +1,13 @@ +/************************************************************************* + > File Name: panggu_http.h + > Author: + > Mail: + > Created Time: 2019年08月26日 星期一 19时30分49秒 + ************************************************************************/ + +#ifndef _PANGGU_HTTP_H +#define _PANGGU_HTTP_H + +extern int pangu_policy_init(struct verify_proxy * verify, const char* profile_path); + +#endif diff --git a/scan/src/pangu_http.cpp b/scan/src/pangu_http.cpp new file mode 100644 index 0000000..1fa5b02 --- /dev/null +++ b/scan/src/pangu_http.cpp @@ -0,0 +1,518 @@ +/************************************************************************* + > File Name: pangu_http.cpp + > Author: + > Mail: + > Created Time: 2019年08月23日 星期五 16时53分25秒 + ************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "verify_policy.h" +#include "verify_policy_utils.h" +#include "verify_policy_logging.h" + +#define MAX_SCAN_RESULT 16 + +enum pangu_action //Bigger action number is prior. +{ + PG_ACTION_NONE = 0x00, + PG_ACTION_MONIT = 0x01, + PG_ACTION_FORWARD = 0x02, /* N/A */ + PG_ACTION_REJECT = 0x10, + PG_ACTION_DROP = 0x20, /* N/A */ + PG_ACTION_MANIPULATE = 0x30, + PG_ACTION_RATELIMIT = 0x40, /* N/A */ + PG_ACTION_LOOP = 0x60, /* N/A */ + PG_ACTION_WHITELIST = 0x80, + __PG_ACTION_MAX +}; + +struct pangu_http_ctx +{ + enum pangu_action action; + char * action_para; + scan_status_t scan_mid; + stream_para_t sp; + size_t hit_cnt; + struct Maat_rule_t result[MAX_SCAN_RESULT]; + size_t n_enforce; + struct Maat_rule_t * enforce_rules; + int thread_id; +}; + +struct pangu_rt +{ + Maat_feather_t maat; + Maat_feather_t dyn_maat; + int subscriber_id_table_id; + void * local_logger; + int log_level; + int thread_num; + int scan_table_id[__SCAN_TABLE_MAX]; +}; +struct pangu_rt * g_pangu_rt; + +#define MAAT_INPUT_JSON 0 +#define MAAT_INPUT_REDIS 1 +#define MAAT_INPUT_FILE 2 + +void * pangu_http_ctx_new(unsigned int thread_id) +{ + struct pangu_http_ctx * ctx = ALLOC(struct pangu_http_ctx, 1); + ctx->scan_mid = NULL; + ctx->thread_id = (int) thread_id; + return (void *)ctx; +} + +static int pangu_action_weight[__PG_ACTION_MAX] = {0}; +void __pangu_action_weight_init() __attribute__((constructor, used)); +void __pangu_action_weight_init() +{ + pangu_action_weight[PG_ACTION_NONE] = 0; + pangu_action_weight[PG_ACTION_MONIT] = 1; + pangu_action_weight[PG_ACTION_MANIPULATE] = 2; + pangu_action_weight[PG_ACTION_REJECT] = 3; + pangu_action_weight[PG_ACTION_WHITELIST] = 4; +} + +static inline int action_cmp(enum pangu_action a1, enum pangu_action a2) +{ + return pangu_action_weight[a1] - pangu_action_weight[a2]; +} + +static enum pangu_action decide_ctrl_action(const struct Maat_rule_t * hit_rules, size_t n_hit, + struct Maat_rule_t ** enforce_rules, size_t * n_enforce) +{ + size_t n_monit = 0, exist_enforce_num = 0, i = 0; + const struct Maat_rule_t * prior_rule = hit_rules; + struct Maat_rule_t monit_rule[n_hit]; + enum pangu_action prior_action = PG_ACTION_NONE; + + for (i = 0; i < n_hit; i++) + { + unsigned char __expand_action = (unsigned char) hit_rules[i].action; + enum pangu_action __action = (enum pangu_action) __expand_action; + + if (__action == PG_ACTION_MONIT) + { + memcpy(monit_rule + n_monit, hit_rules + i, sizeof(struct Maat_rule_t)); + n_monit++; + } + if (action_cmp(__action, prior_action) > 0) + { + prior_rule = hit_rules + i; + prior_action = __action; + } + else if (action_cmp(__action, prior_action) == 0) + { + if (hit_rules[i].config_id > prior_rule->config_id) + { + prior_rule = hit_rules + i; + } + } + else + { + continue; + } + } + + if (prior_action == PG_ACTION_WHITELIST) + { + if(*n_enforce==0) + { + *enforce_rules=ALLOC(struct Maat_rule_t, 1); + } + *enforce_rules[0]=*prior_rule; + *n_enforce=1; + return PG_ACTION_WHITELIST; + } + + exist_enforce_num = *n_enforce; + if (prior_action == PG_ACTION_MONIT) + { + *n_enforce += n_monit; + } + else + { + *n_enforce += n_monit + 1; + } + + *enforce_rules = (struct Maat_rule_t *) realloc(*enforce_rules, sizeof(struct Maat_rule_t) * (*n_enforce)); + if (prior_action == PG_ACTION_MONIT) + { + memcpy(*enforce_rules + exist_enforce_num, monit_rule, n_monit * sizeof(struct Maat_rule_t)); + } + else + { + memmove(*enforce_rules+1, *enforce_rules, exist_enforce_num*sizeof(struct Maat_rule_t)); + memcpy(*enforce_rules, prior_rule, sizeof(struct Maat_rule_t)); + memcpy(*enforce_rules + exist_enforce_num + 1, monit_rule, n_monit * sizeof(struct Maat_rule_t)); + } + + return prior_action; +} + +char *web_json_table_add(void *pme) +{ + char *policy_payload = NULL; size_t i = 0; + cJSON *policy_obj=NULL, *data_obj=NULL, *hit_obj=NULL; + cJSON *execute_obj=NULL, *obj_list=NULL, *category_obj=NULL; + + struct pangu_http_ctx * ctx = (struct pangu_http_ctx *) pme; + + policy_obj=cJSON_CreateObject(); + cJSON_AddNumberToObject(policy_obj, "code", 200); + cJSON_AddStringToObject(policy_obj, "msg", ""); + cJSON_AddNumberToObject(policy_obj, "success", 1); + + data_obj = cJSON_CreateObject(); + cJSON_AddItemToObject(policy_obj, "data", data_obj); + + /*hitPolicyList **/ + hit_obj = cJSON_CreateObject(); + cJSON_AddItemToObject(data_obj, "hitPolicyList", hit_obj); + if (ctx->hit_cnt >= 1) + { + for (i = 0; i < ctx->hit_cnt; i++) + { + cJSON_AddNumberToObject(hit_obj, "policyId", ctx->result[i].config_id); + cJSON_AddStringToObject(hit_obj, "policyName", ""); + } + } + /*executePolicyList **/ + execute_obj = cJSON_CreateObject(); + cJSON_AddItemToObject(data_obj, "executePolicyList", execute_obj); + cJSON_AddNumberToObject(execute_obj, "policyId", ctx->enforce_rules[0].config_id); + cJSON_AddStringToObject(execute_obj, "policyName", ""); + + /*objectList**/ + obj_list = cJSON_CreateObject(); + cJSON_AddItemToObject(data_obj, "objectList", obj_list); + cJSON_AddNumberToObject(obj_list, "objectId", 12); + cJSON_AddStringToObject(obj_list, "objectName", ""); + cJSON *itemList = cJSON_CreateObject(); + cJSON_AddItemToObject(obj_list, "itemList", itemList); + cJSON_AddNumberToObject(itemList, "itemId", 12); + cJSON_AddStringToObject(itemList, "reqParam", ""); + + /*categoryList**/ + category_obj = cJSON_CreateObject(); + cJSON_AddItemToObject(data_obj, "categoryList", category_obj); + cJSON_AddNumberToObject(category_obj, "categoryId", 12); + cJSON_AddStringToObject(category_obj, "reqParam", ""); + + policy_payload = cJSON_PrintUnformatted(policy_obj); + printf("%s\n", policy_payload); + cJSON_Delete(policy_obj); + + return policy_payload; +} + +void http_scan(const char * value, enum tfe_http_event events, + const unsigned char * body_frag, size_t frag_size, void *pme) +{ + const char * field_val = NULL; + int scan_ret = 0, table_id = 0; + size_t hit_cnt = 0; + + struct pangu_http_ctx * ctx = (struct pangu_http_ctx *) pme; + + if (events & EV_HTTP_IP) + { + scan_ret = Maat_scan_proto_addr(g_pangu_rt->maat, g_pangu_rt->scan_table_id[PXY_CTRL_IP], NULL, 0, + ctx->result+hit_cnt, MAX_SCAN_RESULT-hit_cnt, &(ctx->scan_mid), ctx->thread_id); + if (scan_ret > 0) + { + hit_cnt += scan_ret; + } + } + + if (events & EV_HTTP_SUBSCRIBE_ID) + { + scan_ret = Maat_full_scan_string(g_pangu_rt->maat, g_pangu_rt->scan_table_id[PXY_CTRL_SUBSCRIBE_ID], + CHARSET_UTF8, value, strlen(value), + ctx->result+hit_cnt, NULL, MAX_SCAN_RESULT-hit_cnt, + &(ctx->scan_mid), ctx->thread_id); + if(scan_ret>0) + { + hit_cnt+=scan_ret; + } + } + + if (events & EV_HTTP_FQDN) + { + const char *str_host = value; + int str_host_length = (int) (strlen(value)); + + scan_ret = Maat_full_scan_string(g_pangu_rt->maat, g_pangu_rt->scan_table_id[PXY_CTRL_HTTP_FQDN], + CHARSET_UTF8, str_host, str_host_length, ctx->result, NULL, MAX_SCAN_RESULT, &(ctx->scan_mid), ctx->thread_id); + if (scan_ret > 0) + { + hit_cnt += scan_ret; + } + } + if (events & EV_HTTP_URL) + { + const char * str_url = value; + int str_url_length = (int) (strlen(value)); + + scan_ret = Maat_full_scan_string(g_pangu_rt->maat, g_pangu_rt->scan_table_id[PXY_CTRL_HTTP_URL], + CHARSET_UTF8, str_url, str_url_length, ctx->result + hit_cnt, NULL, MAX_SCAN_RESULT - hit_cnt, &(ctx->scan_mid), ctx->thread_id); + + if (scan_ret > 0) + { + hit_cnt += scan_ret; + } + } + + if ((events & EV_HTTP_REQ_HDR) || (events & EV_HTTP_RESP_HDR)) + { + table_id = events & PXY_CTRL_HTTP_REQ_HDR ? g_pangu_rt->scan_table_id[PXY_CTRL_HTTP_REQ_HDR] : g_pangu_rt->scan_table_id[PXY_CTRL_HTTP_RES_HDR]; + + const char * str_field_name = NULL; + scan_ret = Maat_set_scan_status(g_pangu_rt->maat, &(ctx->scan_mid), MAAT_SET_SCAN_DISTRICT, + str_field_name, strlen(str_field_name)); + assert(scan_ret == 0); + scan_ret = Maat_full_scan_string(g_pangu_rt->maat, table_id, + CHARSET_UTF8, field_val, strlen(field_val), + ctx->result + hit_cnt, NULL, MAX_SCAN_RESULT - hit_cnt, &(ctx->scan_mid), ctx->thread_id); + if (scan_ret > 0) + { + hit_cnt += scan_ret; + } + } + + if ((events & EV_HTTP_CONTENT)) + { + assert(ctx->sp == NULL); + table_id = events & EV_HTTP_CONTENT ? g_pangu_rt->scan_table_id[PXY_CTRL_HTTP_REQ_BODY] : g_pangu_rt->scan_table_id[PXY_CTRL_HTTP_RES_BODY]; + ctx->sp = Maat_stream_scan_string_start(g_pangu_rt->maat, table_id, ctx->thread_id); + scan_ret = Maat_stream_scan_string(&(ctx->sp), CHARSET_UTF8, (const char *) body_frag, (int) frag_size, + ctx->result + hit_cnt, NULL, MAX_SCAN_RESULT - hit_cnt, &(ctx->scan_mid)); + if (scan_ret > 0) + { + hit_cnt += scan_ret; + } + Maat_stream_scan_string_end(&(ctx->sp)); + ctx->sp = NULL; + } + + if (hit_cnt > 0) + { + ctx->action = decide_ctrl_action(ctx->result, hit_cnt, &ctx->enforce_rules, &ctx->n_enforce); + ctx->hit_cnt = hit_cnt; + } + return ; +} + +char * verify_policy_str_to_addr() +{ + return NULL; +} + +static Maat_feather_t create_maat_feather(const char * instance_name, const char * profile, const char * section, int max_thread, void * logger) +{ + Maat_feather_t target; + int input_mode = 0, maat_perf_on = 0; + int ret = 0, scan_detail = 0, effect_interval = 60; + char table_info[VERIFY_STRING_MAX] = {0}, inc_cfg_dir[VERIFY_STRING_MAX] = {0}, ful_cfg_dir[VERIFY_STRING_MAX] = {0}; + char redis_server[VERIFY_STRING_MAX] = {0}; + char redis_port_range[VERIFY_STRING_MAX] = {0}; + char accept_tags[VERIFY_STRING_MAX] = {0}; + int redis_port_begin=0, redis_port_end=0; + int redis_port_select=0; + int redis_db_idx = 0; + char json_cfg_file[VERIFY_STRING_MAX] = {0}; + MESA_load_profile_int_def(profile, section, "maat_input_mode", &(input_mode), 0); + MESA_load_profile_int_def(profile, section, "perf_switch", &(maat_perf_on), 1); + + MESA_load_profile_string_def(profile, section, "table_info", table_info, sizeof(table_info), ""); + MESA_load_profile_string_def(profile, section, "accept_tags", accept_tags, sizeof(accept_tags), ""); + + MESA_load_profile_string_def(profile, section, "json_cfg_file", json_cfg_file, sizeof(json_cfg_file), ""); + + MESA_load_profile_string_def(profile, section, "maat_redis_server", redis_server, sizeof(redis_server), ""); + MESA_load_profile_string_def(profile, section, "maat_redis_port_range", redis_port_range, sizeof(redis_server), "6379"); + ret=sscanf(redis_port_range,"%d-%d", &redis_port_begin, &redis_port_end); + if(ret==1) + { + redis_port_select=redis_port_begin; + } + else if(ret==2) + { + srand(time(NULL)); + redis_port_select=redis_port_begin+rand()%(redis_port_end-redis_port_begin); + } + else + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Invalid redis port range %s, MAAT init failed.", redis_port_range); + } + MESA_load_profile_int_def(profile, section, "maat_redis_db_index", &(redis_db_idx), 0); + + MESA_load_profile_string_def(profile, section, "inc_cfg_dir", inc_cfg_dir, sizeof(inc_cfg_dir), ""); + MESA_load_profile_string_def(profile, section, "full_cfg_dir", ful_cfg_dir, sizeof(ful_cfg_dir), ""); + MESA_load_profile_int_def(profile, section, "effect_interval_s", &(effect_interval), 60); + + effect_interval *= 1000;//convert s to ms + assert(strlen(inc_cfg_dir) != 0 || strlen(ful_cfg_dir) != 0 || strlen(redis_server)!=0 || strlen(json_cfg_file)!=0); + + target = Maat_feather(max_thread, table_info, logger); + Maat_set_feather_opt(target, MAAT_OPT_INSTANCE_NAME, instance_name, strlen(instance_name) + 1); + switch (input_mode) + { + case MAAT_INPUT_JSON: + Maat_set_feather_opt(target, MAAT_OPT_JSON_FILE_PATH, json_cfg_file, strlen(json_cfg_file) + 1); + break; + case MAAT_INPUT_REDIS: + Maat_set_feather_opt(target, MAAT_OPT_REDIS_IP, redis_server, strlen(redis_server) + 1); + Maat_set_feather_opt(target, MAAT_OPT_REDIS_PORT, &redis_port_select, sizeof(redis_port_select)); + Maat_set_feather_opt(target, MAAT_OPT_REDIS_INDEX, &redis_db_idx, sizeof(redis_db_idx)); + break; + case MAAT_INPUT_FILE: Maat_set_feather_opt(target, MAAT_OPT_FULL_CFG_DIR, ful_cfg_dir, strlen(ful_cfg_dir) + 1); + Maat_set_feather_opt(target, MAAT_OPT_INC_CFG_DIR, inc_cfg_dir, strlen(inc_cfg_dir) + 1); + break; + default: mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Invalid MAAT Input Mode: %d.", input_mode); + goto error_out; + break; + } + + Maat_set_feather_opt(target, MAAT_OPT_FOREIGN_CONT_DIR, "./pangu_files", strlen("./pangu_files")+1); + + Maat_set_feather_opt(target, MAAT_OPT_EFFECT_INVERVAL_MS, &effect_interval, sizeof(effect_interval)); + Maat_set_feather_opt(target, MAAT_OPT_SCAN_DETAIL, &scan_detail, sizeof(scan_detail)); + + ret = Maat_initiate_feather(target); + if (ret < 0) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "%s MAAT init failed.", __FUNCTION__); + goto error_out; + } + + return target; +error_out: + Maat_burn_feather(target); + return NULL; +} + +static int get_column_pos(const char* line, int column_seq, size_t *offset, size_t *len) +{ + const char* seps=" \t"; + char* saveptr=NULL, *subtoken=NULL, *str=NULL; + char* dup_line=strdup(line); + int i=0, ret=-1; + for (str = dup_line; ; str = NULL) + { + subtoken = strtok_r(str, seps, &saveptr); + if (subtoken == NULL) + break; + if(i==column_seq-1) + { + *offset=subtoken-dup_line; + *len=strlen(subtoken); + ret=0; + break; + } + i++; + } + free(dup_line); + return ret; +} + +void subscribe_id_new_cb(int table_id, const char* key, const char* table_line, MAAT_PLUGIN_EX_DATA* ad, long argl, void* argp) +{ + int ret=0; + size_t subscribe_id_offset, len; + ret=get_column_pos(table_line, 4, &subscribe_id_offset, &len); + if(ret<0) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Add subscribe ID faild: %s", table_line); + return; + } + *ad=ALLOC(char, len+1); + memcpy(*ad, table_line+subscribe_id_offset, len); + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Add subscribe ID: %s", (char*)*ad); + return; +} + +void subscribe_id_free_cb(int table_id, MAAT_PLUGIN_EX_DATA* ad, long argl, void* argp) +{ + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Delete subscribe ID: %s", (char*)*ad); + free(*ad); + *ad=NULL; +} + +void subscribe_id_dup_cb(int table_id, MAAT_PLUGIN_EX_DATA* to, MAAT_PLUGIN_EX_DATA* from, long argl, void* argp) +{ + *to = strdup((char*)*from); + return; +} + +int pangu_policy_init(struct verify_proxy * verify, const char* profile_path) +{ + int ret = -1; + + g_pangu_rt = ALLOC(struct pangu_rt, 1); + + g_pangu_rt->thread_num = verify->nr_work_threads; + g_pangu_rt->local_logger = verify->logger; + + g_pangu_rt->maat = create_maat_feather("static", profile_path, "MAAT", g_pangu_rt->thread_num, g_pangu_rt->local_logger); + if (!g_pangu_rt->maat) + { + goto error_out; + } + + const char * table_name[__SCAN_TABLE_MAX]; + table_name[PXY_CTRL_IP] = "PXY_CTRL_IP"; + table_name[PXY_CTRL_HTTP_URL] = "PXY_CTRL_HTTP_URL"; + table_name[PXY_CTRL_HTTP_FQDN] = "TSG_OBJ_FQDN"; + table_name[PXY_CTRL_HTTP_REQ_HDR] = "PXY_CTRL_HTTP_REQ_HDR"; + table_name[PXY_CTRL_HTTP_REQ_BODY] = "TSG_OBJ_CONTENT"; + table_name[PXY_CTRL_HTTP_RES_HDR] = "PXY_CTRL_HTTP_RES_HDR"; + table_name[PXY_CTRL_HTTP_RES_BODY] = "TSG_OBJ_CONTENT"; + table_name[PXY_CTRL_SUBSCRIBE_ID] = "PXY_CTRL_SUBSCRIBE_ID"; + for (int i = 0; i < __SCAN_TABLE_MAX; i++) + { + g_pangu_rt->scan_table_id[i] = Maat_table_register(g_pangu_rt->maat, table_name[i]); + if (g_pangu_rt->scan_table_id[i] < 0) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Pangu HTTP Maat table %s register failed.", table_name[i]); + goto error_out; + } + } + + g_pangu_rt->dyn_maat = create_maat_feather("dyn", profile_path, "DYNAMIC_MAAT", g_pangu_rt->thread_num, g_pangu_rt->local_logger); + if (!g_pangu_rt->maat) + { + goto error_out; + } + g_pangu_rt->subscriber_id_table_id=Maat_table_register(g_pangu_rt->dyn_maat, "TSG_DYN_SUBSCRIBER_IP"); + ret=Maat_plugin_EX_register(g_pangu_rt->dyn_maat, + g_pangu_rt->subscriber_id_table_id, + subscribe_id_new_cb, + subscribe_id_free_cb, + subscribe_id_dup_cb, + NULL, + 0, + NULL); + if(ret!=0) + { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Pangu HTTP Dynamic Maat TSG_DYN_SUBSCRIBER_IP EX data register failed."); + goto error_out; + } + + ret = 0; +error_out: + return ret; +} + diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt new file mode 100644 index 0000000..0ad3e30 --- /dev/null +++ b/vendor/CMakeLists.txt @@ -0,0 +1,118 @@ +# CMakeFiles for 3rd vendor library + +include(ExternalProject) + +### Libevent 2.1.8 +ExternalProject_Add(libevent PREFIX libevent + URL ${CMAKE_CURRENT_SOURCE_DIR}/libevent-2.1.8-stable.tar.gz + URL_MD5 f3eeaed018542963b7d2416ef1135ecc + CONFIGURE_COMMAND PKG_CONFIG_PATH=${OPENSSL_PKGCONFIG_PATH} + ./configure --prefix= --disable-shared --disable-samples + BUILD_IN_SOURCE 1) + +ExternalProject_Get_Property(libevent INSTALL_DIR) +file(MAKE_DIRECTORY ${INSTALL_DIR}/include) + +add_library(libevent-static STATIC IMPORTED GLOBAL) +add_dependencies(libevent-static libevent) +set_property(TARGET libevent-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libevent.a) +set_property(TARGET libevent-static PROPERTY IMPORTED_INTERFACE_LINK_LIBRARIES pthread crypto) +set_property(TARGET libevent-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) + +### cJSON +ExternalProject_Add(cJSON PREFIX cJSON + URL ${CMAKE_CURRENT_SOURCE_DIR}/cJSON-1.7.7.tar.gz + URL_MD5 715009c99728bf81d6c97352718650ff + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DBUILD_SHARED_AND_STATIC_LIBS=1) + +ExternalProject_Get_Property(cJSON INSTALL_DIR) +file(MAKE_DIRECTORY ${INSTALL_DIR}/include) + +add_library(cjson SHARED IMPORTED GLOBAL) +add_dependencies(cjson cJSON) +set_property(TARGET cjson PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib64/libcjson.a) +set_property(TARGET cjson PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) + +#### GoogleTest +ExternalProject_Add(googletest PREFIX googletest + URL ${CMAKE_CURRENT_SOURCE_DIR}/googletest-release-1.8.0.tar.gz + URL_MD5 16877098823401d1bf2ed7891d7dce36 + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) + +ExternalProject_Get_Property(googletest INSTALL_DIR) +file(MAKE_DIRECTORY ${INSTALL_DIR}/include) + +add_library(gtest STATIC IMPORTED GLOBAL) +add_dependencies(gtest googletest) +set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libgtest.a) +set_property(TARGET gtest PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) +set_property(TARGET gtest PROPERTY INTERFACE_LINK_LIBRARIES pthread) + +add_library(gmock STATIC IMPORTED GLOBAL) +add_dependencies(gmock googletest) +set_property(TARGET gmock PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libgmock.a) +set_property(TARGET gmock PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) + + +### MESA Framework +# Consider the MESA Framework is installed in the system. We declare a imported target instead of +# ExternalProject target. we may retrive the MESAFramework source code from git.mesalab.cn and +# compile staticly to TFE binarys in the future. + +set(MESA_FRAMEWORK_LIB_DIR /opt/MESA/lib) +set(MESA_FRAMEWORK_INCLUDE_DIR /opt/MESA/include) +set(MRZCPD_LIB_DIR /opt/mrzcpd/lib) +set(MRZCPD_INCLUDE_DIR /opt/mrzcpd/include) + +add_library(MESA_handle_logger SHARED IMPORTED GLOBAL) +set_property(TARGET MESA_handle_logger PROPERTY IMPORTED_LOCATION ${MESA_FRAMEWORK_LIB_DIR}/libMESA_handle_logger.so) +set_property(TARGET MESA_handle_logger PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MESA_FRAMEWORK_INCLUDE_DIR}) + +add_library(MESA_prof_load SHARED IMPORTED GLOBAL) +set_property(TARGET MESA_prof_load PROPERTY IMPORTED_LOCATION ${MESA_FRAMEWORK_LIB_DIR}/libMESA_prof_load.so) +set_property(TARGET MESA_prof_load PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MESA_FRAMEWORK_INCLUDE_DIR}) + +add_library(wiredcfg SHARED IMPORTED GLOBAL) +set_property(TARGET wiredcfg PROPERTY IMPORTED_LOCATION ${MESA_FRAMEWORK_LIB_DIR}/libwiredcfg.so) +set_property(TARGET wiredcfg PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MESA_FRAMEWORK_INCLUDE_DIR}) + +add_library(MESA_htable SHARED IMPORTED GLOBAL) +set_property(TARGET MESA_htable PROPERTY IMPORTED_LOCATION ${MESA_FRAMEWORK_LIB_DIR}/libMESA_htable.so) +set_property(TARGET MESA_htable PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MESA_FRAMEWORK_INCLUDE_DIR}) + +add_library(wiredLB SHARED IMPORTED GLOBAL) +set_property(TARGET wiredLB PROPERTY IMPORTED_LOCATION ${MESA_FRAMEWORK_LIB_DIR}/libWiredLB.so) +set_property(TARGET wiredLB PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MESA_FRAMEWORK_INCLUDE_DIR}) + +add_library(maatframe SHARED IMPORTED GLOBAL) +set_property(TARGET maatframe PROPERTY IMPORTED_LOCATION ${MESA_FRAMEWORK_LIB_DIR}/libmaatframe.so) +set_property(TARGET maatframe PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MESA_FRAMEWORK_INCLUDE_DIR}) + +add_library(MESA_field_stat SHARED IMPORTED GLOBAL) +set_property(TARGET MESA_field_stat PROPERTY IMPORTED_LOCATION ${MESA_FRAMEWORK_LIB_DIR}/libMESA_field_stat2.so) +set_property(TARGET MESA_field_stat PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MESA_FRAMEWORK_INCLUDE_DIR}) + +add_library(librdkafka SHARED IMPORTED GLOBAL) +set_property(TARGET librdkafka PROPERTY IMPORTED_LOCATION ${MESA_FRAMEWORK_LIB_DIR}/librdkafka.so) +set_property(TARGET librdkafka PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MESA_FRAMEWORK_INCLUDE_DIR}) + +add_library(mrzcpd SHARED IMPORTED GLOBAL) +set_property(TARGET mrzcpd PROPERTY IMPORTED_LOCATION ${MRZCPD_LIB_DIR}/libmarsio.so) +set_property(TARGET mrzcpd PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MRZCPD_INCLUDE_DIR}) + +### pcre2 +ExternalProject_Add(pcre2 PREFIX pcre2 + URL ${CMAKE_CURRENT_SOURCE_DIR}/pcre2-10.32.tar.gz + URL_MD5 a660db882ff171e6a0de5fb1decd5ff5 + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) + +ExternalProject_Get_Property(pcre2 INSTALL_DIR) +file(MAKE_DIRECTORY ${INSTALL_DIR}/include) + +add_library(pcre2-static STATIC IMPORTED GLOBAL) +add_dependencies(pcre2-static pcre2) +set_property(TARGET pcre2-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libpcre2-8.a) +set_property(TARGET pcre2-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) + diff --git a/vendor/MESA_prof_load-3b2bfd.tar.gz b/vendor/MESA_prof_load-3b2bfd.tar.gz new file mode 100644 index 0000000..8004fa3 Binary files /dev/null and b/vendor/MESA_prof_load-3b2bfd.tar.gz differ diff --git a/vendor/cJSON-1.7.7.tar.gz b/vendor/cJSON-1.7.7.tar.gz new file mode 100644 index 0000000..c2350cf Binary files /dev/null and b/vendor/cJSON-1.7.7.tar.gz differ diff --git a/vendor/googletest-release-1.8.0.tar.gz b/vendor/googletest-release-1.8.0.tar.gz new file mode 100644 index 0000000..a40df33 Binary files /dev/null and b/vendor/googletest-release-1.8.0.tar.gz differ diff --git a/vendor/libevent-2.1.8-stable.tar.gz b/vendor/libevent-2.1.8-stable.tar.gz new file mode 100644 index 0000000..2004f84 Binary files /dev/null and b/vendor/libevent-2.1.8-stable.tar.gz differ diff --git a/vendor/pcre2-10.32.tar.gz b/vendor/pcre2-10.32.tar.gz new file mode 100644 index 0000000..7dca599 Binary files /dev/null and b/vendor/pcre2-10.32.tar.gz differ