#!/bin/bash
# shellcheck disable=SC2120,SC2128,SC2178

# This file is part of TorBox, an easy to use anonymizing router based on Raspberry Pi.
# Copyright (C) 2026 radio_24
# Contact: anonym@torbox.ch
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it is useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# DESCRIPTION
# This file changes the MAC adress of TorBox's interfaces. Possible
# settings are:
# - random for randomizing the MAC (this is the default)
# - permanent for using the permanent, unique MAC address of the interface
# - xx:xx:xx:xx:xx:xx for using a specifig MAC address (for example for
#   passing through a captive portal)
#
# SYNTAX
# ./change_MAC [<interface>] [<how_to_change>]
#
# The <interface> is the interface on which the MAC address should be changed (wlan0, wlan1, eth0, eth1).
#
# With <how_to_change> the way of changing the MAC address can be defined. Possible values for <how_to_change> are:
# 1 - randomize
# 2 - reset to the permanent, unique MAC address
# 3 - use a a specifig MAC address
#
#
###### SET VARIABLES ######
#
# SIZE OF THE MENU
#
# How many items do you have in the main menu?
NO_ITEMS=2
#
# How many lines are only for decoration and spaces?
NO_SPACER=2
#
#Set the the variables for the menu
MENU_WIDTH=80
MENU_WIDTH_REDUX=60
MENU_HEIGHT_15=15
# MENU_HEIGHT should not exceed 26
MENU_HEIGHT=$((8+NO_ITEMS+NO_SPACER))
MENU_LIST_HEIGHT=$((NO_ITEMS+NO_SPACER))
#
#Colors
RED='\033[1;31m'
YELLOW='\033[1;93m'
NOCOLOR='\033[0m'
#
#Other variables
RUNFILE="/home/torbox/torbox/run/torbox.run"
O_DEVICE=$1
CHOICE2=""
CHOICE2=$2

##############################
######## FUNCTIONS ########

# This function imports the configuration and makes some preparations
read_config()
{
	# List all available network interfaces
	# Important: Randomizing usb0 on a TorBox mini will lock you out!!
	if grep "^TORBOX_MINI=1" ${RUNFILE} ; then
		AVAILABLE_INTERFACES=$(ip -o link show | awk -F': ' '{print $2}' | sed "/^lo/d" | sed "/^wwan/d" | sed "/^usb0/d")
	else
		AVAILABLE_INTERFACES=$(ip -o link show | awk -F': ' '{print $2}' | sed "/^lo/d" | sed "/^wwan/d")
  fi
	# Read interfaces from run/torbox.run
	CLIENT_IFACE=$(grep "^CLIENT_IFACE=" ${RUNFILE} | sed "s/CLIENT_IFACE=//g") 2>/dev/null
	INTERNET_IFACE=$(grep "^INTERNET_IFACE=" ${RUNFILE} | sed "s/INTERNET_IFACE=//g") 2>/dev/null
	MAC_eth0=$(grep "^MAC_eth0=" ${RUNFILE} | sed "s/MAC_eth0=//g") 2>/dev/null
	MAC_eth1=$(grep "^MAC_eth1=" ${RUNFILE} | sed "s/MAC_eth1=//g") 2>/dev/null
	MAC_wlan0=$(grep "^MAC_wlan0=" ${RUNFILE} | sed "s/MAC_wlan0=//g") 2>/dev/null
	MAC_wlan1=$(grep "^MAC_wlan1=" ${RUNFILE} | sed "s/MAC_wlan1=//g") 2>/dev/null
	MAC_usb0=$(grep "^MAC_wlan1=" ${RUNFILE} | sed "s/MAC_wlan1=//g") 2>/dev/null
}

