Skip to content

Instantly share code, notes, and snippets.

@richardg867
Last active January 7, 2024 18:12
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save richardg867/bb19ca2b03545f71ae15 to your computer and use it in GitHub Desktop.
Save richardg867/bb19ca2b03545f71ae15 to your computer and use it in GitHub Desktop.
Comic Chat fixer
#
# 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()
@sebastius
Copy link

Just to let you know i love your script!

Occasionally i have some issues where the script seems to b0rk with a few errors, but i haven't found the exact moment/cause yet.

@fluffytid
Copy link

I am trying to set up and old program who used comic chat to communicate between clients, but I don't even know if there are any comic chat servers running anymore, how do I set up this proxy?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment