* Use dnsmasq to create virtual hosts and mock DNS management for custom domains - dnsmasq docker image - dnsmasq network bridge - point *.sndev to 127.0.0.1 - set-dnsmasq script - -- add/remove/list dns records in dnsmasq.conf - add 'domains' to sndev - 'sndev domains dns' referencing set-dnsmasq script * restart dnsmasq if add/remove succeeded * add domain to /etc/hosts; cleanup * tell if the command needs sudo permission * add directions for dnsmasq DNS server usage * add --no-hosts flag to skip asking to edit /etc/hosts * add domains command to README.md * add dnsmasq instructions to README.md * correct exit on usage function; final cleanup and comments * portable bash; use default network for dnsmasq; set a version for dnsmasq image * POSIX compliance, add env var to .env.development, adjust README * ignore dnsmasq.conf edits, use template instead * use extra configs for dnsmasq, more POSIX compliance * fix --no-hosts flag recognition, light cleanup * shift 4 only if the command has enough args; more error messages; adjust TXT type only on list * different sed syntax for macOS
		
			
				
	
	
		
			268 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			268 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/bin/sh
 | 
						|
# Script to handle local DNS testing via dnsmasq
 | 
						|
# supports add/remove/list of CNAME and TXT records
 | 
						|
# on dnsmasq config changes it will restart the dnsmasq container
 | 
						|
# it also asks to add the record to the /etc/hosts file if --no-hosts is not used
 | 
						|
 | 
						|
# prep
 | 
						|
set -e
 | 
						|
 | 
						|
# ensure directory exists before using the file
 | 
						|
mkdir -p ./docker/dnsmasq/dnsmasq.d
 | 
						|
# dedicated sndev.conf file
 | 
						|
DNSMASQ_CONF_PATH="./docker/dnsmasq/dnsmasq.d/sndev.conf"
 | 
						|
 | 
						|
# check if running on Windows or macOS
 | 
						|
# this script doesn't support Windows /etc/hosts editing
 | 
						|
# sed -i has different syntax on macOS/BSD and Linux
 | 
						|
IS_WINDOWS=false
 | 
						|
IS_DARWIN=false
 | 
						|
 | 
						|
OS_NAME=$(uname -s)
 | 
						|
case "$OS_NAME" in
 | 
						|
  MINGW*|CYGWIN*|MSYS*)
 | 
						|
    IS_WINDOWS=true
 | 
						|
    ;;
 | 
						|
  Darwin*)
 | 
						|
    IS_DARWIN=true
 | 
						|
    ;;
 | 
						|
esac
 | 
						|
 | 
						|
# general usage
 | 
						|
usage() {
 | 
						|
  cat <<EOF
 | 
						|
Set mock DNS records for custom domains in dnsmasq conf.
 | 
						|
Use .sndev domains as they automatically resolve to 127.0.0.1.
 | 
						|
 | 
						|
USAGE
 | 
						|
  $ sndev domains dns [COMMAND]
 | 
						|
 | 
						|
COMMANDS
 | 
						|
  add    <cname|txt> <name> <value> [--no-hosts]
 | 
						|
  remove <cname|txt> <name> <value> [--no-hosts]
 | 
						|
  list   <cname|txt>
 | 
						|
 | 
						|
FLAGS
 | 
						|
  --no-hosts  Skip asking to add/remove record to /etc/hosts
 | 
						|
              Useful if you're using dnsmasq [127.0.0.1:5353] as your DNS server.
 | 
						|
 | 
						|
EXAMPLES
 | 
						|
  $ sndev domains dns add cname www.pizza.sndev sn.sndev
 | 
						|
  $ sndev domains dns remove txt _snverify.www.pizza.sndev "7vfyvQO...vMALqvqkTQ"
 | 
						|
  $ sndev domains dns list cname|txt
 | 
						|
EOF
 | 
						|
exit 1
 | 
						|
}
 | 
						|
 | 
						|
# handle flags
 | 
						|
while [ $# -gt 0 ]; do
 | 
						|
  case "$1" in
 | 
						|
    -h|--help)
 | 
						|
      usage
 | 
						|
    ;;
 | 
						|
    *)
 | 
						|
      break
 | 
						|
    ;;
 | 
						|
  esac
 | 
						|
done
 | 
						|
 | 
						|
INTENT=$1       # add, remove, list
 | 
						|
TYPE=$2         # cname, txt
 | 
						|
NAME=$3         # www.pizza.com
 | 
						|
VALUE=$4        # stacker.news or "7vfyvQO...vMALqvqkTQ="
 | 
						|
NO_HOSTS=false  # handled via --no-hosts flag
 | 
						|
 | 
						|
# creates a line compatible with dnsmasq config file
 | 
						|
prepare_line() {
 | 
						|
  if [ "$TYPE" = "cname" ]; then
 | 
						|
    LINE="cname=${NAME},${VALUE}"
 | 
						|
  elif [ "$TYPE" = "txt" ]; then
 | 
						|
    escaped_quotes=$(printf '%s' "$VALUE" | sed 's/"/\\"/g')
 | 
						|
    LINE="txt-record=${NAME},\"${escaped_quotes}\""
 | 
						|
  else
 | 
						|
    echo "Invalid record type: $TYPE"
 | 
						|
    usage
 | 
						|
  fi
 | 
						|
}
 | 
						|
 | 
						|
# if we're adding or removing a record, we need to check for required args
 | 
						|
if [ "$INTENT" = "add" ] || [ "$INTENT" = "remove" ]; then
 | 
						|
    if [ $# -lt 4 ]; then
 | 
						|
        echo "Not enough arguments"
 | 
						|
        usage
 | 
						|
    else
 | 
						|
        prepare_line # prepare the line for the dnsmasq config file
 | 
						|
        shift 4 # 4 args: intent, type, name, value
 | 
						|
        # we need to get the --no-hosts flag if it's present
 | 
						|
        if [ "$1" = "--no-hosts" ]; then
 | 
						|
            NO_HOSTS=true
 | 
						|
            shift
 | 
						|
        fi
 | 
						|
    fi
 | 
						|
# if we're listing records, we need to have at least the TYPE arg
 | 
						|
elif [ "$INTENT" = "list" ] && [ $# -lt 2 ]; then
 | 
						|
    echo "No type provided"
 | 
						|
    usage
 | 
						|
fi
 | 
						|
 | 
						|
# add a record to the dnsmasq config
 | 
						|
add_record() {
 | 
						|
  # check if the record already exists
 | 
						|
  if grep -Fxq "$LINE" "$DNSMASQ_CONF_PATH"; then
 | 
						|
    echo "Record already exists: $LINE"
 | 
						|
    exit 1
 | 
						|
  fi
 | 
						|
 | 
						|
  # add the record to the dnsmasq config
 | 
						|
  echo "Adding record: $LINE"
 | 
						|
  printf "%s\n" "$LINE" >> "$DNSMASQ_CONF_PATH"
 | 
						|
 | 
						|
  # if we're adding a CNAME record and --no-hosts is not used, we need to ask to add the record to the /etc/hosts file
 | 
						|
  if [ "$TYPE" = "cname" ] && [ "$NO_HOSTS" = false ]; then
 | 
						|
    # dnsmasq pamphlet 
 | 
						|
    printf "
 | 
						|
    While sndev will use dnsmasq DNS server, your system won't use it by default.
 | 
						|
    You can either manually point DNS to 127.0.0.1:5353 to access it system-wide,
 | 
						|
    or add this record to /etc/hosts to access it via browser.\n\n"
 | 
						|
    
 | 
						|
    # ask to add the record to the /etc/hosts file
 | 
						|
    printf "[sudo] Do you want to add '127.0.0.1 %s' to /etc/hosts? [y/N] " "$NAME"
 | 
						|
    read -r response
 | 
						|
    case "$response" in
 | 
						|
      [Yy]*)
 | 
						|
        # add the record to the /etc/hosts file
 | 
						|
        if ! add_record_to_hosts "$NAME"; then
 | 
						|
          echo "/etc/hosts hasn't been touched."
 | 
						|
        fi
 | 
						|
        ;;
 | 
						|
    esac
 | 
						|
  fi
 | 
						|
  echo "Done."
 | 
						|
  exit 0
 | 
						|
}
 | 
						|
 | 
						|
# remove a record from the dnsmasq config
 | 
						|
remove_record() {
 | 
						|
  # check if the record exists
 | 
						|
  if ! grep -Fxq "$LINE" "$DNSMASQ_CONF_PATH"; then
 | 
						|
    echo "Can't find record: $LINE"
 | 
						|
    echo "The record may have been removed or the name/value is incorrect or incomplete."
 | 
						|
    echo "Use 'sndev domains dns list' to see all records."
 | 
						|
    exit 1
 | 
						|
  fi
 | 
						|
 | 
						|
  # remove the record from the dnsmasq config
 | 
						|
  echo "Removing record: $LINE"
 | 
						|
  if [ "$IS_DARWIN" = true ]; then
 | 
						|
    sed -i '' "/^$LINE$/d" "$DNSMASQ_CONF_PATH"
 | 
						|
  else
 | 
						|
    sed -i "/^$LINE$/d" "$DNSMASQ_CONF_PATH"
 | 
						|
  fi
 | 
						|
 | 
						|
  # if we're removing a CNAME record and --no-hosts is not used, we need to ask to remove the record from the /etc/hosts file
 | 
						|
  if [ "$TYPE" = "cname" ] && [ "$NO_HOSTS" = false ]; then
 | 
						|
    # ask to remove the record from the /etc/hosts file
 | 
						|
    printf "[sudo] Do you want to remove this record from /etc/hosts? [y/N] "
 | 
						|
    read -r response
 | 
						|
    case "$response" in
 | 
						|
      [Yy]*)
 | 
						|
        # remove the record from the /etc/hosts file
 | 
						|
        if ! remove_record_from_hosts "$NAME"; then
 | 
						|
          echo "/etc/hosts hasn't been touched."
 | 
						|
        fi
 | 
						|
        ;;
 | 
						|
    esac
 | 
						|
  fi
 | 
						|
  echo "Done."
 | 
						|
  exit 0
 | 
						|
}
 | 
						|
 | 
						|
# list all records of a given type
 | 
						|
list_records() {
 | 
						|
  if [ "$TYPE" = "txt" ]; then
 | 
						|
    TYPE="txt-record"
 | 
						|
  fi
 | 
						|
  grep "^$TYPE=" "$DNSMASQ_CONF_PATH" || echo "No $TYPE records found."
 | 
						|
}
 | 
						|
 | 
						|
# add a record to the /etc/hosts file
 | 
						|
add_record_to_hosts() {
 | 
						|
  domain="$1"
 | 
						|
  
 | 
						|
  # this script doesn't support Windows /etc/hosts editing
 | 
						|
  if [ "$IS_WINDOWS" = true ]; then
 | 
						|
    echo "Adding records to /etc/hosts via this script is not supported on Windows"
 | 
						|
    return 1
 | 
						|
  fi
 | 
						|
 | 
						|
  # check if the record already exists in the /etc/hosts file
 | 
						|
  if check_record_hosts_exists "$domain"; then
 | 
						|
    echo "Record already exists in /etc/hosts: $domain"
 | 
						|
    return 1
 | 
						|
  fi
 | 
						|
 | 
						|
  # add the record to the /etc/hosts file
 | 
						|
  echo "Adding record to /etc/hosts: $domain"
 | 
						|
  echo "This operation will require sudo privileges"
 | 
						|
  if ! echo "127.0.0.1 $domain" | sudo tee -a "/etc/hosts" > /dev/null; then
 | 
						|
    echo "Failed to add record to /etc/hosts"
 | 
						|
    return 1
 | 
						|
  fi
 | 
						|
  echo "$domain added to /etc/hosts."
 | 
						|
  echo "You can now access http://$domain:3000 via browser."
 | 
						|
  return 0
 | 
						|
}
 | 
						|
 | 
						|
remove_record_from_hosts() {
 | 
						|
  domain="$1"
 | 
						|
  
 | 
						|
  # this script doesn't support Windows /etc/hosts editing
 | 
						|
  if [ "$IS_WINDOWS" = true ]; then
 | 
						|
    echo "Removing records from /etc/hosts via this script is not supported on Windows"
 | 
						|
    return 1
 | 
						|
  fi
 | 
						|
 | 
						|
  # check if the record exists in the /etc/hosts file
 | 
						|
  if ! check_record_hosts_exists "$domain"; then
 | 
						|
    echo "Record not found in /etc/hosts: $domain"
 | 
						|
    return 1
 | 
						|
  fi
 | 
						|
 | 
						|
  # remove the record from the /etc/hosts file
 | 
						|
  echo "Removing record from /etc/hosts: $domain"
 | 
						|
  echo "This operation will require sudo privileges"
 | 
						|
  if [ "$IS_DARWIN" = true ]; then
 | 
						|
    if ! sudo sed -i '' "/^127.0.0.1 $domain$/d" "/etc/hosts" 2>/dev/null; then
 | 
						|
      echo "Failed to remove record from /etc/hosts."
 | 
						|
      return 1
 | 
						|
    fi
 | 
						|
  else
 | 
						|
    if ! sudo sed -i "/^127.0.0.1 $domain$/d" "/etc/hosts" 2>/dev/null; then
 | 
						|
      echo "Failed to remove record from /etc/hosts."
 | 
						|
      return 1
 | 
						|
    fi
 | 
						|
  fi
 | 
						|
  echo "$domain removed from /etc/hosts."
 | 
						|
  return 0
 | 
						|
}
 | 
						|
 | 
						|
# check if a record exists in the /etc/hosts file
 | 
						|
check_record_hosts_exists() {
 | 
						|
  domain="$1"
 | 
						|
  
 | 
						|
  # grep for the record
 | 
						|
  if grep -Fxq "127.0.0.1 $domain" "/etc/hosts"; then
 | 
						|
    return 0
 | 
						|
  fi
 | 
						|
  return 1
 | 
						|
}
 | 
						|
 | 
						|
# switch intents
 | 
						|
case "$INTENT" in
 | 
						|
  add)    add_record ;;
 | 
						|
  remove) remove_record ;;
 | 
						|
  list)   list_records ;;
 | 
						|
  *)      usage ;;
 | 
						|
esac
 | 
						|
 |