Home | History | Annotate | Line # | Download | only in dnssdutil
      1  1.1  christos #! /bin/bash
      2  1.1  christos #
      3  1.1  christos #	Copyright (c) 2020-2022 Apple Inc. All rights reserved.
      4  1.1  christos #
      5  1.1  christos 
      6  1.1  christos declare -r version=1.1
      7  1.1  christos declare -r script=${BASH_SOURCE[0]}
      8  1.1  christos declare -r rcodesURL='https://www.iana.org/assignments/dns-parameters/dns-parameters-6.csv'
      9  1.1  christos 
     10  1.1  christos #============================================================================================================================
     11  1.1  christos 
     12  1.1  christos PrintHelp()
     13  1.1  christos {
     14  1.1  christos 	echo ""
     15  1.1  christos 	echo "Usage: $( basename "${script}" ) [options]"
     16  1.1  christos 	echo ""
     17  1.1  christos 	echo "Options:"
     18  1.1  christos 	echo "    -h  Display script usage."
     19  1.1  christos 	echo "    -V  Display version of this script and exit."
     20  1.1  christos 	echo ""
     21  1.1  christos 	echo "This script writes C functions to convert DNS RCODE values to strings and vice versa to stdout"
     22  1.1  christos 	echo "based on the latest DNS RCODE data available at"
     23  1.1  christos 	echo ""
     24  1.1  christos 	echo "    ${rcodesURL}"
     25  1.1  christos 	echo ""
     26  1.1  christos }
     27  1.1  christos 
     28  1.1  christos #============================================================================================================================
     29  1.1  christos 
     30  1.1  christos ErrQuit()
     31  1.1  christos {
     32  1.1  christos 	echo "error: $*" 1>&2
     33  1.1  christos 	exit 1
     34  1.1  christos }
     35  1.1  christos 
     36  1.1  christos #============================================================================================================================
     37  1.1  christos 
     38  1.1  christos StripLeadingTrailingWhitespace()
     39  1.1  christos {
     40  1.1  christos 	sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
     41  1.1  christos }
     42  1.1  christos 
     43  1.1  christos #============================================================================================================================
     44  1.1  christos 
     45  1.1  christos GetNamesAndValues()
     46  1.1  christos {
     47  1.1  christos 	shopt -s nocasematch
     48  1.1  christos 	while IFS=',' read value name others; do
     49  1.1  christos 		name=$( StripLeadingTrailingWhitespace <<< "${name}" )
     50  1.1  christos 		[[ ${name} =~ ^unassigned$ ]] && continue
     51  1.1  christos 		
     52  1.1  christos 		value=$( StripLeadingTrailingWhitespace <<< "${value}" )
     53  1.1  christos 		[[ ${value} =~ ^[0-9]+$ ]] || continue
     54  1.1  christos 		[ "${value}" -le 65535 ] || continue # Currently, RCODEs can be up to 16-bits in size.
     55  1.1  christos 		
     56  1.1  christos 		# The value 65535 is reserved according to <https://datatracker.ietf.org/doc/html/rfc6895#section-2.3>.
     57  1.1  christos 		# However, the name in the CSV file is "Reserved, can be allocated by Standards Action". For simplicity, we just
     58  1.1  christos 		# use the name "Reserved".
     59  1.1  christos 		
     60  1.1  christos 		if [ "${value}" -eq 65535 ]; then
     61  1.1  christos 			name='Reserved'
     62  1.1  christos 		fi
     63  1.1  christos 		echo "${name},${value}"
     64  1.1  christos 	done
     65  1.1  christos 	shopt -u nocasematch
     66  1.1  christos }
     67  1.1  christos 
     68  1.1  christos #============================================================================================================================
     69  1.1  christos 
     70  1.1  christos RCodeMnemonicToEnum()
     71  1.1  christos {
     72  1.1  christos 	name="${1//[^A-Za-z0-9_]/_}" # Only allow alphanumeric and underscore characters.
     73  1.1  christos 	printf "kDNSRCode_${name}"
     74  1.1  christos }
     75  1.1  christos 
     76  1.1  christos #============================================================================================================================
     77  1.1  christos 
     78  1.1  christos PrintRCodeEnums()
     79  1.1  christos {
     80  1.1  christos 	local -r inputFile=${1}
     81  1.1  christos 	printf "typedef enum\n"
     82  1.1  christos 	printf "{\n"
     83  1.1  christos 	local sep=""
     84  1.1  christos 	< "${inputFile}" sort --field-separator=, --key=2,2 --numeric-sort --unique |
     85  1.1  christos 	while IFS=',' read name value; do
     86  1.1  christos 		printf "%b" "${sep}"
     87  1.1  christos 		local enum=$( RCodeMnemonicToEnum "${name}" )
     88  1.1  christos 		printf "\t%-20s= %d" "${enum}" "${value}"
     89  1.1  christos 		sep=",\n"
     90  1.1  christos 	done
     91  1.1  christos 	printf "\n"
     92  1.1  christos 	printf "\t\n"
     93  1.1  christos 	printf "}\tDNSRCode;\n"
     94  1.1  christos }
     95  1.1  christos 
     96  1.1  christos #============================================================================================================================
     97  1.1  christos 
     98  1.1  christos PrintValueToStringElseIf()
     99  1.1  christos {
    100  1.1  christos 	local -r first=${1}
    101  1.1  christos 	local -r last=${2}
    102  1.1  christos 	[ "${first}" -le "${last}" ] || ErrQuit "${first} > ${last}"
    103  1.1  christos 	shift 2
    104  1.1  christos 	local stringArray=( "$@" )
    105  1.1  christos 	
    106  1.1  christos 	if [ "${last}" -ne "${first}" ]; then
    107  1.1  christos 		printf "\telse if( ( inValue >= ${first} ) && ( inValue <= ${last} ) )\n"
    108  1.1  christos 		local -r arrayVarName="sNames_${first}_${last}"
    109  1.1  christos 	else
    110  1.1  christos 		printf "\telse if( inValue == ${first} )\n"
    111  1.1  christos 		local -r arrayVarName="sNames_${first}"
    112  1.1  christos 	fi
    113  1.1  christos 	printf "\t{\n"
    114  1.1  christos 	printf "\t\tstatic const char * const\t\t${arrayVarName}[] =\n"
    115  1.1  christos 	printf "\t\t{\n"
    116  1.1  christos 	local value=${first}
    117  1.1  christos 	for string in "${stringArray[@]}"; do
    118  1.1  christos 		printf "\t\t\t%-15s // %3d\n" "\"${string}\"," "${value}"
    119  1.1  christos 		value=$(( value + 1 ))
    120  1.1  christos 	done
    121  1.1  christos 	local -r stringCount=$(( value - first ))
    122  1.1  christos 	local -r expectedCount=$(( last - first + 1 ))
    123  1.1  christos 	[ "${stringCount}" -eq "${expectedCount}" ] || ErrQuit "${stringCount} != ${expectedCount}"
    124  1.1  christos 	printf "\t\t};\n"
    125  1.1  christos 	printf "\t\tstring = ${arrayVarName}[ inValue - ${first} ];\n"
    126  1.1  christos 	printf "\t}\n"
    127  1.1  christos }
    128  1.1  christos 
    129  1.1  christos #============================================================================================================================
    130  1.1  christos 
    131  1.1  christos PrintValueToStringFunction()
    132  1.1  christos {
    133  1.1  christos 	local -r inputFile=${1}
    134  1.1  christos 	printf "const char *\tDNSRCodeToString( const int inValue )\n"
    135  1.1  christos 	printf "{\n"
    136  1.1  christos 	printf "\tswitch( inValue )\n"
    137  1.1  christos 	printf "\t{\n"
    138  1.1  christos 	< "${inputFile}" sort --field-separator=, --key=2,2 --numeric-sort --unique |
    139  1.1  christos 	{
    140  1.1  christos 		local stringArray=()
    141  1.1  christos 		while IFS=',' read name value; do
    142  1.1  christos 		    local enum=$( RCodeMnemonicToEnum "${name}" )
    143  1.1  christos 			printf "\t\t%-28s%s\n" "case ${enum}:" "return( \"${name}\" );"
    144  1.1  christos 		done
    145  1.1  christos 	}
    146  1.1  christos 	printf "\t\t%-28sreturn( NULL );\n" "default:"
    147  1.1  christos 	printf "\t}\n"
    148  1.1  christos 	printf "}\n"
    149  1.1  christos }
    150  1.1  christos 
    151  1.1  christos #============================================================================================================================
    152  1.1  christos 
    153  1.1  christos PrintStringToValueFunction()
    154  1.1  christos {
    155  1.1  christos 	local -r inputFile=${1}
    156  1.1  christos 	printf "#include <stdlib.h>\n"
    157  1.1  christos 	printf "\n"
    158  1.1  christos 	printf "typedef struct\n"
    159  1.1  christos 	printf "{\n"
    160  1.1  christos 	printf "\tconst char *\t\tname;\n"
    161  1.1  christos 	printf "\tint\t\t\t\t\tvalue;\n"
    162  1.1  christos 	printf "\t\n"
    163  1.1  christos 	printf "}\t_DNSRCodeTableEntry;\n"
    164  1.1  christos 	printf "\n"
    165  1.1  christos 	printf "static int\t_DNSRCodeFromStringCmp( const void *inKey, const void *inElement );\n"
    166  1.1  christos 	printf "\n"
    167  1.1  christos 	printf "int\tDNSRCodeFromString( const char * const inString )\n"
    168  1.1  christos 	printf "{\n"
    169  1.1  christos 	printf "\t// The name-value table is sorted by name in ascending lexicographical order to allow going from name to\n"
    170  1.1  christos 	printf "\t// value in logarithmic time via a binary search.\n"
    171  1.1  christos 	printf "\t\n"
    172  1.1  christos 	printf "\tstatic const _DNSRCodeTableEntry\t\tsTable[] =\n"
    173  1.1  christos 	printf "\t{\n"
    174  1.1  christos 	
    175  1.1  christos 	local sep=""
    176  1.1  christos 	< "${inputFile}" sort --field-separator=, --key=1,1 --ignore-case --unique |
    177  1.1  christos 	while IFS=',' read name value; do
    178  1.1  christos 		printf "%b" "${sep}"
    179  1.1  christos 		local enum=$( RCodeMnemonicToEnum "${name}" )
    180  1.1  christos 		printf "\t\t%-16s%-20s}" "{ \"${name}\"," "${enum}"
    181  1.1  christos 		sep=",\n"
    182  1.1  christos 	done
    183  1.1  christos 	printf "\n"
    184  1.1  christos 	printf "\t};\n"
    185  1.1  christos 	printf "\tconst _DNSRCodeTableEntry *\t\t\tentry;\n"
    186  1.1  christos 	printf "\t\n"
    187  1.1  christos 	printf "\tentry = (_DNSRCodeTableEntry *) bsearch( inString, sTable, sizeof( sTable ) / sizeof( sTable[ 0 ] ),\n"
    188  1.1  christos 	printf "\t\tsizeof( sTable[ 0 ] ), _DNSRCodeFromStringCmp );\n"
    189  1.1  christos 	printf "\treturn( entry ? entry->value : -1 );\n"
    190  1.1  christos 	printf "}\n"
    191  1.1  christos 	printf "\n"
    192  1.1  christos 	printf "static int\t_DNSRCodeFromStringCmp( const void * const inKey, const void * const inElement )\n"
    193  1.1  christos 	printf "{\n"
    194  1.1  christos 	printf "\tconst _DNSRCodeTableEntry * const\t\tentry = (const _DNSRCodeTableEntry *) inElement;\n"
    195  1.1  christos 	printf "\treturn( strcasecmp( (const char *) inKey, entry->name ) );\n"
    196  1.1  christos 	printf "}\n"
    197  1.1  christos }
    198  1.1  christos 
    199  1.1  christos #============================================================================================================================
    200  1.1  christos 
    201  1.1  christos ExitHandler()
    202  1.1  christos {
    203  1.1  christos 	if [ -d "${tempDir}" ]; then
    204  1.1  christos 		rm -fr "${tempDir}"
    205  1.1  christos 	fi
    206  1.1  christos }
    207  1.1  christos 
    208  1.1  christos #============================================================================================================================
    209  1.1  christos 
    210  1.1  christos PrintAutoGenNote()
    211  1.1  christos {
    212  1.1  christos 	printf "// This code was autogenerated on $( date -u '+%Y-%m-%d' ) by $( basename ${script} ) version ${version}\n"
    213  1.1  christos 	printf "// Data source URL: ${rcodesURL}\n"
    214  1.1  christos 	printf "\n"
    215  1.1  christos }
    216  1.1  christos 
    217  1.1  christos #============================================================================================================================
    218  1.1  christos 
    219  1.1  christos main()
    220  1.1  christos {
    221  1.1  christos 	while getopts ":hO:V" option; do
    222  1.1  christos 		case "${option}" in
    223  1.1  christos 			h)
    224  1.1  christos 				PrintHelp
    225  1.1  christos 				exit 0
    226  1.1  christos 				;;
    227  1.1  christos 			V)
    228  1.1  christos 				echo "$( basename "${script}" ) version ${version}"
    229  1.1  christos 				exit 0
    230  1.1  christos 				;;
    231  1.1  christos 			:)
    232  1.1  christos 				ErrQuit "option '${OPTARG}' requires an argument."
    233  1.1  christos 				;;
    234  1.1  christos 			*)
    235  1.1  christos 				ErrQuit "unknown option '${OPTARG}'."
    236  1.1  christos 				;;
    237  1.1  christos 		esac
    238  1.1  christos 	done
    239  1.1  christos 	
    240  1.1  christos 	[ "${OPTIND}" -gt "$#" ] || ErrQuit "unexpected argument \"${!OPTIND}\"."
    241  1.1  christos 	
    242  1.1  christos 	trap ExitHandler EXIT
    243  1.1  christos 	tempDir=$( mktemp -d ) || ErrQuit "Failed to make temporary directory."
    244  1.1  christos 	declare -r originalRCodesFile="${tempDir}/rcodesOriginal.csv"
    245  1.1  christos 	curl --output "${originalRCodesFile}" "${rcodesURL}" || ErrQuit "Failed to download CSV file."
    246  1.1  christos 	
    247  1.1  christos 	declare -r rcodesFile="${tempDir}/rcodes.csv"
    248  1.1  christos 	< "${originalRCodesFile}" GetNamesAndValues > "${rcodesFile}"
    249  1.1  christos 	
    250  1.1  christos 	declare -r tempFile="${tempDir}/temp.csv"
    251  1.1  christos 	< "${rcodesFile}" sort --field-separator=, --key=2,2 --unique --numeric-sort > "${tempFile}"
    252  1.1  christos 	< "${tempFile}" sort --field-separator=, --key=1,1 --unique --ignore-case > "${rcodesFile}"
    253  1.1  christos 	
    254  1.1  christos 	PrintAutoGenNote
    255  1.1  christos 	PrintRCodeEnums "${rcodesFile}"
    256  1.1  christos 	printf "\n"
    257  1.1  christos 	PrintAutoGenNote
    258  1.1  christos 	PrintValueToStringFunction "${rcodesFile}"
    259  1.1  christos 	printf "\n"
    260  1.1  christos 	PrintAutoGenNote
    261  1.1  christos 	PrintStringToValueFunction "${rcodesFile}"
    262  1.1  christos }
    263  1.1  christos 
    264  1.1  christos main "$@"
    265