Problema de apagado del túnel Paramiko SSH

Estoy trabajando en un script de Python para consultar algunas bases de datos remotas a través de un túnel ssh establecido de vez en cuando. Estoy bastante familiarizado con la biblioteca paramiko, así que esa fue mi elección de ruta. Prefiero mantener esto en python completo para poder usar paramiko para tratar problemas clave, así como también usar python para iniciar, controlar y apagar los túneles ssh.

Ha habido algunas preguntas relacionadas por aquí sobre este tema, pero la mayoría de ellas parecían incompletas en las respuestas. Mi solución a continuación es un hackeado de las soluciones que he encontrado hasta ahora.

Ahora para el problema: soy capaz de crear el primer túnel con bastante facilidad (en un hilo separado) y hacer mis cosas de DB / python, pero cuando intento cerrar el túnel, el host local no liberará el puerto local al que me uní. A continuación, he incluido mi fuente y los datos relevantes de netstat en cada paso del proceso.

#!/usr/bin/python

import select
import SocketServer
import sys
import paramiko
from threading import Thread
import time



class ForwardServer(SocketServer.ThreadingTCPServer):
    daemon_threads = True
    allow_reuse_address = True

class Handler (SocketServer.BaseRequestHandler):
    def handle(self):
        try:
            chan = self.ssh_transport.open_channel('direct-tcpip', (self.chain_host, self.chain_port), self.request.getpeername())
        except Exception, e:
            print('Incoming request to %s:%d failed: %s' % (self.chain_host, self.chain_port, repr(e)))
            return
        if chan is None:
            print('Incoming request to %s:%d was rejected by the SSH server.' % (self.chain_host, self.chain_port))
            return
        print('Connected!  Tunnel open %r -> %r -> %r' % (self.request.getpeername(), chan.getpeername(), (self.chain_host, self.chain_port)))
        while True:
            r, w, x = select.select([self.request, chan], [], [])
            if self.request in r:
                data = self.request.recv(1024)
                if len(data) == 0:
                    break
                chan.send(data)
            if chan in r:
                data = chan.recv(1024)
                if len(data) == 0:
                    break
                self.request.send(data)
        chan.close()
        self.request.close()
        print('Tunnel closed from %r' % (self.request.getpeername(),))

class DBTunnel():

    def __init__(self,ip):
        self.c = paramiko.SSHClient()
        self.c.load_system_host_keys()
        self.c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.c.connect(ip, username='someuser')
        self.trans = self.c.get_transport()

    def startTunnel(self):
        class SubHandler(Handler):
            chain_host = '127.0.0.1'
            chain_port = 5432
            ssh_transport = self.c.get_transport()
        def ThreadTunnel():
            global t
            t = ForwardServer(('', 3333), SubHandler)
            t.serve_forever()
        Thread(target=ThreadTunnel).start()

    def stopTunnel(self):
        t.shutdown()
        self.trans.close()
        self.c.close()

Aunque terminaré usando un método de tipo stopTunnel (), me doy cuenta de que el código no es del todo correcto, sino más bien una experimentación de tratar de cerrar el túnel correctamente y probar mis resultados.

Cuando llamo por primera vez, creo el objeto DBTunnel y llamo startTunnel (), netstat produce lo siguiente:

tcp4       0      0 *.3333                 *.*                    LISTEN
tcp4       0      0 MYIP.36316      REMOTE_HOST.22                ESTABLISHED
tcp4       0      0 127.0.0.1.5432         *.*                    LISTEN

Una vez que llamo a stopTunnel (), o incluso elimino el objeto DBTunnel en sí ... Me quedo con esta conexión hasta que salga de Python todos juntos, y lo que supongo que es el recolector de basura se encarga de ello:

tcp4       0      0 *.3333                 *.*                    LISTEN

Sería bueno saber por qué este socket abierto se cuelga independientemente del objeto DBConnect y cómo cerrarlo correctamente desde mi script. Si intento vincular una conexión diferente a una IP diferente usando el mismo puerto local antes de salir completamente de python (time_wait no es el problema), entonces obtengo la infame dirección de error err 48 en uso. Gracias por adelantado :)

Respuestas a la pregunta(3)

Su respuesta a la pregunta