9

I just wrote an IRC bot in bash (I know, I know). It does everything I need it to (SASL auth, resolves links, etc.), except it doesn't connect over SSL. After much googling and much bothering on IRC, I have yet to find a way to get /dev/tcp to use SSL. Here is my current code:

#!/bin/bash

if [[ "$1" == "-debug" ]]; then
    set -x
fi

irc_send() {
    printf ">>> %s\n" "$1"
    printf "%s\r\n" "$1" >&3 &
}

sasl_successful() {
    while read -ru3 line; do
        printf "%s\n" "$line"
        read -r location numeric rest <<< "$line"

        if [[ "$numeric" == "903" ]]; then
            return 0
        elif [[ "$numeric" == "904" || "$numeric" == "906" ]]; then
            return 1
        fi
    done
}

sasl_connect() {
    user="$1"
    pass="$2"
    server_pass="$3"
    nick="$4"
    ident="$5"
    realname="$6"
    saslstring="$(printf '%s\0%s\0%s' "$user" "$user" "$pass" | base64)"

    irc_send "CAP REQ :sasl"

    if [[ "$server_pass" ]]; then
        irc_send "PASS $server_pass"
    fi

    irc_send "NICK $nick"
    irc_send "USER $ident * * :$realname"
    irc_send "AUTHENTICATE PLAIN"
    irc_send "AUTHENTICATE $saslstring"

    if sasl_successful; then
        irc_send "CAP END"
        return 0
    else
        return 1
    fi
}

get_title() {
    xmllint --html --xpath '//title/text()' - <<< "$(curl -sL "$1")" 2> /dev/null
}

domain_base() {
    domain="$(python -c "import urlparse; print urlparse.urlparse('$1').netloc")"

    if [[ -z "$domain" ]]; then
        echo "$1"
    else
        echo "$domain"
    fi
}

links=(
    'http://static.giantbomb.com/uploads/square_small/2/25062/833357-link_avatar3.jpg'
    'http://vignette1.wikia.nocookie.net/zelda/images/2/2a/Fierce_Deity_Link.png/revision/latest?cb=20100124002042'
    'http://8wayrun.com/attachments/lnksc2art2-jpg.3846/'
    'https://camo.githubusercontent.com/9bb56499161ec0a5c20b6b3a3af674f51072af7c/687474703a2f2f7777772e6e6f727468636173746c652e636f2e756b2f6775696c642f6172742f616e6e615f6c2f6c696e6b312e6a7067'
    'http://www.jdubuzz.com/files/2015/05/Link_3_ST.png'
    'http://www.smashbros.com/images/og/link.jpg'
    'https://upload.wikimedia.org/wikipedia/en/3/39/Wakerlink.jpg'
)

host="irc.freenode.net"
server_pass=""
port=6667
nick="mewtwo"
user="mewtwo"
realname="A simple irc bot written in bash."
channels="#somechannel"
username="$1"
password="$2"

exec 3<>"/dev/tcp/$host/$port"

if ! sasl_connect "$username" "$password" "$server_pass" "$nick" "$user" "$realname"; then
    printf "Could not connect.\n" >&2
fi

irc_send "JOIN $channels"

while :; do
    irc_send "PING $RANDOM"
    sleep 1m
done &

while read -ru3 line; do
    line="$(perl -pe 's/\r\n/\n/' <<< "$line")"
    read -r from type target msg <<<  "$line"
    msg="${msg#:}" nick="${from#:}" nick="${nick%%!*}" host="${from%%@*}" host="${from#"$host"@}"

    # parsing logic
    if [[ "$type" == "PRIVMSG" ]]; then
        printf "[%s] %s@%s: %s\n" "$target" "$nick" "$host" "$msg" &
    else
        printf "%s\n" "$line" &
    fi

    # response logic
    if [[ "$type" == "PRIVMSG" ]]; then
        if [[ "$msg" == "link?" ]]; then
            irc_send "PRIVMSG $target :${links[RANDOM % ${#links[@]}]}"
        fi

        urls=($(grep -oPm 1 '(?:https?:\/\/)?([a-z\d-]*\.)?[a-z\d-]+\.[a-z]+(?:\/\S*)?(?(?<=\.(jpg|png|gif))(*SKIP)(*F))' <<< "$msg"))

        for url in "${urls[@]}"; do
            irc_send "PRIVMSG $target :[$(get_title "$url")] - $(domain_base "$url")"
        done
    fi
done

Any suggestions are welcome!

2
  • "to get /dev/tcp to use SSL" but why? TLS is already so complicated... Why not just setting a local stunnel? It will nicely handle TLS for you and you just connect locally to it through TCP/IP. TLS mandates various exchanges on top of TCP before exchanging any data, I doubt you can redo that simply in bash, even without taking into account performance and security constraints. Commented Aug 8, 2018 at 0:16
  • 1
    I also use this technique for checking port availability on my home network. Would be nice to know how to use SSL. I just came across your question searching for an answer myself. Commented Aug 11, 2018 at 18:53

1 Answer 1

2

Open SSL socket with OpenSSL and talk to it with stdin stdout

openssl s_client irc.freenode.net:7070
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.