private void parseRouterDescriptor(String rd)
            throws TorException {
        this.routerDescriptor = rd;

        // Router item: nickname, hostname, onion-router-port, socks-port, dir-port
        Matcher m = ROUTER_PATTERN.matcher(rd);
        m.find();

        
        this.nickname = m.group(1);

        this.hostname = m.group(2);
        this.orPort = Integer.parseInt(m.group(3));
        this.socksPort = Integer.parseInt(m.group(4));
        this.dirPort = Integer.parseInt(m.group(5));

        // secondary information
        platform = Parsing.parseStringByRE(rd, PLATFORM_PATTERN, "unknown");
        published = Util.parseUtcTimestamp(Parsing.parseStringByRE(rd, PUBLISHED_PATTERN, ""));
        validUntil = new Date(published.getTime()+TorConfig.ROUTER_DESCRIPTION_VALID_PERIOD_MS);
        uptime = Integer.parseInt(Parsing.parseStringByRE(rd, UPTIME_PATTERN, "0"));
        try {
            fingerprint = new FingerprintImpl(Encoding.parseHex(Parsing.parseStringByRE(rd, FINGERPRINT_PATTERN, "")));
        } catch (Exception e) {
                throw new TorException("Server " + nickname + " skipped as router");
        }
        contact = Parsing.parseStringByRE(rd, CONTACT_PATTERN, "");

        // make that IF description is from a trusted server, that fingerprint is correct
        /* not needed:
                if (tor.config.trustedServers.containsKey(nickname)) {
                    String fingerprintFromConfig = (String) (tor.config.trustedServers.get(nickname)).get("fingerprint");
                    if (!fingerprint.getHex().equalsIgnoreCase(fingerprintFromConfig))
                        throw new TorException("Server " + nickname + " is trusted, but fingerprint check failed");
                }
        */
        
        // bandwidth
        m = ROUTER_PATTERN2.matcher(rd);
        if (m.find()) {
            bandwidthAvg = Integer.parseInt(m.group(1));
            bandwidthBurst = Integer.parseInt(m.group(2));
            bandwidthObserved = Integer.parseInt(m.group(3));
        }

        // onion key
        String stringOnionKey = Parsing.parseStringByRE(rd, ONIONKEY_PATTERN, "");
        onionKey = Encryption.extractPublicRSAKey(stringOnionKey);

        // signing key
        String stringSigningKey = Parsing.parseStringByRE(rd, SIGNINGKEY_PATTERN, "");
        signingKey = Encryption.extractPublicRSAKey(stringSigningKey);

        // verify signing-key against fingerprint
        try {
            byte[] pkcs = Encryption.getPKCS1EncodingFromRSAPublicKey(signingKey);
            byte[] keyHash = Encryption.getDigest(pkcs);
            if (!new FingerprintImpl(keyHash).equals(fingerprint)) {
                throw new TorException("Server " + nickname + " doesn't verify signature vs fingerprint");
            }
        } catch (TorException e) {
            throw e;
        } catch (Exception e) {
            throw new TorException("Server " + nickname
                    + " doesn't verify signature vs fingerprint");
        }

        // parse family
        String stringFamily = Parsing.parseStringByRE(rd, STRINGFAMILY_PATTERN, "");
        if (stringFamily.length()==0) {
            stringFamily = Parsing.parseStringByRE(rd, STRINGOPTFAMILY_PATTERN_PATTERN, "");
        }
        Matcher mFamily = PFAMILY_PATTERN.matcher(stringFamily);
        while (mFamily.find()) {
            String host = mFamily.group(1);
            family.add(host);
        }

        // check the validity of the signature    
        routerSignature = Encoding.parseBase64(Parsing.parseStringByRE(rd, ROUTERSIGNATURE_PATTERN, ""));
        byte[] sha1Input = (Parsing.parseStringByRE(rd, SHA1INPUT_PATTERN, "")).getBytes();
        if (!Encryption.verifySignature(routerSignature, signingKey, sha1Input)) {
            log.info("Server -> router-signature check failed for " + nickname);
            throw new TorException("Server " + nickname + ": description signature verification failed");
        }

        // exit policy
        exitpolicy = parseExitPolicy(rd);
        // usually in directory the hostname is already set to the IP
        // so, following resolve just converts it to the InetAddress
        try {
            address = InetAddress.getByName(hostname); 
        } catch (UnknownHostException e) {
            throw new TorException("Server.ParseRouterDescriptor: Unresolvable hostname " + hostname);
        }
    }