View difference between Paste ID: zayqgZW8 and z3Ygc3jx
SHOW: | | - or go back to the newest paste.
1
#!/usr/bin/python
2
#
3
# This is the relay script mentioned in http://blog.zorinaq.com/?e=81
4
#
5
# Listens on the address and port specified by --local-ip and --local-port, and
6
# relay all connections to the endpoint specified by --remote-hosts and
7
# --remote-port. Multiple remote hosts can be specified: one will be selected
8
# randomly for each connection.
9
#
10
# Optionally, if --mode 1:<secret> is specified, insert the secret key as the
11
# first bytes of data transmitted through each relayed connection, and if
12
# --mode 2:<secret> is specified, verify and remove the secret key (ignore
13
# the connection by discarding all data if the key does not match).
14
#
15
# I recommend a long hex string for the secret, for example:
16
# $ secret=`ps aux | md5sum | cut -c 1-32`
17
# $ ./tcprelay-secret-exp.py [...] -m 1:"$secret"
18
19
#import asyncake
20
import asyncore
21
import socket, random, struct
22
import re
23
24
class forwarder(asyncore.dispatcher):
25
    def __init__(self, ip, port, remoteip, remoteport, mode, secret, backlog=600):
26
        asyncore.dispatcher.__init__(self)
27
        self.remoteip=remoteip
28
        self.remoteport=remoteport
29
        self.mode=mode
30
        self.secret=secret
31
        self.create_socket(socket.AF_INET,socket.SOCK_STREAM)
32
        self.set_reuse_addr()
33
        self.bind((ip,port))
34
        self.listen(backlog)
35
36
    def handle_accept(self):
37
        conn, addr = self.accept()
38
        # print '--- Connect --- '
39
        sender(receiver(conn, addr[0], self.mode, self.secret),self.remoteip,self.remoteport, addr[0])
40
41
class receiver(asyncore.dispatcher):
42
    def __init__(self, conn, client_ip, mode, secret):
43
        asyncore.dispatcher.__init__(self,conn)
44
        self.mode=mode
45
        self.secret=secret
46
        self.from_remote_buffer=''
47
        self.to_remote_buffer=''
48
        self.sender=None
49
        self.client_ip = client_ip
50
        self.zero_bytes_forwarded = True
51
        # for framing
52
        self.look_for_type = True
53
        self.field_type = None
54
        self.look_for_len_byte_nr = None
55
        self.field_len = None
56
        self.bytes_left_to_extract = None
57
58
    def handle_connect(self):
59
        pass
60
61
    def detect_and_remove_framing(self, read):
62
        processed_read = ''
63
        for b in read:
64
            if self.look_for_type:
65
                self.field_type = b
66
                self.look_for_type = False
67
                self.look_for_len_byte_nr = 0
68
                self.field_len = 0
69
            elif self.look_for_len_byte_nr != None:
70
                self.field_len <<= 8
71
                self.field_len += ord(b)
72
                self.look_for_len_byte_nr += 1
73
                if self.look_for_len_byte_nr >= 2:
74
                    self.look_for_len_byte_nr = None
75
                    self.bytes_left_to_extract = self.field_len
76
            elif self.bytes_left_to_extract != None:
77
                if self.field_type == 'd':
78
                    processed_read += b
79
                else:
80
                    pass
81
                self.bytes_left_to_extract -= 1
82
                if self.bytes_left_to_extract == 0:
83
                    self.bytes_left_to_extract = None
84
                    self.look_for_type = True
85
        return processed_read
86
87
    def handle_read(self):
88
        """Read from TCP client."""
89
        read = self.recv(4096)
90
        if self.mode == '1': # insert the secret key
91
            # Implement simple framing ('d' for data packets, 'p' for padding packets)
92
            read = 'd' + struct.pack('>h', len(read)) + read
93
            rlen = 0
94
            padding = 1
95
            if padding == 0: # no padding
96
                rlen = 0
97
            elif padding == 1 and len(read) < 1500: # padding
98
                if len(read) < 1000:
99
                    rlen = random.randint(1000 - len(read), 1500 - len(read))
100
                else:
101
                    rlen = random.randint(0, 1500 - len(read))
102
            if rlen:
103
                read += 'p' + struct.pack('>h', rlen) + ('_' * rlen)
104
            if self.zero_bytes_forwarded:
105
                read = self.secret + read
106
                self.zero_bytes_forwarded = False
107
        elif self.mode == '2': # verify and remove the secret key
108
            if self.zero_bytes_forwarded:
109
                if read.startswith(self.secret):
110
                    read = read[len(self.secret):]
111
                    self.zero_bytes_forwarded = False
112
                else:
113
                    read = ''
114
            read = self.detect_and_remove_framing(read)
115
        # print '%04i -->'%len(read)
116
        self.from_remote_buffer += read
117
118
    def writable(self):
119
        return (len(self.to_remote_buffer) > 0)
120
121
    def handle_write(self):
122
        sent = self.send(self.to_remote_buffer)
123
        # print '%04i <--'%sent
124
        self.to_remote_buffer = self.to_remote_buffer[sent:]
125
126
    def handle_close(self):
127
        self.close()
128
        if self.sender:
129
            self.sender.close()
130
131
class sender(asyncore.dispatcher):
132
    def __init__(self, receiver, remoteaddr, remoteport, client_ip):
133
        asyncore.dispatcher.__init__(self)
134
        self.receiver=receiver
135
        receiver.sender=self
136
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
137
        self.connect((random.choice(remoteaddr), remoteport))
138
139
    def handle_connect(self):
140
        pass
141
142
    def handle_read(self):
143
        """Read from TCP server."""
144
        read = self.recv(4096)
145
        # print '<-- %04i'%len(read)
146
        self.receiver.to_remote_buffer += read
147
148
    def writable(self):
149
        return (len(self.receiver.from_remote_buffer) > 0)
150
151
    def handle_write(self):
152
        sent = self.send(self.receiver.from_remote_buffer)
153
        # print '--> %04i'%sent
154
        self.receiver.from_remote_buffer = self.receiver.from_remote_buffer[sent:]
155
156
    def handle_close(self):
157
        # when the buffer has not yet fully been written to the client, don't close quite yet.
158
        # handle_close() will be automatically called again by asyncore
159
        if not self.receiver.to_remote_buffer:
160
            self.close()
161
            self.receiver.close()
162
163
if __name__=='__main__':
164
    import optparse
165
    parser = optparse.OptionParser()
166
167
    parser.add_option(
168
            '-l','--local-ip',
169
            dest='local_ip',default='127.0.0.1',
170
            help='Local IP address to bind to (for listening socket)')
171
    parser.add_option(
172
            '-p','--local-port',
173
            type='int',dest='local_port',
174
            help='Local port to bind to')
175
    parser.add_option(
176
            '-P','--remote-port',
177
            type='int',dest='remote_port',
178
            help='Remote port to connect to')
179
    parser.add_option(
180
            '-r','--remote-hosts',
181
            type='string',dest='remote_hosts',default='127.0.0.1',
182
            help='Remote host(s) to connect to, comma-separated')
183
    parser.add_option(
184
            '-m','--mode',
185
            type='string',dest='mode',
186
            help='Operating mode ("0" for not using a secret key, ' + \
187
                    '"1:<secret>" for using/inserting the specified secret key, ' + \
188
                    '"2:<secret>" for verifying/stripping the specified secret key')
189
    options, args = parser.parse_args()
190
    alladdresses = {}
191
    for h in options.remote_hosts.split(','):
192
        (name, aliaslist, addresslist) = socket.gethostbyname_ex(h)
193
        for a in addresslist:
194
            alladdresses[a] = None
195
    if options.mode is None:
196
        (mode, secret) = (None, None)
197
    else:
198
        (mode, secret) = options.mode.split(':', 1)
199
        if len(secret) < 32:
200
            raise Exception('secret specified in -m option needs to be at least 32 characters long')
201
    #x = asyncake.AsynCake()
202
    forwarder(options.local_ip, options.local_port, alladdresses.keys(), options.remote_port, mode, secret)
203
    #x.loop()
204
    asyncore.loop()