# This function randomize, resets or changes the MAC address of an interface.
# Syntax: change_mac_address [OUTPUT_INTERFACE]
change_mac_address()
{
	O_DEVICE=$1
	exitstatus=0
	if [ -z "$CHOICE2" ]; then
		INPUT="TorBox v.0.5.5 - Change the MAC address for $O_DEVICE"
		CHOICE2=$(whiptail --nocancel --title "$INPUT" --radiolist "Choose with SPACE and then press ENTER (ESC -> go back)" 9 $MENU_WIDTH 3 \
		"1" "Randomize the MAC addresse(s)" ON \
		"2" "Use the permanent, unique MAC address of the interface(s)" OFF \
		"3" "Use a a specifig MAC address for the selected interface(s)" OFF \
		3>&1 1>&2 2>&3)
		exitstatus=$?
	fi

	# macchanger has to be installed
	if [ $exitstatus = 0 ]; then
		case $CHOICE2 in
			1)
				MAC_ADDRESS="random"
				sudo sed -i "s/^MAC_$O_DEVICE=.*/MAC_$O_DEVICE=$MAC_ADDRESS/" ${RUNFILE}
				clear
				# We have only to put an interface down, if it is not already down
				# ATTENTION not connected interfaces have to put down, even the state is already down --> NO-CARRIER
				if ip link | grep "$O_DEVICE" | grep -e "state UP" -e "NO-CARRIER" ; then
					clear
					echo -e "${RED}[+] Shutting interface $O_DEVICE down!${NOCOLOR}"
					sudo ip link set dev $O_DEVICE down
					INTERFACE1_DOWN=1
					sleep 2
				fi
				#
				echo -e "${RED}[+] Randomizing MAC address...${NOCOLOR}"
				echo
				sudo macchanger -r $O_DEVICE
				echo
				# We have only to put an interface up, if it was up before
				if [ "$INTERFACE1_DOWN" == "1" ]; then
					echo -e "${RED}[+] Starting up interface $O_DEVICE!${NOCOLOR}"
					sudo ip link set dev $O_DEVICE up
					INTERFACE1_DOWN=0
					echo
				fi
				#
				sleep 5
			;;
			2)
				MAC_ADDRESS="permanent"
				sudo sed -i "s/^MAC_$O_DEVICE=.*/MAC_$O_DEVICE=$MAC_ADDRESS/" ${RUNFILE}
				clear
				# We have only to put an interface down, if it is not already down
				# ATTENTION not connected interfaces have to put down, even the state is already down --> NO-CARRIER
				if ip link | grep "$O_DEVICE" | grep -e "state UP" -e "NO-CARRIER" ; then
					clear
					echo -e "${RED}[+] Shutting interface $O_DEVICE down!${NOCOLOR}"
					sudo ip link set dev $O_DEVICE down
					INTERFACE1_DOWN=1
					sleep 2
				fi
				#
				echo -e "${RED}[+] Resetting MAC address...${NOCOLOR}"
				echo
				sudo macchanger -p $O_DEVICE
				echo
				# We have only to put an interface up, if it was up before
				if [ "$INTERFACE1_DOWN" == "1" ]; then
					echo -e "${RED}[+] Starting up interface $O_DEVICE!${NOCOLOR}"
					sudo ip link set dev $O_DEVICE up
					INTERFACE1_DOWN=0
					echo
				fi
				#
				sleep 5
			;;
			3)
				OLD_MAC_ADDRESS="MAC_${O_DEVICE}"
				[[ "${!OLD_MAC_ADDRESS}" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ ]] && MAC_ADDRESS="${!OLD_MAC_ADDRESS}" || MAC_ADDRESS=""
			 	MAC_ADDRESS=$(whiptail --title "TorBox - INFO" --inputbox "\n\nPlease enter the MAC address of the device which already passed the Captive Portal (e.g.: 8B:C2:CC:28:6A:97; for more information check https://www.torbox.ch/?page_id=2627):" $MENU_HEIGHT_15 $MENU_WIDTH_REDUX $MAC_ADDRESS 3>&1 1>&2 2>&3)
			 	if [ ! -z "${MAC_ADDRESS}" ]; then
				 	[[ "$MAC_ADDRESS" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ ]] && MAC_CHECK="valid" || MAC_CHECK="invalid"
				 	if [ "$MAC_CHECK" == "invalid" ]; then
					 	clear
					 	echo -e "${YELLOW}[!] THIS IS NOT A VALID MAC ADDRESS!${NOCOLOR}"
					 	echo ""
					 	read -n 1 -s -r -p $'\e[1;31mPlease press any key to continue... \e[0m'
					 	clear
					 	exit 0
				 	fi
				 	sudo sed -i "s/^MAC_$O_DEVICE=.*/MAC_$O_DEVICE=$MAC_ADDRESS/" ${RUNFILE}
					clear
					# We have only to put an interface down, if it is not already down
					# ATTENTION not connected interfaces have to put down, even the state is already down --> NO-CARRIER
					if ip link | grep "$O_DEVICE" | grep -e "state UP" -e "NO-CARRIER" ; then
						clear
						echo -e "${RED}[+] Shutting interface $O_DEVICE down!${NOCOLOR}"
			 	 		sudo ip link set dev $O_DEVICE down
						INTERFACE1_DOWN=1
						sleep 2
					fi
					#
					echo -e "${RED}[+] Setting MAC address to $MAC_ADDRESS ${NOCOLOR}"
			 		sudo ip link set dev $O_DEVICE address $MAC_ADDRESS
					# We have only to put an interface up, if it was up before
					if [ "$INTERFACE1_DOWN" == "1" ]; then
						echo -e "${RED}[+] Starting up interface $O_DEVICE!${NOCOLOR}"
			 			sudo ip link set dev $O_DEVICE up
						INTERFACE1_DOWN=0
					fi
					#
					echo
					sleep 5
				else
					clear
					echo -e "${YELLOW}[!] YOU DIDN'T ENTER A MAC ADDRESS!${NOCOLOR}"
					echo " "
					read -n 1 -s -r -p $'\e[1;31mPlease press any key to continue... \e[0m'
					exit 1
				fi
			;;

			*)
				clear
				exit 0
		esac
		CHOICE2=$2
	fi
}

# This function restarts hostapd or asks for reselecting the Internet source in the main menu.
change_mac_address_finish()
{
	# I guess the old one was wrong
	# if [[ "$O_DEVICE" =~ "$CLIENT_IFACE" ]]; then
	if [[ "$CLIENT_IFACE" =~ "$O_DEVICE" ]]; then
		# NEW v.0.5.4 avoid to run hostapd if it is masked
		AP_STATUS=$(sudo systemctl is-enabled hostapd)
		if [ "$AP_STATUS" != "masked" ] ; then
			echo -e "${RED}[+] Restarting hostapd!${NOCOLOR}"
			sudo systemctl restart hostapd
		fi
	elif [[ "$INTERNET_IFACE" == "$O_DEVICE" ]]; then
		echo
		echo -e "${YELLOW}[!] YOU MUST RECONNECT TO THE INTERNET IN THE MAIN MENU!${NOCOLOR}"
	fi
	echo " "
	read -n 1 -s -r -p $'\e[1;31mPlease press any key to continue... \e[0m'
}

######## MAIN ########
read_config

if [ -z "$CHOICE2" ]; then
	CHOICE3=$(whiptail --cancel-button "Back" --title "TorBox v.0.5.5 - MAC ADRESSES OF TORBOX'S INTERFACES" --menu "Choose an option (ESC -> go back)" $MENU_HEIGHT $MENU_WIDTH $MENU_LIST_HEIGHT \
	"==" "===============================================================" \
	" 1" "Change/randomize the MAC adresses of TorBox's interfaces"  \
	" 2" "List the MAC adresses of TorBox's interfaces"  \
	"==" "===============================================================" \
	3>&1 1>&2 2>&3)
	CHOICE3=$(echo "$CHOICE3" | tr -d ' ')
else
	CHOICE3=1
fi

