mytorsocks.sh 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. #!/bin/bash
  2. cleanup() {
  3. kill $torpid
  4. rm -r "$datadir"
  5. [[ "$file" != "" ]] && rm "$file"
  6. }
  7. usage() {
  8. [[ "$@" != "" ]] && echo "$@"
  9. cat <<EOF
  10. Wrapper to start tor before starting applications with torsocks.
  11. Looks for free ports and starts a new instance of tor.
  12. Usage: $me [${me}_options] -- command [command_options]
  13. Options:
  14. -C Use a control port which is always one higher than the listening port.
  15. -f str Save port and controlport to this file, in shell-sourcable format.
  16. -E Use a selection of European entry/exit nodes: $Ecountries
  17. -c str List of countries to use (note the format above).
  18. The default is to not restrict the choice of entry/exit nodes.
  19. -P int Set Tor SOCKS localhost listening port. Default: $port
  20. -t int Try higher ports if chosen port is in use.
  21. Increase port by 1 for [int] times before giving up.
  22. This option checks ports with fuser The default is to try 100 times.
  23. If set to 0 (zero) higher ports won't be tried.
  24. -r Loop retry, each time with a new tor connection
  25. (requires interactive terminal).
  26. -q Be quiet. Only the launched application speaks.
  27. -d Set delay to sleep (in seconds) after starting tor. Default: $delay
  28. ~/.config/tor/ must be writeable.
  29. EOF
  30. exit 1
  31. }
  32. log() {
  33. [[ "$quiet" == '' ]] && echo -e "$@" >&2
  34. }
  35. trap cleanup EXIT
  36. me="${0##*/}"
  37. Ecountries="{fi},{ax},{se},{no},{dk},{ee},{lv},{lt},{cz},{sk},{at},{si},{it},{ch},{fr},{es},{pt},{be},{nl},{de},{gb},{ie},{is},{gr}"
  38. countries=""
  39. declare -i port=9222 # declare as integer
  40. try=100 # try higher ports so many times
  41. retry=0
  42. delay=1
  43. quiet=''
  44. torpid=bla
  45. file=''
  46. # character for separator line
  47. sepchar="-"
  48. sep=''
  49. columns="$(tput cols)"
  50. [ -z "$columns" ] && columns=80
  51. # adjust separator width to terminal width
  52. for ((i=0;i<columns;i++)); do sep="$sep$sepchar"; done
  53. unset sepchar columns
  54. controlport=false
  55. while getopts "c:CEP:t:rqd:h" opt; do
  56. case $opt in
  57. c)
  58. countries="$OPTARG"
  59. ;;
  60. C)
  61. controlport=true
  62. ;;
  63. E)
  64. countries="$Ecountries"
  65. ;;
  66. f)
  67. file="$OPTARG"
  68. [ -r "$file" ] && usage "File $file already exists. Please remove it."
  69. touch "$file" || usage "Cannot touch $file. Permissions?"
  70. ;;
  71. P) [[ "$OPTARG" =~ [0-9]+ ]] && (( OPTARG >= 0 )) && (( OPTARG <= 65535 )) || usage "Invalid port: $OPTARG"
  72. port="$OPTARG"
  73. ;;
  74. t) try="$OPTARG"
  75. ;;
  76. r) retry=1
  77. ;;
  78. q) quiet='--quiet'
  79. ;;
  80. d) delay="$OPTARG"
  81. ;;
  82. *|h) usage
  83. ;;
  84. esac
  85. done
  86. shift $((OPTIND-1))
  87. unset Ecountries
  88. # simple dependency check
  89. for dep in fuser tor kill torsocks; do
  90. type -f $dep >/dev/null || usage
  91. done
  92. ### Retry Loop
  93. while :; do
  94. now="$(printf "%(%H:%M:%S)T")"
  95. [[ "$controlport" == true ]] && controlport=$((port+1))
  96. if (( try != 0 )); then
  97. [[ "$try" =~ [0-9]+ ]] && (( try <= (65535-port) )) || usage "Invalid number: $try (Must be between 1 and $((65535-port))."
  98. # find a free port
  99. i=0
  100. for ((;i<=try;i++)); do
  101. fuser -sn tcp "$((port+i))" && continue
  102. [[ "$controlport" != false ]] && fuser -sn tcp "$((controlport+i))" && continue
  103. break
  104. done
  105. (( i > try )) && usage "Could not find a free port between $port and $((port+try))"
  106. port=$((port+i))
  107. [[ "$controlport" != false ]] && controlport=$((controlport+i))
  108. else
  109. fuser -sn tcp "$port" && usage "Port $port is not free."
  110. fi
  111. ### prepare tor ###
  112. log "Tor data dir: $HOME/.config/tor/${me}.${now}.${port}"
  113. datadir="$HOME/.config/tor/${me}.${now}.${port}"
  114. torrc="$datadir/torrc"
  115. rm -rf "$datadir"
  116. mkdir -p "$datadir" || usage "Cannot mkdir $datadir"
  117. if [[ "$countries" == "" ]]; then
  118. printf "SocksPort $port\nDataDirectory \"$datadir\"\nRunAsDaemon 1\nPidFile \"$datadir/pid\"\n" > "$torrc"
  119. log "Tor: no restrictions on choice of nodes\n$sep"
  120. else
  121. printf "EntryNodes $countries\nExitNodes $countries\nSocksPort $port\nDataDirectory \"$datadir\"\nRunAsDaemon 1\nPidFile \"$datadir/pid\"\n" > "$torrc"
  122. log "Tor: nodes only from these countries:\n$countries\n$sep"
  123. fi
  124. [[ "$controlport" != false ]] && printf "ControlPort $controlport\nCookieAuthentication 0\n" >> "$torrc"
  125. ### launch tor ###
  126. tor $quiet -f "$torrc" >&2
  127. ###
  128. [ -r "$datadir/pid" ] && torpid="$(<"$datadir/pid")"
  129. [[ "$torpid" == 0 ]] && usage "Apparently tor did not start; $datadir/pid is empty or does not exist."
  130. [[ "$file" != "" ]] && printf "port=%s\ncontrolport=%s\n" "$port" "$controlport" > "$file"
  131. log "$sep\nTor pid: $torpid\n$sep\nWaiting ${delay}s before continuing..."
  132. sleep "$delay"
  133. ### launch the script ###
  134. if [[ "$@" != "" ]] && [ -x "$1" ]; then
  135. log "$sep\nLaunching: torsocks $quiet -i -P $port $@\n$sep"
  136. torsocks $quiet -i -P "$port" "$@"
  137. else
  138. echo "Tor is now running on port $port."
  139. [[ "$controlport" != false ]] && echo "Control port is $controlport."
  140. fi
  141. ###
  142. if [[ "$retry" == 1 ]]; then
  143. read -p "Do you want to retry (r)? " answer
  144. [[ "$answer" != r ]] && exit 0
  145. fi
  146. cleanup
  147. echo -e "$sep\n###### Retrying #########\n$sep"
  148. clear
  149. done