ccproxy
This commit is contained in:
parent
afa462467b
commit
442a79002f
186
.scripts/ccproxy.py
Executable file
186
.scripts/ccproxy.py
Executable file
@ -0,0 +1,186 @@
|
|||||||
|
#
|
||||||
|
# Comic Chat fixer MITM proxy: fixes Comic Chat to (sort of) work with modern
|
||||||
|
# IRC servers. Tested with Microsoft Chat 2.5 on Windows XP, 8 and 10
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
|
||||||
|
import getopt, re, socket, sys, threading, time
|
||||||
|
try:
|
||||||
|
import ssl
|
||||||
|
except ImportError:
|
||||||
|
ssl = None
|
||||||
|
|
||||||
|
def thread_c2s(client, client_addr, password, host, port, use_ssl):
|
||||||
|
f = client.makefile()
|
||||||
|
|
||||||
|
queued_lines = []
|
||||||
|
if password:
|
||||||
|
client.sendall(':cchat.proxy 464 * :Password incorrect\r\n')
|
||||||
|
while True:
|
||||||
|
line = f.readline().rstrip('\r\n')
|
||||||
|
if line[:12] == 'OPER (null) ':
|
||||||
|
if password == line[12:]:
|
||||||
|
print '[-] {0}:{1} authenticated successfully'.format(*client_addr)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print '[!] {0}:{1} failed to authenticate'.format(*client_addr)
|
||||||
|
client.sendall(':cchat.proxy 464 * :Password incorrect\r\n')
|
||||||
|
else:
|
||||||
|
queued_lines.append(line)
|
||||||
|
|
||||||
|
irc = socket.create_connection((host, port))
|
||||||
|
if use_ssl:
|
||||||
|
if not ssl: raise Exception('no ssl module')
|
||||||
|
irc = ssl.wrap_socket(irc)
|
||||||
|
|
||||||
|
for line in queued_lines:
|
||||||
|
irc.sendall(line + '\r\n')
|
||||||
|
|
||||||
|
t = threading.Thread(target=thread_s2c, args=(client, client_addr, irc))
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
line = f.readline()
|
||||||
|
irc.sendall(line)
|
||||||
|
if len(line) == 0 or line[:5] == 'QUIT ': break
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(1)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
irc.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
client.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def thread_s2c(client, client_addr, irc):
|
||||||
|
f = irc.makefile()
|
||||||
|
|
||||||
|
srv_prefix = '@+'
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
line = f.readline()
|
||||||
|
|
||||||
|
split = line.split(' ')
|
||||||
|
if len(split) > 2:
|
||||||
|
if split[0] == 'ERROR':
|
||||||
|
client.sendall(line)
|
||||||
|
break
|
||||||
|
elif split[1] == '005':
|
||||||
|
# Get PREFIX= to fix ranks in the NAMES response
|
||||||
|
match = re.search(''' PREFIX=\(([^\)]+)\)([^\s]+)''', line)
|
||||||
|
if match:
|
||||||
|
srv_prefix = match.group(2)
|
||||||
|
elif split[1] == 'JOIN' and split[2][0] != ':':
|
||||||
|
# Main purpose of the proxy. Fixes a crash bug with newer
|
||||||
|
# ircds, which send JOIN confirmations like this:
|
||||||
|
#
|
||||||
|
# :nick!user@host JOIN #channel
|
||||||
|
#
|
||||||
|
# instead of this:
|
||||||
|
#
|
||||||
|
# :nick!user@host JOIN :#channel
|
||||||
|
#
|
||||||
|
# CChat expects the channel name to have a : before the
|
||||||
|
# name. If it doesn't, it will crash, since it somehow
|
||||||
|
# attempts a stricmp(0).
|
||||||
|
split[2] = ':' + split[2]
|
||||||
|
elif split[1] == '353':
|
||||||
|
# Convert additional ranks to regular op
|
||||||
|
for i in range(5, len(split)):
|
||||||
|
rank = ''
|
||||||
|
nick = ''
|
||||||
|
for char in split[i]:
|
||||||
|
if char == '+' and rank != '@':
|
||||||
|
# voice
|
||||||
|
rank = '+'
|
||||||
|
elif char in srv_prefix:
|
||||||
|
# everything unknown to CChat becomes op
|
||||||
|
rank = '@'
|
||||||
|
elif char != ':':
|
||||||
|
# not a rank
|
||||||
|
nick += char
|
||||||
|
split[i] = (split[i][0] == ':' and ':' or '') + rank + nick
|
||||||
|
line = ' '.join(split)
|
||||||
|
|
||||||
|
# Comic Chat will stop receiving if it receives a line longer than
|
||||||
|
# 512 bytes, including the trailing CRLF.
|
||||||
|
client.sendall(line.rstrip('\r\n')[:510] + '\r\n')
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
irc.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
client.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def main():
|
||||||
|
bind_host = ''
|
||||||
|
bind_port = 6461
|
||||||
|
password = None
|
||||||
|
|
||||||
|
options, remainder = getopt.getopt(sys.argv[1:], 'h:p:a:', ['bindhost=', 'bindport=', 'password='])
|
||||||
|
for opt, arg in options:
|
||||||
|
if opt in ('-h', '--bindhost'):
|
||||||
|
bind_host = arg
|
||||||
|
elif opt in ('-p', '--bindport'):
|
||||||
|
bind_port = int(arg)
|
||||||
|
elif opt in ('-a', '--password'):
|
||||||
|
password = arg
|
||||||
|
|
||||||
|
if bind_port < 0 or bind_port > 65535 or len(remainder) < 1:
|
||||||
|
print 'Usage: proxy.py [-h bindhost] [-p bindport] [-a password] server [[+]port]'
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
host = remainder[0]
|
||||||
|
if len(remainder) > 1:
|
||||||
|
if remainder[1][0] == '+':
|
||||||
|
use_ssl = True
|
||||||
|
port = int(remainder[1][1:])
|
||||||
|
else:
|
||||||
|
use_ssl = False
|
||||||
|
port = int(remainder[1])
|
||||||
|
else:
|
||||||
|
use_ssl = False
|
||||||
|
port = 6667
|
||||||
|
|
||||||
|
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
server.bind((bind_host, bind_port))
|
||||||
|
server.listen(5)
|
||||||
|
|
||||||
|
print '[-] Waiting for connections'
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
client, client_addr = server.accept()
|
||||||
|
print '[-] Connection from {0}:{1}'.format(*client_addr)
|
||||||
|
t = threading.Thread(target=thread_c2s, args=(client, client_addr, password, host, port, use_ssl))
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
server.close()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue
Block a user