org.freedesktop.dbus.Transport.java Source code

Java tutorial

Introduction

Here is the source code for org.freedesktop.dbus.Transport.java

Source

/*
   D-Bus Java Implementation
   Copyright (c) 2005-2006 Matthew Johnson
    
   This program is free software; you can redistribute it and/or modify it
   under the terms of either the GNU Lesser General Public License Version 2 or the
   Academic Free Licence Version 2.1.
    
   Full licence texts are included in the COPYING file with this program.
 */
package org.freedesktop.dbus;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.Collator;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Random;
import java.util.Vector;

import org.apache.commons.codec.binary.Hex;
import org.apache.log4j.Logger;
import org.newsclub.net.unix.AFUNIXServerSocket;
import org.newsclub.net.unix.AFUNIXSocket;
import org.newsclub.net.unix.AFUNIXSocketAddress;
import org.newsclub.net.unix.SocketCredentials;

public class Transport {

    static final Logger log = Logger.getLogger(Transport.class);

    public static class SASL {

        public static class Command {

            private int command;
            private int mechs;
            private String data;
            private String response;

            public Command() {
            }

            public Command(String s) throws IOException {
                String[] ss = s.split(" ");

                if (log.isTraceEnabled()) {
                    log.trace("Creating command from: " + Arrays.toString(ss));
                }
                if (0 == col.compare(ss[0], "OK")) {
                    this.command = COMMAND_OK;
                    this.data = ss[1];
                } else if (0 == col.compare(ss[0], "AUTH")) {
                    this.command = COMMAND_AUTH;
                    if (ss.length > 1) {
                        if (0 == col.compare(ss[1], "EXTERNAL"))
                            this.mechs = AUTH_EXTERNAL;
                        else if (0 == col.compare(ss[1], "DBUS_COOKIE_SHA1"))
                            this.mechs = AUTH_SHA;
                        else if (0 == col.compare(ss[1], "ANONYMOUS"))
                            this.mechs = AUTH_ANON;
                    }
                    if (ss.length > 2)
                        this.data = ss[2];
                } else if (0 == col.compare(ss[0], "DATA")) {
                    this.command = COMMAND_DATA;
                    this.data = ss[1];
                } else if (0 == col.compare(ss[0], "REJECTED")) {
                    this.command = COMMAND_REJECTED;
                    for (int i = 1; i < ss.length; i++)
                        if (0 == col.compare(ss[i], "EXTERNAL"))
                            this.mechs |= AUTH_EXTERNAL;
                        else if (0 == col.compare(ss[i], "DBUS_COOKIE_SHA1"))
                            this.mechs |= AUTH_SHA;
                        else if (0 == col.compare(ss[i], "ANONYMOUS"))
                            this.mechs |= AUTH_ANON;
                } else if (0 == col.compare(ss[0], "BEGIN")) {
                    this.command = COMMAND_BEGIN;
                } else if (0 == col.compare(ss[0], "CANCEL")) {
                    this.command = COMMAND_CANCEL;
                } else if (0 == col.compare(ss[0], "ERROR")) {
                    this.command = COMMAND_ERROR;
                    this.data = ss[1];
                } else {
                    throw new IOException("Invalid Command " + ss[0]);
                }
                if (log.isTraceEnabled()) {
                    log.trace("Created command: " + this);
                }
            }

            public int getCommand() {
                return this.command;
            }

            public int getMechs() {
                return this.mechs;
            }

            public String getData() {
                return this.data;
            }

            public String getResponse() {
                return this.response;
            }

            public void setResponse(String s) {
                this.response = s;
            }

            @Override
            public String toString() {
                return "Command(" + this.command + ", " + this.mechs + ", " + this.data + ", " + null + ")";
            }
        }

