Skip to content

Instantly share code, notes, and snippets.

@harlowja
Created November 3, 2017 20:36
Show Gist options
  • Save harlowja/43453ca1fce77b68655cc165e666a220 to your computer and use it in GitHub Desktop.
Save harlowja/43453ca1fce77b68655cc165e666a220 to your computer and use it in GitHub Desktop.
import contextlib
import errno
import itertools
import logging
import pkg_resources
import re
import socket
import threading
import traceback
import futurist
import munch
import six
from daddy import channel as c
from daddy import exceptions as excp
from daddy import message as m
LOG = logging.getLogger(__name__)
class FileProxy(object):
def __init__(self, conn):
self._handle = conn.makefile("rw")
self._lock = threading.Lock()
def close(self):
self._handle.close()
def write(self, blob):
with self._lock:
self._handle.write(blob)
self._handle.flush()
def read_line(self, prompt=None):
if prompt:
self.write(prompt)
return self._handle.readline()
def write_lines(self, lines, add_newline=True):
with self._lock:
self._handle.write("\r\n".join(lines))
if add_newline:
self._handle.write("\r\n")
self._handle.flush()
def write_line(self, line):
with self._lock:
self._handle.write(line)
self._handle.write("\r\n")
self._handle.flush()
class Client(threading.Thread):
what = 'daddy'
welcome_tpl = ("You have connected to the %s v%s"
" backdoor micro-telnet server.")
prompt_tpl = "%s Telnet Server> "
close_tpl = "Session to %s disconnected."
result_wait = 1
def __init__(self, bot, socket_pair, ts_counter):
super(Client, self).__init__()
self.bot = bot
self.socket_pair = socket_pair
self.daemon = True
self.ts_counter = ts_counter
def run(self):
conn, addr = self.socket_pair
connected_to = "%s:%s" % addr
f = FileProxy(conn)
try:
with contextlib.closing(f):
with contextlib.closing(conn):
self._run(f, connected_to)
except (IOError, socket.error) as e:
if e.errno in (errno.ECONNRESET, errno.EPIPE):
pass
else:
LOG.warning("Received error closing connection to %s.",
connected_to, exc_info=True)
LOG.debug(self.close_tpl, connected_to)
def _run(self, f, connected_to):
me = pkg_resources.get_distribution(self.what)
welcome = self.welcome_tpl % (self.what, me.version)
prompt = self.prompt_tpl % (self.what.title())
def replier(text, **kwargs):
f.write_line(" " + text)
def reply_maker(message, wants_attachments=False):
if wants_attachments:
raise NotImplementedError
else:
return replier
f.write_line(welcome)
while True:
try:
line = f.read_line(prompt=prompt)
except EOFError:
break
except (IOError, socket.error) as e:
if e.errno in (errno.ECONNRESET, errno.EPIPE):
break
else:
LOG.warning("Received error reading line from %s.",
connected_to, exc_info=True)
else:
line = line.strip()
thread_ts = None
# Simple threading...
t_m = re.match(r"^@(\d+)\s+(.*)$", line)
if t_m:
thread_ts = t_m.group(1)
line = t_m.group(2)
ts = str(six.next(self.ts_counter))
m_headers = {
m.VALIDATED_HEADER: True,
m.TO_ME_HEADER: True,
m.CHECK_AUTH_HEADER: False,
}
m_body = munch.Munch({
'text': line,
'ts': ts,
'thread_ts': thread_ts,
'text_no_links': line,
'username': connected_to,
'user_id': connected_to,
})
if thread_ts is not None:
fut = self.bot.submit_message(
m.Message("repl/message", m_headers,
m_body, reply_maker=reply_maker),
c.FOLLOWUP)
else:
fut = self.bot.submit_message(
m.Message("repl/message", m_headers,
m_body, reply_maker=reply_maker),
c.TARGETED)
lines = [
"Submitted '%s'" % (line),
"Thread = '%s'" % ts,
]
if thread_ts is not None:
lines.append("Thread ts = '%s'" % thread_ts)
lines.append("Please wait for result...")
f.write_lines(lines)
f_waiting = True
while f_waiting:
try:
fut.result(timeout=self.result_wait)
f_waiting = False
except futurist.TimeoutError:
pass
except Exception:
f_waiting = False
f.write_line("Done!")
try:
res = fut.result()
except Exception as e:
if isinstance(e, excp.NoHandlerFound) and e.suggestion:
f.write_lines([
"Finished:",
" Perhaps you meant '%s'?" % e.suggestion,
])
else:
lines = [
"Failed:",
]
buf = six.StringIO()
traceback.print_exc(file=buf)
for line in buf.getvalue().splitlines():
if not line:
continue
lines.append(" " + line)
f.write_lines(lines)
else:
f.write_lines([
"Finished:",
" Result = %s" % res,
])
class Server(threading.Thread):
def __init__(self, bot, port):
super(Server, self).__init__()
self.bot = bot
self.daemon = True
self.dead = threading.Event()
self.port = port
def run(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# This ensures the message ts (from slack terminology) is always
# increasing, no matter how many clients and ...
ts_counter = itertools.count(0)
with contextlib.closing(sock):
sock.bind(('localhost', self.port))
sock.listen(2)
print("Backdoor server listening on %s:%s" % sock.getsockname())
self.port = sock.getsockname()[1]
while True:
try:
socket_pair = sock.accept()
except socket.timeout:
pass
else:
conn_client = Client(self.bot, socket_pair,
ts_counter)
conn_client.start()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment