#!/bin/bash
#
# tor-ctrl is a commandline tool for executing commands on a tor server via
# the controlport.  In order to get this to work, add "ControlPort 9051" and
# "CookieAuthentication 1" to your torrc and reload tor.  Or - if you want a
# fixed password - leave out "CookieAuthentication 1" and use the following
# line to create the appropriate HashedControlPassword entry for your torrc
# (you need to change yourpassword, of course):
#
# echo "HashedControlPassword $(tor --hash-password yourpassword | tail -n 1)"
#
# tor-ctrl will return 0 if it was successful and 1 if not, 2 will be returned
# if something (telnet, xxd) is missing.  4 will be returned if it executed
# several commands from a file.
#
# For setting the bandwidth for specific times of the day, I suggest calling
# tor-ctrl via cron, e.g.:
#
# 0 22 * * * /path/to/tor-ctrl -c "SETCONF bandwidthrate=1mb"
# 0 7 * * *  /path/to/tor-ctrl -c "SETCONF bandwidthrate=100kb"
#
# This would set the bandwidth to 100kb at 07:00 and to 1mb at 22:00.  You can
# use notations like 1mb, 1kb or the number of bytes.
#
# Many, many other things are possible, see
#              https://www.torproject.org/svn/trunk/doc/spec/control-spec.txt
#
# Copyright (c) 2007 by Stefan Behte
#
# tor-ctrl 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 2 of the License, or
# (at your option) any later version.
#
# tor-ctrl is distributed in the hope that it will be 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 tor-ctrl; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# Written by Stefan Behte
#
# Please send bugs, comments, wishes, thanks and success stories to:
# Stefan dot Behte at gmx dot net
#
# Also have a look at my page:
# http://ge.mine.nu/
#
# 2007-10-03: First version, only changing bandwidth possible.
# 2007-10-04: Renaming to "tor-ctrl", added a lot of functions, it's now a
#             general-purpose tool.
#             Added control_auth_cookie/controlpassword auth, getopts,
#             program checks, reading from file etc.

VERSION=v1
TORCTLIP=127.0.0.1
TORCTLPORT=9051
TOR_COOKIE="/var/lib/tor/data/control_auth_cookie"
SLEEP_AFTER_CMD=1
VERBOSE=0

usage()
{
cat <<EOF

tor-ctrl $VERSION by Stefan Behte (http://ge.mine.nu)
You should have a look at 
https://www.torproject.org/svn/trunk/doc/spec/control-spec.txt

usage: tor-ctrl [-switch] [variable]

       [-c] [command] = command to execute
                        notice: always "quote" your command

       [-f] [file]    = file to execute commands from
                        notice: only one command per line

       [-a] [path]    = path to tor's control_auth_cookie
                        default: /var/lib/tor/data/control_auth_cookie
                        notice: do not forget to adjust your torrc

       [-s] [time]    = sleep [var] seconds after each command sent
                        default: 1 second
                        notice: for GETCONF, you can use smaller pause times
                        than for SETCONF; this is due to telnet's behaviour.

       [-p] [pwd]     = Use password [var] instead of tor's control_auth_cookie
                        default: not used
                        notice: do not forget to adjust your torrc
                                
       [-P] [port]     = Tor ControlPort
                        default: 9051

       [-v]           = verbose
                        default: not set
                        notice: the default output is the return code ;)
                        You propably want to set -v when running manually

       Examples:      $0 -c "SETCONF bandwidthrate=1mb"
                      $0 -v -c "GETINFO version"
                      $0 -v -s 0 -P 9051 -p foobar -c "GETCONF bandwidthrate"

EOF
exit 2
}

checkprogs()
{
        programs="telnet"
        if [ "$PASSWORD" = "" ]   
        then
                # you only need xxd when using control_auth_cookie
                programs="$programs xxd"
        fi

        for p in $programs
        do
                which $p &>/dev/null            # are you there?
                if [ "$?" != "0" ]
                then
                        echo "$p is missing."
                        exit 2
                fi
        done
}

sendcmd()
{
        echo "$@"
        sleep ${SLEEP_AFTER_CMD}
}

login()
{
        if [ "$PASSWORD" = "" ]
        then
                sendcmd "AUTHENTICATE $(xxd -c 32 -g 0 ${TOR_COOKIE} | awk '{print $2}')"
        else
                sendcmd "AUTHENTICATE \"${PASSWORD}\""
        fi
}

cmdpipe()
{
        login
        sendcmd "$@"
        sendcmd "QUIT"
}

vecho()
{
        if [ $VERBOSE -ge 1 ]
        then
                echo "$@"
        fi
}

myecho()
{
        STR=$(cat)
        vecho "$STR"

        echo "$STR" | if [ "$(grep -c ^"250 ")" = 3 ]
        then
                exit 0
        else
                exit 1
        fi
}

filepipe()
{
        login
        cat "$1" | while read line
        do
                sendcmd "$line"
        done
        sendcmd "QUIT"
}

while getopts ":a:c:s:p:P:f:vh" Option
do
        case $Option in
                a) TOR_COOKIE="${OPTARG}";;
                c) CMD="${OPTARG}";;
                s) SLEEP_AFTER_CMD="${OPTARG}";;
                p) PASSWORD="${OPTARG}";;
                P) TORCTLPORT="${OPTARG}";;
                f) FILE="${OPTARG}";;
                v) VERBOSE=1;;
                h) usage;;
                *) usage;;
        esac
done

if [ -e "$FILE" ]
then
        checkprogs
        filepipe "$FILE" | telnet $TORCTLIP $TORCTLPORT 2>/dev/null | myecho
        exit 4
fi

if [ "$CMD" != "" ]
then
        checkprogs
        cmdpipe $CMD | telnet $TORCTLIP $TORCTLPORT 2>/dev/null | myecho
else
        usage
fi