def connect(self, blocking=1):
        self.fd.connect(self.path)
        self.fd.setblocking(blocking)

        m = Buffer()
        m.put_int(MUX_MSG_HELLO)
        m.put_int(SSHMUX_VER)
        if self._write_packet(m) is not None:
            return (False, 'Can\'t send HELLO message')

        m = self._read_packet()

        mux_msg = m.get_int()
        if mux_msg != MUX_MSG_HELLO:
            return (False, 'Expected HELLO reply, got %u' % (mux_msg,))

        mux_ver = m.get_int()
        if mux_ver != SSHMUX_VER:
            return (False, 'Unsupported multiplexing protocol version %d (expected %d)' % (mux_ver, SSHMUX_VER))

        extensions = {}
        while len(m):
            name = m.get_str()
            value = m.get_str()
            extensions[name] = value
        return (True, extensions)
    def info(self, fmt):
        m = Buffer()
        m.put_int(MUX_C_INFO)
        rid = self.rid
        self.rid += 1
        m.put_int(rid)
        m.put_str(fmt)

        if self._write_packet(m) is not None:
            return (False, 'Can\'t send INFO request')

        m = self._read_packet()

        rep_msg = m.get_int()

        rep_rid = m.get_int()
        if rep_rid != rid:
            return (False, 'Got unexpected request id %u, expected %u' % (rep_rid, rid))

        if rep_msg != MUX_S_RESULT:
            rep_reason = m.get_str()
            if rep_msg == MUX_S_FAILURE:
                return (False, rep_reason)
            return (False, 'Expected INFO reply, got %x' % (rep_msg,))

        rep_str = m.get_str()

        return (True, rep_str)
    def stop(self):
        m = Buffer()
        m.put_int(MUX_C_STOP_LISTENING)
        rid = self.rid
        self.rid += 1
        m.put_int(rid)

        if self._write_packet(m) is not None:
            return (False, 'Can\'t send STOP-LISTENING request')

        m = self._read_packet()

        rep_msg = m.get_int()

        rep_rid = m.get_int()
        if rep_rid != rid:
            return (False, 'Got unexpected request id %u, expected %u' % (rep_rid, rid))

        if rep_msg != MUX_S_OK:
            rep_reason = m.get_str()
            if rep_msg == MUX_S_FAILURE:
                return (False, 'Failure in STOP message: %s' % (rep_reason,))
            elif rep_msg == MUX_S_PERMISSION_DENIED:
                return (False, 'Permission denied for STOP message: %s' % (rep_reason,))
            return (False, 'Unexpected server reply, got %u' % (rep_msg,))

        return (True, None)
    def forwards(self):
        m = Buffer()
        m.put_int(MUX_C_LIST_FWDS)
        rid = self.rid
        self.rid += 1
        m.put_int(rid)

        if self._write_packet(m) is not None:
            return (False, 'Can\'t send LIST-FORWARDS request')

        m = self._read_packet()

        rep_msg = m.get_int()

        rep_rid = m.get_int()
        if rep_rid != rid:
            return (False, 'Got unexpected request id %u, expected %u' % (rep_rid, rid))

        if rep_msg != MUX_S_RESULT:
            rep_reason = m.get_str()
            if rep_msg == MUX_S_FAILURE:
                return (False, 'Failure in LIST-FORWARDS request: %s' % (rep_reason,))
            elif rep_msg == MUX_S_PERMISSION_DENIED:
                return (False, 'Permission denied for LIST-FORWARDS request: %s' % (rep_reason,))
            return (False, 'Unexpected server reply, got %u' % (rep_msg,))

        fwds = []
        while len(m):
            fid = m.get_int()
            ftype = m.get_int()
            listen_host = m.get_str()
            listen_port = m.get_int()
            connect_host = m.get_str()
            connect_port = m.get_int()

            if ftype == MUX_FWD_REMOTE and listen_port == 0:
                listen_port = m.get_int()

            fwds.append((fid, _fwd_types[ftype], listen_host, listen_port, connect_host, connect_port))
        return (True, fwds)
    def sessions(self):
        m = Buffer()
        m.put_int(MUX_C_LIST_SESSIONS)
        rid = self.rid
        self.rid += 1
        m.put_int(rid)

        if self._write_packet(m) is not None:
            return (False, 'Can\'t send LIST-SESSIONS request')

        m = self._read_packet()

        rep_msg = m.get_int()

        rep_rid = m.get_int()
        if rep_rid != rid:
            return (False, 'Got unexpected request id %u, expected %u' % (rep_rid, rid))

        if rep_msg != MUX_S_RESULT:
            rep_reason = m.get_str()
            if rep_msg == MUX_S_FAILURE:
                return (False, 'Failure in LIST-SESSIONS request: %s' % (rep_reason,))
            elif rep_msg == MUX_S_PERMISSION_DENIED:
                return (False, 'Permission denied for LIST-SESSIONS request: %s' % (rep_reason,))
            return (False, 'Unexpected server reply, got %u' % (rep_msg,))

        sessions = []
        while len(m):
            sid = m.get_int();
            stype = m.get_int();
            rid = m.get_int();
            cid = m.get_int();
            tname = m.get_str();
            rname = m.get_str();

            sessions.append((sid, stype, rid, cid, tname, rname))

        return (True, sessions)
    def forward(self, mode, ftype, listen_host, listen_port, connect_host, connect_port):
        m = Buffer()
        if mode:
            n = 'OPEN'
            m.put_int(MUX_C_OPEN_FWD)
        else:
            n = 'CLOSE'
            m.put_int(MUX_C_CLOSE_FWD)
        rid = self.rid
        self.rid += 1
        m.put_int(rid)

        if isinstance(ftype, basestring):
            assert ftype in _fwd_names, 'Invalid forward type %s' % (ftype,)
            ftype = _fwd_names[ftype]
        m.put_int(ftype)
        m.put_str(listen_host)
        m.put_int(listen_port)
        m.put_str(connect_host)
        m.put_int(connect_port)

        if self._write_packet(m) is not None:
            return (False, 'Can\'t send %s-FORWARD request' % (n,))

        m = self._read_packet()

        rep_msg = m.get_int()

        rep_rid = m.get_int()
        if rep_rid != rid:
            return (False, 'Got unexpected request id %u, expected %u' % (rep_rid, rid))

        if rep_msg != MUX_S_OK and rep_msg != MUX_S_REMOTE_PORT:
            rep_reason = m.get_str()
            if rep_msg == MUX_S_FAILURE:
                return (False, 'Failure in %s-FORWARD request: %s' % (n, rep_reason,))
            elif rep_msg == MUX_S_PERMISSION_DENIED:
                return (False, 'Permission denied for %s-FORWARD request: %s' % (n, rep_reason,))
            return (False, 'Unexpected server reply, got %u' % (rep_msg,))

        if ftype == MUX_FWD_REMOTE and listen_port == 0:
            if rep_msg != MUX_S_REMOTE_PORT:
                return (False, 'Expected remote port reply, got %u' % (rep_msg,))
            rep_port = m.get_int()
            return (True, rep_port)

        return (True, None)