~aleteoryx/ntalk

eca131347c4561f3102a9e2c75a0d01cd5714488 — Aleteoryx a month ago 69a3bf4
nanobnc
3 files changed, 145 insertions(+), 1 deletions(-)

M README.md
A nanobnc.py
M ntalk.tcl
M README.md => README.md +34 -0
@@ 26,6 26,11 @@ the connection is done entirely over $sok, so set that to whatever.
if you want to include e.g. tcltls, go ahead. the config file is saved
to/read from ~/.config/ntalk/cscript.tcl.

there is a `connect` proc available to the config, that automatically sets up the socket
with the right encoding and line endings. it is called the same as `socket`. it is
provided to make interacting with nanobnc.py easier. its use is not mandatory, ntalk will
ensure the socket is setup right later.

right click on any sixel in the chatlog, and you can save it under a custom name.
saved sixels can be accessed through the "sixels" option in the top menu.
slashes are interpreted, so naming one e.g. "faces / :D" will create a submenu called "faces".


@@ 61,6 66,35 @@ consists of the following scripts:
- `9talk/motd addr`: checks for the most recent MOTD of $addr, and echos it. the most recent MOTD and last-seen message id are stored in $home/lib/9talk/$addr.motd and $home/lib/9talk/$addr.id, respectively.


## `nanobnc.py`: simple nanochat bouncer

this script should be run behind a connection broker, like socat. it assumes a client is
attached to stdio. run it like:

```
socat TCP-LISTEN:22344,fork EXEC:'python nanobnc.py <password> [timeout]'
```

timeout is in seconds. it defaults to 1800, 30 minutes.

when a client connects, it should send a line of the form `<addr> <port> <password>`. if
the password matches the one passed as an argument, the script will connect to the
upstream nanochat server at addr:port. data is blindly copied between the client and the
upstream, until either the client and server are idle for timeout seconds, or until the
client sends QUIT, at which point the script gracefully disconnects from the upstream
server, and exits

to use this with ntalk, replace the `set sok ...` line in your config with the following:

```
set sok [connect <bnc addr> <bnc port>]
puts $sok "<upstream addr> <upstream port> <password>"
```

as this script is essentially an open TCP proxy, be very careful with your choice of
password, and with who you allow to access the server. 


## `scrollbackup.sh`: simple backup script

just sends HIST and QUIT and logs it to a file whose name indicates

A nanobnc.py => nanobnc.py +104 -0
@@ 0,0 1,104 @@
#!/usr/bin/env python

import socket
import threading
from sys import exit, argv, stderr, stdout, stdin
from signal import alarm, signal, SIGALRM

argv0 = 'nanobnc.py'
sok = None

def usage():
	print(f'usage: {argv0} passphrase [timeout]')
	exit(1)

def handle_args():
	global argv0, argv
	if len(argv) > 0:
		argv0 = argv[0]
		argv = argv[1:]
	if len(argv) not in (1, 2):
		usage()

	passphrase = argv[0]
	if len(argv) == 1:
		timeout = 1800
	else:
		timeout = int(argv[1])

	return passphrase, timeout

def handle_req(passphrase):
	req = input().split(maxsplit=2)
	if len(req) != 3:
		print('bad # of connection args')
		exit(3)
	host, port, pw = req

	if pw != passphrase:
		print('bad passphrase')
		print(f'bad passphrase trying to connect to {host}:{port}', file=stderr)
		exit(4)

	try:
		port = int(port)
	except:
		print('bad port')
		exit(3)

	return host, port

def shutdown():
	if sok is not None:
		try:
			sok.sendall(b'QUIT\n')
			sok.shutdown(socket.SHUT_RDWR)
		except:
			pass

def onalarm(sig, stk):
	try:
		print('timeout!')
		stdout.flush()
	except:
		pass
	try:
		print('timeout!', file=stderr)
		stderr.flush()
	except:
		pass

	shutdown()
	exit(2)

signal(SIGALRM, onalarm)

passphrase, timeout = handle_args()
alarm(timeout)
host, port = handle_req(passphrase)

try:
	sok = socket.create_connection((host, port))
except Exception as e:
	print(f'connection failed: {e}')
	exit(5)

def toserver():
	while (s := stdin.readline()) != '':
		if s == 'QUIT\n':
			break
		sok.sendall(s.encode())
	shutdown()
	exit(0)

def toclient():
	fp = sok.makefile('r', encoding='ascii', errors='ignore')
	while (s := fp.readline()) != '':
		stdout.write(s)
		stdout.flush()
	shutdown()
	exit(0)

t = threading.Thread(target=toclient)
t.start()
toserver()

M ntalk.tcl => ntalk.tcl +7 -1
@@ 502,6 502,12 @@ bind .buffer <Button-3> {

### SERVER MANAGEMENT ###

proc connect {addr port} {
	set sok [socket $addr $port]
	fconfigure $sok -translation lf -blocking false -encoding iso8859-1
	return $sok
}

proc gencscript {{server localhost} {port 44322}} {
	set server [list $server]
	set port [list $port]


@@ 516,7 522,7 @@ proc gencscript {{server localhost} {port 44322}} {
set server $server
set port $port
}]
	append ret {set sok [socket $server $port]}
	append ret {set sok [connect $server $port]}
	return [string trim $ret]
}