View Single Post
  #4  
Old 17.10.2010, 01:08
Entropy
Guest
 
Posts: n/a
Default Help for D-Link DIR-635 under Linux (no UPNP)

Moderator Edit: The script in the Wiki did not work for this user.
-----------------------------------------------------------------
I scripted one on my own (This was quite tricky, since I had to transfer lots of java-script-functions the router uses to compute the login hash...).

What I did was, I wrote a bash-script (sorry Windows-guys, Unix-only...) to do basic connecting/ disconnecting/ reconnecting with the router.
Only problem: The login-hash.
To compute this I wrote a java-program that computes the hash from the random number the router sends and the password.

bash-script:
Spoiler:
Code:
#!/bin/bash

#begin options
declare nonsilent
nonsilent=true
declare no_ignore_failed
no_ignore_failed=true
declare timeout
timeout=60
declare pass
pass=""
declare javadir
javadir="${HOME}/java/"
#end options

#begin global variables
declare action
action=""
declare newip
newip=""
declare exitcode
exitcode=0
#end global variables

#begin helper functions
function echo {
    if ${nonsilent} ; then
        builtin echo "$@"
    fi
}

function enable_login {
    data=`lynx -dump **External links are only visible to Support Staff**    data=${data:9:8}
    wd=$(pwd)
    cd ${javadir}
    code=$(java prepare_login "${data}" "${pass}")
    cd ${wd}
    response=$(lynx -dump "**External links are only visible to Support Staff**)
    if [ "${response:3}" == "ERROR" ]; then
        echo "Login refused (wrong password?)"
        exitcode=$((${exitcode}|16))
        if ${no_ignore_failed} ; then
            echo "Exit."
            exit ${exitcode}
        else
            echo "Trying to proceed anyway."
        fi
    fi
#    lynx -dump **External links are only visible to Support Staff**}

function connect {
    echo "Connecting..."
    lynx -dump **External links are only visible to Support Staff**    echo "Connection Requested. Waiting..."

    echo "" > .TEMPNEWIP
    lynx -dump "**External links are only visible to Support Staff** > .TEMPNEWIP &
    lynxpid=$!
    declare success
    success=false
    for ((i=0; i<${timeout}; i++)) do
        if [ -n "$(cat .TEMPNEWIP)" ]; then
            success=true
            newip=$(cat .TEMPNEWIP)
            rm .TEMPNEWIP
            break
        fi
        sleep 1
    done
    if [ -z "${newip}" ]; then
        echo "Connection failed. Consider manual reconnection (or wait some longer)."
        kill ${lynxpid} > /dev/null
        rm .TEMPNEWIP
        exitcode=$((${exitcode}|4))
    else
        echo "Done."
        echo "New IP: ${newip}"
        exitcode=$((${exitcode}|1))
    fi
}
function disconnect {
    echo "Disconnecting..."
    lynx -dump **External links are only visible to Support Staff**    echo "Done."
    exitcode=$((${exitcode}|2))
}
function reconnect {
    oldip=$(lynx -dump "**External links are only visible to Support Staff**)
    echo "Attempting reconnect..."
    echo "Old IP was: ${oldip}"
    echo ""
    disconnect
    echo ""
    sleep 1
    connect
    if [ -n "${newip}" ]; then
        if [ "${oldip}" == "${newip}" ]; then
            echo "New IP equals old one (probably static IP)."
            exitcode=$((${exitcode}|8))
        else
            echo "New IP is different from old one."
        fi
    fi
}
#end helper functions

#begin script
case ${1} in
    connect|disconnect|reconnect)
        action=${1}
        pass=${2}
        if [ -n "${3}" ]; then
            shift
            shift
            while [ -n "${1}" ]; do
                case ${param} in
                    -t|--timeout)
                        shift
                        timeout=${1}
                        shift
                        ;;
                    -s|--silent)
                        nonsilent=false
                        ;;
                    -i|--ignore-failed)
                        no_ignore_failed=false
                        shift
                        ;;
                    -j|--java-dir)
                        shift
                        javadir=${1}
                        shift
                        ;;
                    *)
                        exitcode=$((${exitcode}|128))
                        echo "Ignored unknown option:   ${1}"
                        shift
                        ;;
                esac
            done
        fi
        enable_login
        ;;
    *)
        echo "Usage:"
        echo "routerctrl.sh (connect|disconnect|reconnect) <pass> [OPTIONS]"
        echo ""
        echo "Valid options are:"
        echo "    -t|--timeout <timeout>    Specifies timeout in seconds for connect (default: 60)"
        echo "    -s|--silent               Silent"
        echo "    -i|--ignore-failed        Tries to proceed with further steps even if login is refused"
        echo "    -j|--java-dir <dir>       Specify directory of prepare_login.class"
        echo ""
        echo "Exitcodes (or'd):"
        echo "    0:   nothing"
        echo "    1:   successfully connected"
        echo "    2:   successfully disconnected"
        echo "    4:   connecting failed"
        echo "    8:   reconnected successfully, but new IP equals old one"
        echo "    16:  login refused"
        echo "    128: one or more unknown options specified"
        echo ""
        echo "Written by Entropy"
        exit 0
esac

${action}
exit ${exitcode}
#end script

Java-code:
Spoiler:
Code:
// Written by Entropy
class prepare_login {
    static private int[] b64_char_to_6;
    static private String pwd;

    prepare_login() {
    }
    
    //Build a 256 entry array that convert a base-64 character to its corresponding 6-bit number. Invalid characters will be translated to 0.
    static private void build_b64_char_to_6() { 
        b64_char_to_6 = new int[256];
        for (int i=0; i<256; ++i) b64_char_to_6[i] = 0;
        for (int i=0; i<26; ++i) b64_char_to_6[i+65] = i+1;
        for (int i=0; i<26; ++i) b64_char_to_6[i+97] = i+27;
        for (int i=0; i<10; ++i) b64_char_to_6[i+48] = i+53;
        b64_char_to_6[95] = 63;
    }

    static private void from_base64(String buf, int[] dst) {
        byte j = 0;
        for (int i = 0; i < buf.length(); i += 4) {
            int cc_1 = (b64_char_to_6[(int)buf.charAt(i)] << 18);
            int cc_2 = (b64_char_to_6[(int)buf.charAt(i+1)] << 12);
            int cc_3 = (b64_char_to_6[(int)buf.charAt(i+2)] << 6);
            int cc_4 = (b64_char_to_6[(int)buf.charAt(i+3)]);
            int cc = cc_1 | cc_2 | cc_3 | cc_4;
            dst[j]   = (cc >> 16) & 0xff;
            dst[j+1] = (cc >> 8) & 0xff;
            dst[j+2] = (cc) & 0xff;
            j += 3;
        }
    }
    
    static private String maketwochar(String s) {
        if (s.length()<2) s="0"+s;
        return s;
    }
    
    static private String fillstr(String s, byte l) {
        while (s.length()<l) {
            s = s + (char)1;
        }
        return s;
    }
    
    static private String to_HexString(int[] dst) {
        String s = "";
        for (byte i=0; i<4; ++i) {
            s = s + maketwochar(Integer.toHexString(dst[i]));
        }
        return s.toUpperCase();
    }
    
// Convert a string to an array of little-endian words
    static private void str2binl(String s, int[] bin) {
        byte mask = (byte)((1 << 8) - 1);
        for(int i=0; i<s.length()*8; i+=8) {
            bin[i>>5] |= ((byte)s.charAt(i/8) & mask) << (i%32);
        }
    }
    
// Bitwise rotate a 32-bit number to the left.
    static private int bit_rol(int num, int cnt)
    {
        return (num << cnt) | (num >>> (32 - cnt));
    }
    
// Some obscure adding algorithm the router uses
    static private int safe_add(int x, int y) {
        int lsw = (x & 0xFFFF) + (y & 0xFFFF);
        int msw = (x >> 16) + (y >> 16) + (lsw >> 16);
        return (msw << 16) | (lsw & 0xFFFF);
    }
    
// Begin basic md5 algorithms
    static private int md5_cmn(int q, int a, int b, int x, int s, int t) {
        return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
    }
    
    static private int md5_ff(int a, int b, int c, int d, int x, int s, int t) {
        return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
    }
    
    static private int md5_gg(int a, int b, int c, int d, int x, int s, int t) {
        return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
    }
    
    static private int md5_hh(int a, int b, int c, int d, int x, int s, int t) {
        return md5_cmn(b ^ c ^ d, a, b, x, s, t);
    }
    
    static private int md5_ii(int a, int b, int c, int d, int x, int s, int t) {
        return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
    }
// End basic md5 algorithms

// Core md5algorithm
    static public void core_md5(int[] x, int len, int[] md5_arr) {
        x[len >> 5] |= 0x80 << ((len) % 32);
        x[(((len + 64) >>> 9) << 4) + 14] = len;
        
        int a =  1732584193;
        int b = -271733879;
        int c = -1732584194;
        int d =  271733878;

        for (int i = 0; i < x.length; i+=16) {
            int olda = a;
            int oldb = b;
            int oldc = c;
            int oldd = d;
            
            a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
            d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
            c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
            b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
            a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
            d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
            c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
            b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
            a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
            d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
            c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
            b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
            a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
            d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
            c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
            b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

            a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
            d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
            c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
            b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
            a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
            d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
            c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
            b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
            a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
            d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
            c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
            b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
            a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
            d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
            c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
            b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

            a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
            d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
            c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
            b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
            a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
            d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
            c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
            b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
            a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
            d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
            c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
            b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
            a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
            d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
            c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
            b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

            a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
            d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
            c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
            b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
            a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
            d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
            c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
            b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
            a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
            d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
            c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
            b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
            a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
            d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
            c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
            b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

            a = safe_add(a, olda);
            b = safe_add(b, oldb);
            c = safe_add(c, oldc);
            d = safe_add(d, oldd);
        }
        md5_arr[0] = a;
        md5_arr[1] = b;
        md5_arr[2] = c;
        md5_arr[3] = d;
    }
    
    static private String binl2hex(int[] binarray) {
        String hex_tab = "0123456789abcdef";
        String str = "";
        for (int i = 0; i < binarray.length*4; ++i) {
            str = str + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8)) & 0xF);
        }
        return str;
    }
    
    static private String hex_md5(String s) {
        int[] bin;
        bin = new int[32];
        int[] md5_arr;
        md5_arr = new int[4];

        str2binl(s, bin);
        core_md5(bin, s.length()*8, md5_arr);
        return binl2hex(md5_arr);
    }
    
// Convert a hex string of 'len' bytes into a byte array of 'len2' bytes. Ignore leading and trailing whitespace. If there are any conversion errors or the string is the wrong length or the string is empty or all whitespace, return an array of all zeros.
// Since here input is guaranteed to be a pure hex string, we ignore all the case-checking the router-script does...
    static private void convertHexString(String s, int len, int len2, short[] a) {
        int i;
        for (i=0; i < len; ++i) {
            a[i] = (short)Integer.parseInt(s.substring(i*2,i*2+2),16);
        }
        for (; i < len2; ++i) {
            a[i] = (short)0;
        }
    }
    
    static private String convertToBase64(short[] arr) {
        // Make array with k*4 entries
        short[] a;
        int templen = arr.length;
        while ( (templen%3) != 0 ) {
            ++templen;
        }
        a = new short[templen];
        int i;
        for (i = 0; i < arr.length; ++i) {
            a[i] = arr[i];
        }
        for (; i < templen; ++i) {
            a[i] = (short)0;
        }
        
        String b64 = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
        String str = "";
        
        for (i=0; i < a.length; i += 3) {
            int x = (a[i] << 16) | (a[i+1] << 8) | a[i+2];
            str = str + b64.charAt((x >> 18) & 0x3f) + b64.charAt((x >> 12) & 0x3f) + b64.charAt((x >> 6) & 0x3f) + b64.charAt(x & 0x3f);
        }
        return str;
    }

    static public void main(String[] args){
        String buf;
        int[] dst;
        String str;
        String shex;
        String hash;
        String saltHash;
        short[] bytearr;
        buf = args[0];
        pwd = args[1];
        while ( (buf.length()%4) != 0 ) {   // Make buf.length() divisible by 4
            buf = buf + ".";
        }
        dst = new int[buf.length()];
        build_b64_char_to_6();
        from_base64(buf, dst);
        shex = to_HexString(dst);
        pwd = fillstr(pwd, (byte)16);
        str = shex + pwd;
        str = fillstr(str, (byte)64);
        hash = hex_md5(str);
        saltHash = shex+hash;
        bytearr = new short[20];
        convertHexString(saltHash, 20, 20, bytearr);
        
        System.out.println(convertToBase64(bytearr));
    }
}


The script awaits the first parameter to be "reconnect" (which is what you want it to do for jDownloader) and the second one to be your password. (First line in the jDownloader-option-tab for the script is "reconnect", second one your password)

Additionally, the it expects the java program to be named prepare_login.class and to be at ${HOME}/java/; the path can be changed by the parameter "-j <dir>" (one line in jDownloader-option-tab for the script is "-j", the next one the directory where you stored the class).
(To compile the java-file simply safe it as prepare_login.java and enter "java prepare_login" to your console)

I hope I am able to help all those who experienced the same annoyance with this router as I did
Feel free to use and spread these as long as you don't remove the "Written by"s

Last edited by drbits; 17.10.2010 at 09:09.
Reply With Quote