| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
""" |
|---|
| 7 |
This module contains the implementation of the TCP forwarding, which allows |
|---|
| 8 |
clients and servers to forward arbitrary TCP data across the connection. |
|---|
| 9 |
|
|---|
| 10 |
Maintainer: Paul Swartz |
|---|
| 11 |
""" |
|---|
| 12 |
|
|---|
| 13 |
import struct |
|---|
| 14 |
|
|---|
| 15 |
from twisted.internet import protocol, reactor |
|---|
| 16 |
from twisted.python import log |
|---|
| 17 |
|
|---|
| 18 |
import common, channel |
|---|
| 19 |
|
|---|
| 20 |
class SSHListenForwardingFactory(protocol.Factory): |
|---|
| 21 |
def __init__(self, connection, hostport, klass): |
|---|
| 22 |
self.conn = connection |
|---|
| 23 |
self.hostport = hostport |
|---|
| 24 |
self.klass = klass |
|---|
| 25 |
|
|---|
| 26 |
def buildProtocol(self, addr): |
|---|
| 27 |
channel = self.klass(conn = self.conn) |
|---|
| 28 |
client = SSHForwardingClient(channel) |
|---|
| 29 |
channel.client = client |
|---|
| 30 |
addrTuple = (addr.host, addr.port) |
|---|
| 31 |
channelOpenData = packOpen_direct_tcpip(self.hostport, addrTuple) |
|---|
| 32 |
self.conn.openChannel(channel, channelOpenData) |
|---|
| 33 |
return client |
|---|
| 34 |
|
|---|
| 35 |
class SSHListenForwardingChannel(channel.SSHChannel): |
|---|
| 36 |
|
|---|
| 37 |
def channelOpen(self, specificData): |
|---|
| 38 |
log.msg('opened forwarding channel %s' % self.id) |
|---|
| 39 |
if len(self.client.buf)>1: |
|---|
| 40 |
b = self.client.buf[1:] |
|---|
| 41 |
self.write(b) |
|---|
| 42 |
self.client.buf = '' |
|---|
| 43 |
|
|---|
| 44 |
def openFailed(self, reason): |
|---|
| 45 |
self.closed() |
|---|
| 46 |
|
|---|
| 47 |
def dataReceived(self, data): |
|---|
| 48 |
self.client.transport.write(data) |
|---|
| 49 |
|
|---|
| 50 |
def eofReceived(self): |
|---|
| 51 |
self.client.transport.loseConnection() |
|---|
| 52 |
|
|---|
| 53 |
def closed(self): |
|---|
| 54 |
if hasattr(self, 'client'): |
|---|
| 55 |
log.msg('closing local forwarding channel %s' % self.id) |
|---|
| 56 |
self.client.transport.loseConnection() |
|---|
| 57 |
del self.client |
|---|
| 58 |
|
|---|
| 59 |
class SSHListenClientForwardingChannel(SSHListenForwardingChannel): |
|---|
| 60 |
|
|---|
| 61 |
name = 'direct-tcpip' |
|---|
| 62 |
|
|---|
| 63 |
class SSHListenServerForwardingChannel(SSHListenForwardingChannel): |
|---|
| 64 |
|
|---|
| 65 |
name = 'forwarded-tcpip' |
|---|
| 66 |
|
|---|
| 67 |
class SSHConnectForwardingChannel(channel.SSHChannel): |
|---|
| 68 |
|
|---|
| 69 |
def __init__(self, hostport, *args, **kw): |
|---|
| 70 |
channel.SSHChannel.__init__(self, *args, **kw) |
|---|
| 71 |
self.hostport = hostport |
|---|
| 72 |
self.client = None |
|---|
| 73 |
self.clientBuf = '' |
|---|
| 74 |
|
|---|
| 75 |
def channelOpen(self, specificData): |
|---|
| 76 |
cc = protocol.ClientCreator(reactor, SSHForwardingClient, self) |
|---|
| 77 |
log.msg("connecting to %s:%i" % self.hostport) |
|---|
| 78 |
cc.connectTCP(*self.hostport).addCallbacks(self._setClient, self._close) |
|---|
| 79 |
|
|---|
| 80 |
def _setClient(self, client): |
|---|
| 81 |
self.client = client |
|---|
| 82 |
log.msg("connected to %s:%i" % self.hostport) |
|---|
| 83 |
if self.clientBuf: |
|---|
| 84 |
self.client.transport.write(self.clientBuf) |
|---|
| 85 |
self.clientBuf = None |
|---|
| 86 |
if self.client.buf[1:]: |
|---|
| 87 |
self.write(self.client.buf[1:]) |
|---|
| 88 |
self.client.buf = '' |
|---|
| 89 |
|
|---|
| 90 |
def _close(self, reason): |
|---|
| 91 |
log.msg("failed to connect: %s" % reason) |
|---|
| 92 |
self.loseConnection() |
|---|
| 93 |
|
|---|
| 94 |
def dataReceived(self, data): |
|---|
| 95 |
if self.client: |
|---|
| 96 |
self.client.transport.write(data) |
|---|
| 97 |
else: |
|---|
| 98 |
self.clientBuf += data |
|---|
| 99 |
|
|---|
| 100 |
def closed(self): |
|---|
| 101 |
if self.client: |
|---|
| 102 |
log.msg('closed remote forwarding channel %s' % self.id) |
|---|
| 103 |
if self.client.channel: |
|---|
| 104 |
self.loseConnection() |
|---|
| 105 |
self.client.transport.loseConnection() |
|---|
| 106 |
del self.client |
|---|
| 107 |
|
|---|
| 108 |
def openConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avatar): |
|---|
| 109 |
remoteHP, origHP = unpackOpen_direct_tcpip(data) |
|---|
| 110 |
return SSHConnectForwardingChannel(remoteHP, |
|---|
| 111 |
remoteWindow=remoteWindow, |
|---|
| 112 |
remoteMaxPacket=remoteMaxPacket, |
|---|
| 113 |
avatar=avatar) |
|---|
| 114 |
|
|---|
| 115 |
class SSHForwardingClient(protocol.Protocol): |
|---|
| 116 |
|
|---|
| 117 |
def __init__(self, channel): |
|---|
| 118 |
self.channel = channel |
|---|
| 119 |
self.buf = '\000' |
|---|
| 120 |
|
|---|
| 121 |
def dataReceived(self, data): |
|---|
| 122 |
if self.buf: |
|---|
| 123 |
self.buf += data |
|---|
| 124 |
else: |
|---|
| 125 |
self.channel.write(data) |
|---|
| 126 |
|
|---|
| 127 |
def connectionLost(self, reason): |
|---|
| 128 |
if self.channel: |
|---|
| 129 |
self.channel.loseConnection() |
|---|
| 130 |
self.channel = None |
|---|
| 131 |
|
|---|
| 132 |
|
|---|
| 133 |
def packOpen_direct_tcpip((connHost, connPort), (origHost, origPort)): |
|---|
| 134 |
"""Pack the data suitable for sending in a CHANNEL_OPEN packet. |
|---|
| 135 |
""" |
|---|
| 136 |
conn = common.NS(connHost) + struct.pack('>L', connPort) |
|---|
| 137 |
orig = common.NS(origHost) + struct.pack('>L', origPort) |
|---|
| 138 |
return conn + orig |
|---|
| 139 |
|
|---|
| 140 |
packOpen_forwarded_tcpip = packOpen_direct_tcpip |
|---|
| 141 |
|
|---|
| 142 |
def unpackOpen_direct_tcpip(data): |
|---|
| 143 |
"""Unpack the data to a usable format. |
|---|
| 144 |
""" |
|---|
| 145 |
connHost, rest = common.getNS(data) |
|---|
| 146 |
connPort = int(struct.unpack('>L', rest[:4])[0]) |
|---|
| 147 |
origHost, rest = common.getNS(rest[4:]) |
|---|
| 148 |
origPort = int(struct.unpack('>L', rest[:4])[0]) |
|---|
| 149 |
return (connHost, connPort), (origHost, origPort) |
|---|
| 150 |
|
|---|
| 151 |
unpackOpen_forwarded_tcpip = unpackOpen_direct_tcpip |
|---|
| 152 |
|
|---|
| 153 |
def packGlobal_tcpip_forward((host, port)): |
|---|
| 154 |
return common.NS(host) + struct.pack('>L', port) |
|---|
| 155 |
|
|---|
| 156 |
def unpackGlobal_tcpip_forward(data): |
|---|
| 157 |
host, rest = common.getNS(data) |
|---|
| 158 |
port = int(struct.unpack('>L', rest[:4])[0]) |
|---|
| 159 |
return host, port |
|---|
| 160 |
|
|---|
| 161 |
"""This is how the data -> eof -> close stuff /should/ work. |
|---|
| 162 |
|
|---|
| 163 |
debug3: channel 1: waiting for connection |
|---|
| 164 |
debug1: channel 1: connected |
|---|
| 165 |
debug1: channel 1: read<=0 rfd 7 len 0 |
|---|
| 166 |
debug1: channel 1: read failed |
|---|
| 167 |
debug1: channel 1: close_read |
|---|
| 168 |
debug1: channel 1: input open -> drain |
|---|
| 169 |
debug1: channel 1: ibuf empty |
|---|
| 170 |
debug1: channel 1: send eof |
|---|
| 171 |
debug1: channel 1: input drain -> closed |
|---|
| 172 |
debug1: channel 1: rcvd eof |
|---|
| 173 |
debug1: channel 1: output open -> drain |
|---|
| 174 |
debug1: channel 1: obuf empty |
|---|
| 175 |
debug1: channel 1: close_write |
|---|
| 176 |
debug1: channel 1: output drain -> closed |
|---|
| 177 |
debug1: channel 1: rcvd close |
|---|
| 178 |
debug3: channel 1: will not send data after close |
|---|
| 179 |
debug1: channel 1: send close |
|---|
| 180 |
debug1: channel 1: is dead |
|---|
| 181 |
""" |
|---|