case "$CHOICE3" in
  # Change/randomize the MAC adresses of TorBox's interfaces
  1)
		if [ -z "$CHOICE2" ]; then
			if grep -q "usb0" <<< "$AVAILABLE_INTERFACES"; then
				###### DISPLAY THE AVAILABLE INTERFACES inluding usb0 ######
				CHOICE=$(whiptail --nocancel --title "TorBox v.0.5.5 - CHANGE MAC ADRESSES" --checklist --separate-output "\nPlease, use SPACE to select/deselect, ENTER to apply, ESC to go back." $MENU_HEIGHT_15 $MENU_WIDTH 5 \
				"1" "Change the wireless interface 0 (onboard chip; wlan0)" OFF \
				"2" "Change the wireless interface 1 (USB adapter; wlan1)" OFF \
				"3" "Change the ethernet interface 0 (onboard ethernet connector; eth0)" OFF \
				"4" "Change the ethernet interface 1 (USB adapter; eth1)" OFF \
				"5" "Change the usb0 interface (USB adapter)" OFF \
				3>&1 1>&2 2>&3)
			else
				###### DISPLAY THE AVAILABLE INTERFACES without usb0 ######
				CHOICE=$(whiptail --nocancel --title "TorBox v.0.5.5 - CHANGE MAC ADRESSES" --checklist --separate-output "\n\Please, use SPACE to select/deselect, ENTER to apply, ESC to go back." 11 $MENU_WIDTH 4 \
				"1" "Change the wireless interface 0 (onboard chip; wlan0)" OFF \
				"2" "Change the wireless interface 1 (USB adapter; wlan1)" OFF \
				"3" "Change the ethernet interface 0 (onboard ethernet connector; eth0)" OFF \
				"4" "Change the ethernet interface 1 (USB adapter; eth1)" OFF \
				3>&1 1>&2 2>&3)
			fi
			if [ ! -z "$CHOICE" ]; then mapfile -t CHOICE <<< "$CHOICE"; else exit 1; fi
			if [ "$CHOICE" == "1" ]; then O_DEVICE="wlan0";
			elif [ "$CHOICE" == "2" ]; then O_DEVICE="wlan1";
			elif [ "$CHOICE" == "3" ]; then O_DEVICE="eth0";
			elif [ "$CHOICE" == "4" ]; then O_DEVICE="eth1";
			elif [ "$CHOICE" == "5" ]; then O_DEVICE="usb0"; fi
		else
			if [ "$O_DEVICE" == "wlan0" ]; then CHOICE=1;
			elif [ "$O_DEVICE" == "wlan1" ]; then CHOICE=2;
			elif [ "$O_DEVICE" == "eth0" ]; then CHOICE=3;
			elif [ "$O_DEVICE" == "eth1" ]; then CHOICE=4;
			elif [ "$O_DEVICE" == "usb0" ]; then CHOICE=5; fi
		fi
		[ -z "$CHOICE" ] && exit 0
		if grep -q "$O_DEVICE" <<< "$AVAILABLE_INTERFACES"; then
			change_mac_address $O_DEVICE
			if [ ! -z "$O_DEVICE" ] && [ ! -z "$CHOICE2" ]; then change_mac_address_finish; fi
		else
			clear
			echo -e "${RED}[!] Interface ${YELLOW}$O_DEVICE is not available - skipping!${NOCOLOR}"
			echo " "
			read -n 1 -s -r -p $'\e[1;31mPlease press any key to continue... \e[0m'
		fi
		CHOICE3=""
	;;

	# "List the MAC adresses of TorBox's interfaces
	2)
		clear
		echo -e "${YELLOW}List wireless / ethernet interfaces with MAC status and adress:${NOCOLOR}"
		echo ""
		O_DEVICE="wlan0"
		if grep -q "$O_DEVICE" <<< "$AVAILABLE_INTERFACES"; then
			MAC_ADDRESS_NUMBER=$(ip -o link show $O_DEVICE | cut -d ' ' -f 2,20 | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}')
 			echo -e "Interface: ${RED}$O_DEVICE${NOCOLOR} - status: ${RED}$MAC_wlan0${NOCOLOR} - current MAC: ${RED}$MAC_ADDRESS_NUMBER${NOCOLOR}"
		fi
		O_DEVICE="wlan1"
		if grep -q "$O_DEVICE" <<< "$AVAILABLE_INTERFACES"; then
			MAC_ADDRESS_NUMBER=$(ip -o link show $O_DEVICE | cut -d ' ' -f 2,20 | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}')
			echo -e "Interface: ${RED}$O_DEVICE${NOCOLOR} - status: ${RED}$MAC_wlan1${NOCOLOR} - current MAC: ${RED}$MAC_ADDRESS_NUMBER${NOCOLOR}"
		fi
		O_DEVICE="eth0"
		if grep -q "$O_DEVICE" <<< "$AVAILABLE_INTERFACES"; then
			MAC_ADDRESS_NUMBER=$(ip -o link show $O_DEVICE | cut -d ' ' -f 2,20 | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}')
			echo -e "Interface: ${RED}$O_DEVICE${NOCOLOR} - status: ${RED}$MAC_eth0${NOCOLOR} - current MAC: ${RED}$MAC_ADDRESS_NUMBER${NOCOLOR}"
		fi
		O_DEVICE="eth1"
		if grep -q "$O_DEVICE" <<< "$AVAILABLE_INTERFACES"; then
			MAC_ADDRESS_NUMBER=$(ip -o link show $O_DEVICE | cut -d ' ' -f 2,20 | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}')
 			echo -e "Interface: ${RED}$O_DEVICE${NOCOLOR} - status: ${RED}$MAC_eth1${NOCOLOR} - current MAC: ${RED}$MAC_ADDRESS_NUMBER${NOCOLOR}"
		fi
		O_DEVICE="usb0"
		if grep -q "$O_DEVICE" <<< "$AVAILABLE_INTERFACES"; then
			MAC_ADDRESS_NUMBER=$(ip -o link show $O_DEVICE | cut -d ' ' -f 2,20 | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}')
			echo -e "Interface: ${RED}$O_DEVICE${NOCOLOR} - status: ${RED}$MAC_usb0${NOCOLOR} - current MAC: ${RED}$MAC_ADDRESS_NUMBER${NOCOLOR}"
		fi
		echo ""
		read -n 1 -s -r -p "Press any key to continue"
		clear
		CHOICE3=""
	;;

	*)
    clear
    exit 0
esac

#bash change_MAC
exit 0
