mirror of
https://github.com/xcat2/confluent.git
synced 2025-01-15 04:07:51 +00:00
70fad4335a
Properly implement authentication and switch the protocol over to the tlv based protocol. Abandon all thought of the socket being directly accessible. Any CLI semantics will be in confetty and an appliance wishing to expose that CLI directly should use standard ssh stuff with a shell of confetty. The unix domain authentication support makes this feasible (requires user creation push name into confluent repository at the moment..)
180 lines
5.7 KiB
Python
Executable File
180 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# ultimately, this should provide an interactive cli for navigating confluent
|
|
# tree and doing console with a socket. <ESC>]0;<string><BELL> can be used to
|
|
# present info such as whether it is in a console or other mode and, if in
|
|
# console, how many other connections are live and looking
|
|
# this means 'wcons' simply needs to make a terminal run and we'll take care of
|
|
# the title while providing more info
|
|
# this also means the socket interface needs to have ways to convey more
|
|
# interesting pieces of data (like concurrent connection count)
|
|
# socket will probably switch to a TLV scheme:
|
|
# 32 bit TL, 8 bits of type code and 24 bit size
|
|
# type codes:
|
|
# 0: string data
|
|
# 1: json data
|
|
# 24 bit size allows the peer to avoid having to do any particular parsing to
|
|
# understand message boundaries (which is a significant burden on the xCAT
|
|
# protocol)
|
|
|
|
# When in a console client mode, will recognize two escape sequences by
|
|
# default:
|
|
# Ctrl-E, c, ?: mimick conserver behavior
|
|
# ctrl-]: go to interactive prompt (telnet escape, but not telnet prompt)
|
|
# esc-( would interfere with normal esc use too much
|
|
# ~ I will not use for now...
|
|
|
|
import fcntl
|
|
import getpass
|
|
import optparse
|
|
import os
|
|
import select
|
|
import socket
|
|
import ssl
|
|
import sys
|
|
import termios
|
|
import tty
|
|
|
|
path = os.path.dirname(os.path.realpath(__file__))
|
|
path = os.path.realpath(os.path.join(path, '..'))
|
|
sys.path.append(path)
|
|
|
|
import confluent.common.tlvdata as tlvdata
|
|
|
|
SO_PASSCRED = 16
|
|
conserversequence = '\x05c' # ctrl-e, c
|
|
|
|
oldtcattr = termios.tcgetattr(sys.stdin.fileno())
|
|
server = None
|
|
|
|
def parseservervalue(serverstring):
|
|
if serverstring.find(']:') != -1:
|
|
server, port = serverstring[1:].split(']:')
|
|
elif serverstring[0] == '[':
|
|
server = serverstring[1:-1]
|
|
port = 4001
|
|
elif -1 != opts.server.find(':'):
|
|
server, port = opts.server.split(":")
|
|
else:
|
|
server = serverstring
|
|
port = 4001
|
|
return (server, port)
|
|
|
|
|
|
def exit(code=0):
|
|
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, oldtcattr)
|
|
server.shutdown(socket.SHUT_RDWR)
|
|
server.close()
|
|
sys.exit(code)
|
|
|
|
def conserver_command(fh, command):
|
|
while not command:
|
|
ready, _, _ = select.select((fh,), (), (), 1)
|
|
if ready:
|
|
command += fh.read()
|
|
if command[0] == '.':
|
|
print("disconnect]\r")
|
|
exit()
|
|
elif command[0] == '?':
|
|
print("help]\r")
|
|
print(". disconnect\r")
|
|
print("<cr> abort command\r")
|
|
elif command[0] == '\x0d':
|
|
print("ignored]\r")
|
|
else: #not a command at all..
|
|
print("unknown -- use '?']\r")
|
|
|
|
|
|
def check_escape_seq(input, fh):
|
|
while conserversequence.startswith(input):
|
|
if input.startswith(conserversequence): # We have full sequence
|
|
sys.stdout.write("[")
|
|
sys.stdout.flush()
|
|
return conserver_command(fh, input[len(conserversequence):])
|
|
ready, _, _ = select.select((fh,), (), (), 3)
|
|
if not ready: # 3 seconds of no typing
|
|
break
|
|
input += fh.read()
|
|
return input
|
|
|
|
|
|
def connect_unix_server(sockpath):
|
|
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
server.setsockopt(socket.SOL_SOCKET, SO_PASSCRED, 1)
|
|
server.connect(sockpath)
|
|
return server
|
|
|
|
|
|
def connect_tls_server(serverstring):
|
|
host, port = parseservervalue(serverstring)
|
|
for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
|
|
socket.SOCK_STREAM):
|
|
af, socktype, proto, cononname, sa = res
|
|
try:
|
|
server = socket.socket(af, socktype, proto)
|
|
except:
|
|
server = None
|
|
continue
|
|
try:
|
|
server.settimeout(5)
|
|
server.connect(sa)
|
|
except:
|
|
server.close()
|
|
server = None
|
|
continue
|
|
break
|
|
if server is None:
|
|
sys.stderr.write("Failed to connect to %s\n" % serverstring)
|
|
sys.exit(1)
|
|
secserver = ssl.wrap_socket(server)
|
|
return secserver
|
|
|
|
parser = optparse.OptionParser()
|
|
parser.add_option("-s", "--server", dest="server",
|
|
help="TLS server to connect to", metavar="SERVER:PORT")
|
|
parser.add_option("-u", "--unixsocket", dest="unixsock",
|
|
help="TLS server to connect to", metavar="UNIXDOMAINSOCKET")
|
|
opts, args = parser.parse_args()
|
|
if opts.server: # going over a TLS network
|
|
server = connect_tls_server(opts.server)
|
|
elif opts.unixsock:
|
|
server = connect_unix_server(opts.unixsock)
|
|
|
|
#Next stop, reading and writing from whichever of stdin and server goes first.
|
|
#see pyghmi code for solconnect.py
|
|
|
|
banner = tlvdata.recv_tlvdata(server)
|
|
authinfo = tlvdata.recv_tlvdata(server)
|
|
while authinfo['authpassed'] != 1:
|
|
username = raw_input("Name: ")
|
|
passphrase = getpass.getpass("Passphrase: ")
|
|
tlvdata.send_tlvdata(server,
|
|
{'username': username, 'passphrase': passphrase})
|
|
authinfo = tlvdata.recv_tlvdata(server)
|
|
tty.setraw(sys.stdin.fileno())
|
|
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
|
|
# clear on start can help with readable of TUI, but it
|
|
# can be annoying, so for now don't do it.
|
|
# sys.stdout.write('\x1b[H\x1b[J')
|
|
# sys.stdout.flush()
|
|
doexit = False
|
|
while not doexit:
|
|
rdylist, _, _ = select.select((sys.stdin, server), (), (), 60)
|
|
for fh in rdylist:
|
|
if fh == server:
|
|
#fh.read()
|
|
data = tlvdata.recv_tlvdata(fh)
|
|
if data:
|
|
sys.stdout.write(data)
|
|
sys.stdout.flush()
|
|
else:
|
|
doexit = True
|
|
sys.stdout.write("\r\n[remote disconnected]\r\n")
|
|
break
|
|
else:
|
|
input = fh.read()
|
|
input = check_escape_seq(input, fh)
|
|
if input:
|
|
tlvdata.send_tlvdata(server, input)
|
|
exit()
|