        static Collator col = Collator.getInstance();
        static {
            col.setDecomposition(Collator.FULL_DECOMPOSITION);
            col.setStrength(Collator.PRIMARY);
        }
        public static final int LOCK_TIMEOUT = 1000;
        public static final int NEW_KEY_TIMEOUT_SECONDS = 60 * 5;
        public static final int EXPIRE_KEYS_TIMEOUT_SECONDS = NEW_KEY_TIMEOUT_SECONDS + (60 * 2);
        public static final int MAX_TIME_TRAVEL_SECONDS = 60 * 5;
        public static final int COOKIE_TIMEOUT = 240;
        public static final String COOKIE_CONTEXT = "org_freedesktop_java";

        private static String findCookie(String context, String ID) throws IOException {
            String homedir = System.getProperty("user.home");
            File f = new File(homedir + "/.dbus-keyrings/" + context);
            try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(f)))) {
                String s = null;
                String c = null;
                long now = System.currentTimeMillis() / 1000;
                while (null != (s = r.readLine())) {
                    String[] line = s.split(" ");
                    long timestamp = Long.parseLong(line[1]);
                    if (line[0].equals(ID) && (!(timestamp < 0 || (now + MAX_TIME_TRAVEL_SECONDS) < timestamp
                            || (now - EXPIRE_KEYS_TIMEOUT_SECONDS) > timestamp))) {
                        c = line[2];
                        break;
                    }
                }
                return c;
            }

        }

        private static void addCookie(String context, String ID, long timestamp, String c) throws IOException {
            String homedir = System.getProperty("user.home");
            File keydir = new File(homedir + "/.dbus-keyrings/");
            File cookiefile = new File(homedir + "/.dbus-keyrings/" + context);
            File lock = new File(homedir + "/.dbus-keyrings/" + context + ".lock");
            File temp = new File(homedir + "/.dbus-keyrings/" + context + ".temp");

            // ensure directory exists
            if (!keydir.exists())
                keydir.mkdirs();

            // acquire lock
            long start = System.currentTimeMillis();
            while (!lock.createNewFile() && LOCK_TIMEOUT > (System.currentTimeMillis() - start))
                ;

            // read old file
            Vector<String> lines = new Vector<>();
            if (cookiefile.exists()) {
                try (BufferedReader r = new BufferedReader(
                        new InputStreamReader(new FileInputStream(cookiefile)))) {
                    String s = null;
                    while (null != (s = r.readLine())) {
                        String[] line = s.split(" ");
                        long time = Long.parseLong(line[1]);
                        // expire stale cookies
                        if ((timestamp - time) < COOKIE_TIMEOUT)
                            lines.add(s);
                    }
                }
            }

            // add cookie
            lines.add(ID + " " + timestamp + " " + c);

            // write temp file
            try (PrintWriter w = new PrintWriter(new FileOutputStream(temp))) {
                for (String l : lines)
                    w.println(l);
            }

            // atomically move to old file
            if (!temp.renameTo(cookiefile)) {
                cookiefile.delete();
                temp.renameTo(cookiefile);
            }

            // remove lock
            lock.delete();
        }

        /**
         * Takes the string, encodes it as hex and then turns it into a string again.
         * No, I don't know why either.
         */
        private static String stupidlyEncode(String data) {
            return Hex.encodeHexString(data.getBytes());
        }

        private static String stupidlyEncode(byte[] data) {
            return Hex.encodeHexString(data);
        }

        private static byte getNibble(char c) {
            switch (c) {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                return (byte) (c - '0');
            case 'A':
            case 'B':
            case 'C':
            case 'D':
            case 'E':
            case 'F':
                return (byte) (c - 'A' + 10);
            case 'a':
            case 'b':
            case 'c':
            case 'd':
            case 'e':
            case 'f':
                return (byte) (c - 'a' + 10);
            default:
                return 0;
            }
        }

        private static String stupidlyDecode(String data) {
            char[] cs = new char[data.length()];
            char[] res = new char[cs.length / 2];
            data.getChars(0, data.length(), cs, 0);
            for (int i = 0, j = 0; j < res.length; i += 2, j++) {
                int b = 0;
                b |= getNibble(cs[i]) << 4;
                b |= getNibble(cs[i + 1]);
                res[j] = (char) b;
            }
            return new String(res);
        }

        public static final int MODE_SERVER = 1;
        public static final int MODE_CLIENT = 2;

        public static final int AUTH_NONE = 0;
        public static final int AUTH_EXTERNAL = 1;
        public static final int AUTH_SHA = 2;
        public static final int AUTH_ANON = 4;

        public static final int COMMAND_AUTH = 1;
        public static final int COMMAND_DATA = 2;
        public static final int COMMAND_REJECTED = 3;
        public static final int COMMAND_OK = 4;
        public static final int COMMAND_BEGIN = 5;
        public static final int COMMAND_CANCEL = 6;
        public static final int COMMAND_ERROR = 7;

        public static final int INITIAL_STATE = 0;
        public static final int WAIT_DATA = 1;
        public static final int WAIT_OK = 2;
        public static final int WAIT_REJECT = 3;
        public static final int WAIT_AUTH = 4;
        public static final int WAIT_BEGIN = 5;
        public static final int AUTHENTICATED = 6;
        public static final int FAILED = 7;

        public static final int OK = 1;
        public static final int CONTINUE = 2;
        public static final int ERROR = 3;
        public static final int REJECT = 4;

        public Command receive(InputStream s) throws IOException {
            StringBuffer sb = new StringBuffer();
            top: while (true) {
                int c = s.read();
                switch (c) {
                case -1:
                    throw new IOException("Stream unexpectedly short (broken pipe)");
                case 0:
                case '\r':
                    continue;
                case '\n':
                    break top;
                default:
                    sb.append((char) c);
                }
            }
            if (log.isTraceEnabled()) {
                log.trace("received: " + sb);
            }
            try {
                return new Command(sb.toString());
            } catch (Exception e) {
                log.warn("Failed to read command", e);
                return new Command();
            }
        }

        public void send(OutputStream out, int command, String... data) throws IOException {
            StringBuffer sb = new StringBuffer();
            switch (command) {
            case COMMAND_AUTH:
                sb.append("AUTH");
                break;
            case COMMAND_DATA:
                sb.append("DATA");
                break;
            case COMMAND_REJECTED:
                sb.append("REJECTED");
                break;
            case COMMAND_OK:
                sb.append("OK");
                break;
            case COMMAND_BEGIN:
                sb.append("BEGIN");
                break;
            case COMMAND_CANCEL:
                sb.append("CANCEL");
                break;
            case COMMAND_ERROR:
                sb.append("ERROR");
                break;
            default:
                return;
            }
            for (String s : data) {
                sb.append(' ');
                sb.append(s);
            }
            sb.append('\r');
            sb.append('\n');
            if (log.isTraceEnabled()) {
                log.trace("sending: " + sb);
            }
            out.write(sb.toString().getBytes());
        }

        public int do_challenge(int auth, Command c) throws IOException {
            switch (auth) {
            case AUTH_SHA:
                String[] reply = stupidlyDecode(c.getData()).split(" ");

                if (log.isTraceEnabled()) {
                    log.trace(Arrays.toString(reply));
                }
                if (3 != reply.length) {
                    if (log.isDebugEnabled()) {
                        log.debug("Reply is not length 3");
                    }
                    return ERROR;
                }
                String context = reply[0];
                String ID = reply[1];
                String serverchallenge = reply[2];
                MessageDigest md = null;
                try {
                    md = MessageDigest.getInstance("SHA");
                } catch (NoSuchAlgorithmException NSAe) {
                    log.warn("Failed to get message digest", NSAe);
                    return ERROR;
                }
                byte[] buf = new byte[8];
                Message.marshallintBig(System.currentTimeMillis(), buf, 0, 8);
                String clientchallenge = stupidlyEncode(md.digest(buf));
                md.reset();
                long start = System.currentTimeMillis();
                String challengeCookie = null;
                while (null == challengeCookie && (System.currentTimeMillis() - start) < LOCK_TIMEOUT)
                    challengeCookie = findCookie(context, ID);
                if (null == challengeCookie) {
                    if (log.isDebugEnabled()) {
                        log.debug("Did not find a cookie in context " + context + " with ID " + ID);
                    }
                    return ERROR;
                }
                String response = serverchallenge + ":" + clientchallenge + ":" + challengeCookie;
                buf = md.digest(response.getBytes());
                if (log.isTraceEnabled()) {
                    log.trace("Response: " + response + " hash: " + Hex.encodeHexString(buf));
                }
                response = stupidlyEncode(buf);
                c.setResponse(stupidlyEncode(clientchallenge + " " + response));
                return OK;
            default:
                if (log.isDebugEnabled()) {
                    log.debug("Not DBUS_COOKIE_SHA1 authtype.");
                }
                return ERROR;
            }
        }

        public String challenge = "";
        public String cookie = "";

        public int do_response(int auth, String Uid, String kernelUid, Command c) {
            MessageDigest md = null;
            try {
                md = MessageDigest.getInstance("SHA");
            } catch (NoSuchAlgorithmException NSAe) {
                log.warn("Failed to get message digest", NSAe);
                return ERROR;
            }
            switch (auth) {
            case AUTH_NONE:
                switch (c.getMechs()) {
                case AUTH_ANON:
                    return OK;
                case AUTH_EXTERNAL:
                    if (0 == col.compare(Uid, c.getData())
                            && (null == kernelUid || 0 == col.compare(Uid, kernelUid)))
                        return OK;

                    return ERROR;
                case AUTH_SHA:
                    String context = COOKIE_CONTEXT;
                    long id = System.currentTimeMillis();
                    byte[] buf = new byte[8];
                    Message.marshallintBig(id, buf, 0, 8);
                    this.challenge = stupidlyEncode(md.digest(buf));
                    Random r = new Random();
                    r.nextBytes(buf);
                    this.cookie = stupidlyEncode(md.digest(buf));
                    try {
                        addCookie(context, "" + id, id / 1000, this.cookie);
                    } catch (IOException IOe) {
                        log.warn("IOException during cookie auth", IOe);
                    }
                    if (log.isDebugEnabled()) {
                        log.debug("Sending challenge: " + context + ' ' + id + ' ' + this.challenge);
                    }
                    c.setResponse(stupidlyEncode(context + ' ' + id + ' ' + this.challenge));
                    return CONTINUE;
                default:
                    return ERROR;
                }
            case AUTH_SHA:
                String[] response = stupidlyDecode(c.getData()).split(" ");
                if (response.length < 2)
                    return ERROR;
                String cchal = response[0];
                String hash = response[1];
                String prehash = this.challenge + ":" + cchal + ":" + this.cookie;
                byte[] buf = md.digest(prehash.getBytes());
                String posthash = stupidlyEncode(buf);
                if (log.isDebugEnabled()) {
                    log.debug("Authenticating Hash; data=" + prehash + " remote hash=" + hash + " local hash="
                            + posthash);
                }
                if (0 == col.compare(posthash, hash))
                    return OK;

                return ERROR;
            default:
                return ERROR;
            }
        }

        public String[] getTypes(int types) {
            switch (types) {
            case AUTH_EXTERNAL:
                return new String[] { "EXTERNAL" };
            case AUTH_SHA:
                return new String[] { "DBUS_COOKIE_SHA1" };
            case AUTH_ANON:
                return new String[] { "ANONYMOUS" };
            case AUTH_SHA + AUTH_EXTERNAL:
                return new String[] { "EXTERNAL", "DBUS_COOKIE_SHA1" };
            case AUTH_SHA + AUTH_ANON:
                return new String[] { "ANONYMOUS", "DBUS_COOKIE_SHA1" };
            case AUTH_EXTERNAL + AUTH_ANON:
                return new String[] { "ANONYMOUS", "EXTERNAL" };
            case AUTH_EXTERNAL + AUTH_ANON + AUTH_SHA:
                return new String[] { "ANONYMOUS", "EXTERNAL", "DBUS_COOKIE_SHA1" };
            default:
                return new String[] {};
            }
        }

        /**
         * performs SASL auth on the given streams.
         * Mode selects whether to run as a SASL server or client.
         * Types is a bitmask of the available auth types.
         * Returns true if the auth was successful and false if it failed.
         */
        public boolean auth(int mode, int types, String guid, OutputStream out, InputStream in, AFUNIXSocket us)
                throws IOException {
            String username = System.getProperty("user.name");
            String Uid = null;
            String kernelUid = null;
            try {
                Class<?> c = Class.forName("com.sun.security.auth.module.UnixSystem");
                Method m = c.getMethod("getUid");
                Object o = c.newInstance();
                long uid = (Long) m.invoke(o);
                log.debug("Got UID " + uid);
                Uid = "" + uid;
            } catch (Exception e) {
                log.debug("Failed to get uid, fallback to user.name", e);
                Uid = username;
            }
            Uid = Hex.encodeHexString(Uid.getBytes("ASCII"));
            Command c;
            int failed = 0;
            int current = 0;
            int state = INITIAL_STATE;

            while (state != AUTHENTICATED && state != FAILED) {
                if (log.isTraceEnabled()) {
                    log.trace("AUTH state: " + state);
                }
                switch (mode) {
                case MODE_CLIENT:
                    switch (state) {
                    case INITIAL_STATE:
                        if (null == us)
                            out.write(new byte[] { 0 });
                        else {
                            log.debug("Sending credentials");
                            us.sendCredentials((byte) 0);
                        }
                        send(out, COMMAND_AUTH);
                        state = WAIT_DATA;
                        break;
                    case WAIT_DATA:
                        c = receive(in);
                        switch (c.getCommand()) {
                        case COMMAND_DATA:
                            switch (do_challenge(current, c)) {
                            case CONTINUE:
                                send(out, COMMAND_DATA, c.getResponse());
                                break;
                            case OK:
                                send(out, COMMAND_DATA, c.getResponse());
                                state = WAIT_OK;
                                break;
                            case ERROR:
                                send(out, COMMAND_ERROR, c.getResponse());
                                break;
                            }
                            break;
                        case COMMAND_REJECTED:
                            failed |= current;
                            int available = c.getMechs() & (~failed);
                            if (0 != (available & AUTH_EXTERNAL)) {
                                log.debug("Trying EXTERNAL with " + Uid);
                                send(out, COMMAND_AUTH, "EXTERNAL", Uid);
                                current = AUTH_EXTERNAL;
                            } else if (0 != (available & AUTH_SHA)) {
                                log.debug("Trying DBUS_COOKIE_SHA1 with " + Uid);
                                send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid);
                                current = AUTH_SHA;
                            } else if (0 != (available & AUTH_ANON)) {
                                log.debug("Trying ANONYMOUS");
                                send(out, COMMAND_AUTH, "ANONYMOUS");
                                current = AUTH_ANON;
                            } else
                                state = FAILED;
                            break;
                        case COMMAND_ERROR:
                            send(out, COMMAND_CANCEL);
                            state = WAIT_REJECT;
                            break;
                        case COMMAND_OK:
                            send(out, COMMAND_BEGIN);
                            state = AUTHENTICATED;
                            break;
                        default:
                            send(out, COMMAND_ERROR, "Got invalid command");
                            break;
                        }
                        break;
                    case WAIT_OK:
                        c = receive(in);
                        switch (c.getCommand()) {
                        case COMMAND_OK:
                            send(out, COMMAND_BEGIN);
                            state = AUTHENTICATED;
                            break;
                        case COMMAND_ERROR:
                        case COMMAND_DATA:
                            send(out, COMMAND_CANCEL);
                            state = WAIT_REJECT;
                            break;
                        case COMMAND_REJECTED:
                            failed |= current;
                            int available = c.getMechs() & (~failed);
                            state = WAIT_DATA;
                            if (0 != (available & AUTH_EXTERNAL)) {
                                send(out, COMMAND_AUTH, "EXTERNAL", Uid);
                                current = AUTH_EXTERNAL;
                            } else if (0 != (available & AUTH_SHA)) {
                                send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid);
                                current = AUTH_SHA;
                            } else if (0 != (available & AUTH_ANON)) {
                                send(out, COMMAND_AUTH, "ANONYMOUS");
                                current = AUTH_ANON;
                            } else
                                state = FAILED;
                            break;
                        default:
                            send(out, COMMAND_ERROR, "Got invalid command");
                            break;
                        }
                        break;
                    case WAIT_REJECT:
                        c = receive(in);
                        switch (c.getCommand()) {
                        case COMMAND_REJECTED:
                            failed |= current;
                            int available = c.getMechs() & (~failed);
                            if (0 != (available & AUTH_EXTERNAL)) {
                                send(out, COMMAND_AUTH, "EXTERNAL", Uid);
                                current = AUTH_EXTERNAL;
                            } else if (0 != (available & AUTH_SHA)) {
                                send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid);
                                current = AUTH_SHA;
                            } else if (0 != (available & AUTH_ANON)) {
                                send(out, COMMAND_AUTH, "ANONYMOUS");
                                current = AUTH_ANON;
                            } else
                                state = FAILED;
                            break;
                        default:
                            state = FAILED;
                            break;
                        }
                        break;
                    default:
                        state = FAILED;
                    }
                    break;
                case MODE_SERVER:
                    switch (state) {
                    case INITIAL_STATE:
                        byte[] buf = new byte[1];
                        if (null == us) {
                            in.read(buf);
                        } else {
                            SocketCredentials creds = us.recieveCredentials();
                            if (creds.getUid() >= 0)
                                kernelUid = stupidlyEncode("" + creds.getUid());
                        }
                        if (0 != buf[0])
                            state = FAILED;
                        else
                            state = WAIT_AUTH;
                        break;
                    case WAIT_AUTH:
                        c = receive(in);
                        switch (c.getCommand()) {
                        case COMMAND_AUTH:
                            if (null == c.getData()) {
                                send(out, COMMAND_REJECTED, getTypes(types));
                            } else {
                                switch (do_response(current, Uid, kernelUid, c)) {
                                case CONTINUE:
                                    send(out, COMMAND_DATA, c.getResponse());
                                    current = c.getMechs();
                                    state = WAIT_DATA;
                                    break;
                                case OK:
                                    send(out, COMMAND_OK, guid);
                                    state = WAIT_BEGIN;
                                    current = 0;
                                    break;
                                case REJECT:
                                    send(out, COMMAND_REJECTED, getTypes(types));
                                    current = 0;
                                    break;
                                }
                            }
                            break;
                        case COMMAND_ERROR:
                            send(out, COMMAND_REJECTED, getTypes(types));
                            break;
                        case COMMAND_BEGIN:
                            state = FAILED;
                            break;
                        default:
                            send(out, COMMAND_ERROR, "Got invalid command");
                            break;
                        }
                        break;
                    case WAIT_DATA:
                        c = receive(in);
                        switch (c.getCommand()) {
                        case COMMAND_DATA:
                            switch (do_response(current, Uid, kernelUid, c)) {
                            case CONTINUE:
                                send(out, COMMAND_DATA, c.getResponse());
                                state = WAIT_DATA;
                                break;
                            case OK:
                                send(out, COMMAND_OK, guid);
                                state = WAIT_BEGIN;
                                current = 0;
                                break;
                            case REJECT:
                                send(out, COMMAND_REJECTED, getTypes(types));
                                current = 0;
                                break;
                            }
                            break;
                        case COMMAND_ERROR:
                        case COMMAND_CANCEL:
                            send(out, COMMAND_REJECTED, getTypes(types));
                            state = WAIT_AUTH;
                            break;
                        case COMMAND_BEGIN:
                            state = FAILED;
                            break;
                        default:
                            send(out, COMMAND_ERROR, "Got invalid command");
                            break;
                        }
                        break;
                    case WAIT_BEGIN:
                        c = receive(in);
                        switch (c.getCommand()) {
                        case COMMAND_ERROR:
                        case COMMAND_CANCEL:
                            send(out, COMMAND_REJECTED, getTypes(types));
                            state = WAIT_AUTH;
                            break;
                        case COMMAND_BEGIN:
                            state = AUTHENTICATED;
                            break;
                        default:
                            send(out, COMMAND_ERROR, "Got invalid command");
                            break;
                        }
                        break;
                    default:
                        state = FAILED;
                    }
                    break;
                default:
                    return false;
                }
            }

            return state == AUTHENTICATED;
        }
    }

    public MessageReader min;
    public MessageWriter mout;

    public Transport() {
    }

    public static String genGUID() {
        Random r = new Random();
        byte[] buf = new byte[16];
        r.nextBytes(buf);
        return Hex.encodeHexString(buf);
    }

    public Transport(BusAddress address) throws IOException {
        connect(address);
    }

    public Transport(String address) throws IOException, ParseException {
        connect(new BusAddress(address));
    }

    public Transport(String address, int timeout) throws IOException, ParseException {
        connect(new BusAddress(address), timeout);
    }

    public void connect(String address) throws IOException, ParseException {
        connect(new BusAddress(address), 0);
    }

    public void connect(String address, int timeout) throws IOException, ParseException {
        connect(new BusAddress(address), timeout);
    }

    public void connect(BusAddress address) throws IOException {
        connect(address, 0);
    }

    @SuppressWarnings("resource")
    public void connect(BusAddress address, int timeout) throws IOException {
        log.info("Connecting to " + address);
        OutputStream out = null;
        InputStream in = null;
        AFUNIXSocket us = null;
        Socket s = null;
        int mode = 0;
        int types = 0;
        if ("unix".equals(address.getType())) {
            types = SASL.AUTH_EXTERNAL;
            File sockFile = null;
            boolean abstractSock = false;
            if (null != address.getParameter("abstract")) {
                sockFile = new File(address.getParameter("abstract"));
                abstractSock = true;
            } else if (null != address.getParameter("path")) {
                sockFile = new File(address.getParameter("path"));
            }

            if (null != address.getParameter("listen")) {
                mode = SASL.MODE_SERVER;
                AFUNIXServerSocket uss = AFUNIXServerSocket.newInstance();
                uss.bind(new AFUNIXSocketAddress(sockFile, abstractSock));
                uss.setPassCred(true);
                s = uss.accept();
            } else {
                mode = SASL.MODE_CLIENT;
                us = AFUNIXSocket.newInstance();

                us.connect(new AFUNIXSocketAddress(sockFile, abstractSock));
                us.setPassCred(true);
                s = us;
            }

        } else if ("tcp".equals(address.getType())) {
            types = SASL.AUTH_SHA;
            if (null != address.getParameter("listen")) {
                mode = SASL.MODE_SERVER;
                ServerSocket ss = new ServerSocket();
                ss.bind(new InetSocketAddress(address.getParameter("host"),
                        Integer.parseInt(address.getParameter("port"))));
                s = ss.accept();
            } else {
                mode = SASL.MODE_CLIENT;
                s = new Socket();
                s.connect(new InetSocketAddress(address.getParameter("host"),
                        Integer.parseInt(address.getParameter("port"))));
            }
        } else {
            throw new IOException("unknown address type " + address.getType());
        }

        in = s.getInputStream();
        out = s.getOutputStream();

        if (!(new SASL()).auth(mode, types, address.getParameter("guid"), out, in, us)) {
            out.close();
            in.close();
            s.close();
            throw new IOException("Failed to auth");
        }

        if (log.isTraceEnabled()) {
            log.trace("Setting timeout to " + timeout + " on Socket");
        }
        s.setSoTimeout(timeout);

        this.mout = new MessageWriter(out);
        this.min = new MessageReader(in);

        log.info("Connection open");
    }

    public void disconnect() throws IOException {
        log.info("Disconnecting Transport");
        this.min.close();
        this.mout.close();
    }
}