1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-12 19:38:57 +00:00

New Telnet server for VirtualBox.

This commit is contained in:
grossmj 2014-11-02 15:47:44 -07:00
parent 4d23c5917c
commit 393a312e7e
4 changed files with 462 additions and 492 deletions

View File

@ -347,8 +347,13 @@ class Dynamips(IModule):
# for local server
new_working_dir = request.pop("working_dir")
try:
self._hypervisor_manager.working_dir = new_working_dir
except DynamipsError as e:
log.error("could not change working directory: {}".format(e))
return
self._working_dir = new_working_dir
self._hypervisor_manager.working_dir = new_working_dir
# apply settings to the hypervisor manager
for name, value in request.items():

View File

@ -1,476 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 GNS3 Technologies Inc.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Parts of this code have been taken from Pyserial project (http://pyserial.sourceforge.net/) under Python license
import sys
import time
import threading
import socket
import select
if sys.platform.startswith("win"):
import win32pipe
import win32file
class PipeProxy(threading.Thread):
def __init__(self, name, pipe, host, port):
self.devname = name
self.pipe = pipe
self.host = host
self.port = port
self.server = None
self.reader_thread = None
self.use_thread = False
self._write_lock = threading.Lock()
self.clients = {}
self.timeout = 0.1
self.alive = True
if sys.platform.startswith("win"):
# we must a thread for reading the pipe on Windows because it is a Named Pipe and it cannot be monitored by select()
self.use_thread = True
try:
if self.host.__contains__(':'):
# IPv6 address support
self.server = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
else:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind((self.host, int(self.port)))
self.server.listen(5)
except socket.error as msg:
self.error("unable to create the socket server %s" % msg)
return
threading.Thread.__init__(self)
self.debug("initialized, waiting for clients on %s:%i..." % (self.host, self.port))
def error(self, msg):
sys.stderr.write("ERROR -> %s PIPE PROXY: %s\n" % (self.devname, msg))
def debug(self, msg):
sys.stdout.write("INFO -> %s PIPE PROXY: %s\n" % (self.devname, msg))
def run(self):
while True:
recv_list = [self.server.fileno()]
if not self.use_thread:
recv_list.append(self.pipe.fileno())
for client in self.clients.values():
if client.active:
recv_list.append(client.fileno)
else:
self.debug("lost client %s" % client.addrport())
try:
client.sock.close()
except:
pass
del self.clients[client.fileno]
try:
rlist, slist, elist = select.select(recv_list, [], [], self.timeout)
except select.error as err:
self.error("fatal select error %d:%s" % (err[0], err[1]))
return False
if not self.alive:
self.debug('Exiting ...')
return True
for sock_fileno in rlist:
if sock_fileno == self.server.fileno():
try:
sock, addr = self.server.accept()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.debug("new client %s:%s" % (addr[0], addr[1]))
except socket.error as err:
self.error("accept error %d:%s" % (err[0], err[1]))
continue
new_client = TelnetClient(sock, addr)
self.clients[new_client.fileno] = new_client
welcome_msg = "%s console is now available ... Press RETURN to get started.\r\n" % self.devname
sock.send(welcome_msg.encode('utf-8'))
if self.use_thread and not self.reader_thread:
self.reader_thread = threading.Thread(target=self.reader)
self.reader_thread.setDaemon(True)
self.reader_thread.setName('pipe->socket')
self.reader_thread.start()
elif not self.use_thread and sock_fileno == self.pipe.fileno():
data = self.read_from_pipe()
if not data:
self.debug("pipe has been closed!")
return False
for client in self.clients.values():
try:
client.send(data)
except:
self.debug(msg)
client.deactivate()
elif sock_fileno in self.clients:
try:
data = self.clients[sock_fileno].socket_recv()
# For some reason, windows likes to send "cr/lf" when you send a "cr".
# Strip that so we don't get a double prompt.
data = data.replace(b"\r\n", b"\n")
self.write_to_pipe(data)
except Exception as msg:
self.debug(msg)
self.clients[sock_fileno].deactivate()
def write_to_pipe(self, data):
if sys.platform.startswith('win'):
win32file.WriteFile(self.pipe, data)
else:
self.pipe.sendall(data)
def read_from_pipe(self):
if sys.platform.startswith('win'):
(read, num_avail, num_message) = win32pipe.PeekNamedPipe(self.pipe, 0)
if num_avail > 0:
(error_code, output) = win32file.ReadFile(self.pipe, num_avail, None)
return output
return ""
else:
return self.pipe.recv(1024)
def reader(self):
"""loop forever and copy pipe->socket"""
self.debug("reader thread started")
while self.alive:
try:
data = self.read_from_pipe()
if not data and not sys.platform.startswith('win'):
self.debug("pipe has been closed!")
break
self._write_lock.acquire()
try:
for client in self.clients.values():
client.send(data)
finally:
self._write_lock.release()
if sys.platform.startswith('win'):
# sleep every 10 ms
time.sleep(0.01)
except:
self.debug("pipe has been closed!")
break
self.debug("reader thread exited")
self.stop()
def stop(self):
"""Stop copying"""
if self.alive:
self.alive = False
for client in self.clients.values():
client.sock.close()
client.deactivate()
# telnet protocol characters
IAC = 255 # Interpret As Command
DONT = 254
DO = 253
WONT = 252
WILL = 251
IAC_DOUBLED = [IAC, IAC]
SE = 240 # Subnegotiation End
NOP = 241 # No Operation
DM = 242 # Data Mark
BRK = 243 # Break
IP = 244 # Interrupt process
AO = 245 # Abort output
AYT = 246 # Are You There
EC = 247 # Erase Character
EL = 248 # Erase Line
GA = 249 # Go Ahead
SB = 250 # Subnegotiation Begin
# selected telnet options
ECHO = 1 # echo
SGA = 3 # suppress go ahead
LINEMODE = 34 # line mode
TERMTYPE = 24 # terminal type
# Telnet filter states
M_NORMAL = 0
M_IAC_SEEN = 1
M_NEGOTIATE = 2
# TelnetOption and TelnetSubnegotiation states
REQUESTED = 'REQUESTED'
ACTIVE = 'ACTIVE'
INACTIVE = 'INACTIVE'
REALLY_INACTIVE = 'REALLY_INACTIVE'
class TelnetOption(object):
"""Manage a single telnet option, keeps track of DO/DONT WILL/WONT."""
def __init__(self, connection, name, option, send_yes, send_no, ack_yes, ack_no, initial_state, activation_callback=None):
"""Init option.
:param connection: connection used to transmit answers
:param name: a readable name for debug outputs
:param send_yes: what to send when option is to be enabled.
:param send_no: what to send when option is to be disabled.
:param ack_yes: what to expect when remote agrees on option.
:param ack_no: what to expect when remote disagrees on option.
:param initial_state: options initialized with REQUESTED are tried to
be enabled on startup. use INACTIVE for all others.
"""
self.connection = connection
self.name = name
self.option = option
self.send_yes = send_yes
self.send_no = send_no
self.ack_yes = ack_yes
self.ack_no = ack_no
self.state = initial_state
self.active = False
self.activation_callback = activation_callback
def __repr__(self):
"""String for debug outputs"""
return "%s:%s(%s)" % (self.name, self.active, self.state)
def process_incoming(self, command):
"""A DO/DONT/WILL/WONT was received for this option, update state and
answer when needed."""
if command == self.ack_yes:
if self.state is REQUESTED:
self.state = ACTIVE
self.active = True
if self.activation_callback is not None:
self.activation_callback()
elif self.state is ACTIVE:
pass
elif self.state is INACTIVE:
self.state = ACTIVE
self.connection.telnetSendOption(self.send_yes, self.option)
self.active = True
if self.activation_callback is not None:
self.activation_callback()
elif self.state is REALLY_INACTIVE:
self.connection.telnetSendOption(self.send_no, self.option)
else:
raise ValueError('option in illegal state %r' % self)
elif command == self.ack_no:
if self.state is REQUESTED:
self.state = INACTIVE
self.active = False
elif self.state is ACTIVE:
self.state = INACTIVE
self.connection.telnetSendOption(self.send_no, self.option)
self.active = False
elif self.state is INACTIVE:
pass
elif self.state is REALLY_INACTIVE:
pass
else:
raise ValueError('option in illegal state %r' % self)
class TelnetClient(object):
"""
Represents a client connection via Telnet.
First argument is the socket discovered by the Telnet Server.
Second argument is the tuple (ip address, port number).
"""
def __init__(self, sock, addr_tup):
self.active = True # Turns False when the connection is lost
self.sock = sock # The connection's socket
self.fileno = sock.fileno() # The socket's file descriptor
self.address = addr_tup[0] # The client's remote TCP/IP address
self.port = addr_tup[1] # The client's remote port
# filter state machine
self.mode = M_NORMAL
self.suboption = None
self.telnet_command = None
# all supported telnet options
self._telnet_options = [
TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED),
TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED),
TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE),
TelnetOption(self, 'LINEMODE', LINEMODE, DONT, DONT, WILL, WONT, REQUESTED),
TelnetOption(self, 'TERMTYPE', TERMTYPE, DO, DONT, WILL, WONT, REQUESTED),
]
for option in self._telnet_options:
if option.state is REQUESTED:
self.telnetSendOption(option.send_yes, option.option)
def telnetSendOption(self, action, option):
"""Send DO, DONT, WILL, WONT."""
self.sock.sendall(bytes([IAC, action, option]))
def escape(self, data):
""" All outgoing data has to be properly escaped, so that no IAC character
in the data stream messes up the Telnet state machine in the server.
"""
for byte in data:
if byte == IAC:
yield IAC
yield IAC
else:
yield byte
def filter(self, data):
""" handle a bunch of incoming bytes. this is a generator. it will yield
all characters not of interest for Telnet
"""
for byte in data:
if self.mode == M_NORMAL:
# interpret as command or as data
if byte == IAC:
self.mode = M_IAC_SEEN
else:
# store data in sub option buffer or pass it to our
# consumer depending on state
if self.suboption is not None:
self.suboption.append(byte)
else:
yield byte
elif self.mode == M_IAC_SEEN:
if byte == IAC:
# interpret as command doubled -> insert character
# itself
if self.suboption is not None:
self.suboption.append(byte)
else:
yield byte
self.mode = M_NORMAL
elif byte == SB:
# sub option start
self.suboption = bytearray()
self.mode = M_NORMAL
elif byte == SE:
# sub option end -> process it now
#self._telnetProcessSubnegotiation(bytes(self.suboption))
self.suboption = None
self.mode = M_NORMAL
elif byte in (DO, DONT, WILL, WONT):
# negotiation
self.telnet_command = byte
self.mode = M_NEGOTIATE
else:
# other telnet commands are ignored!
self.mode = M_NORMAL
elif self.mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following
self._telnetNegotiateOption(self.telnet_command, byte)
self.mode = M_NORMAL
def _telnetNegotiateOption(self, command, option):
"""Process incoming DO, DONT, WILL, WONT."""
# check our registered telnet options and forward command to them
# they know themselves if they have to answer or not
known = False
for item in self._telnet_options:
# can have more than one match! as some options are duplicated for
# 'us' and 'them'
if item.option == option:
item.process_incoming(command)
known = True
if not known:
# handle unknown options
# only answer to positive requests and deny them
if command == WILL or command == DO:
self.telnetSendOption((command == WILL and DONT or WONT), option)
def send(self, data):
"""
Send data to the distant end.
"""
try:
self.sock.sendall(bytes(self.escape(data)))
except socket.error as ex:
self.active = False
raise Exception("socket.sendall() error '%d:%s' from %s" % (ex[0], ex[1], self.addrport()))
def deactivate(self):
"""
Set the client to disconnect on the next server poll.
"""
self.active = False
def addrport(self):
"""
Return the DE's IP address and port number as a string.
"""
return "%s:%s" % (self.address, self.port)
def socket_recv(self):
"""
Called by TelnetServer when recv data is ready.
"""
try:
data = self.sock.recv(4096)
except socket.error as ex:
raise Exception("socket.recv() error '%d:%s' from %s" % (ex[0], ex[1], self.addrport()))
## Did they close the connection?
size = len(data)
if size == 0:
raise Exception("connection closed by %s" % self.addrport())
return bytes(self.filter(data))
if __name__ == '__main__':
if sys.platform.startswith('win'):
import msvcrt
pipe_name = r'\\.\pipe\VBOX\Linux_Microcore_3.8.2'
pipe = open(pipe_name, 'a+b')
pipe_proxy = PipeProxy("VBOX", msvcrt.get_osfhandle(pipe.fileno()), '127.0.0.1', 3900)
else:
try:
unix_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
#unix_socket.settimeout(0.1)
unix_socket.connect("/tmp/pipe_test")
except socket.error as err:
print("Socket error -> %d:%s" % (err[0], err[1]))
sys.exit(False)
pipe_proxy = PipeProxy('VBOX', unix_socket, '127.0.0.1', 3900)
pipe_proxy.setDaemon(True)
pipe_proxy.start()
pipe.proxy.stop()
pipe_proxy.join()

View File

@ -0,0 +1,444 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 GNS3 Technologies Inc.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
import time
import threading
import socket
import select
import logging
log = logging.getLogger(__name__)
if sys.platform.startswith("win"):
import win32pipe
import win32file
class TelnetServer(threading.Thread):
"""
Mini Telnet Server.
:param vm_name: Virtual machine name
:param pipe_path: path to VM pipe (UNIX socket on Linux/UNIX, Named Pipe on Windows)
:param host: server host
:param port: server port
"""
def __init__(self, vm_name, pipe_path, host, port):
self._vm_name = vm_name
self._pipe = pipe_path
self._host = host
self._port = port
self._reader_thread = None
self._use_thread = False
self._write_lock = threading.Lock()
self._clients = {}
self._timeout = 1
self._alive = True
if sys.platform.startswith("win"):
# we must a thread for reading the pipe on Windows because it is a Named Pipe and it cannot be monitored by select()
self._use_thread = True
try:
if ":" in self._host:
# IPv6 address support
self._server_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
else:
self._server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._server_socket.bind((self._host, self._port))
self._server_socket.listen(socket.SOMAXCONN)
except OSError as e:
log.critical("unable to create a server socket: {}".format(e))
return
threading.Thread.__init__(self)
log.info("Telnet server initialized, waiting for clients on {}:{}".format(self._host, self._port))
def run(self):
"""
Thread loop.
"""
while True:
recv_list = [self._server_socket.fileno()]
if not self._use_thread:
recv_list.append(self._pipe.fileno())
for client in self._clients.values():
if client.is_active():
recv_list.append(client.socket().fileno())
else:
del self._clients[client.socket().fileno()]
try:
client.socket().shutdown(socket.SHUT_RDWR)
except OSError as e:
log.warn("shutdown: {}".format(e))
client.socket().close()
break
try:
rlist, slist, elist = select.select(recv_list, [], [], self._timeout)
except OSError as e:
log.critical("fatal select error: {}".format(e))
return False
if not self._alive:
log.info("Telnet server for {} is exiting".format(self._vm_name))
return True
for sock_fileno in rlist:
if sock_fileno == self._server_socket.fileno():
try:
sock, addr = self._server_socket.accept()
host, port = addr
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
log.info("new client {}:{} has connected".format(host, port))
except OSError as e:
log.error("could not accept new client: {}".format(e))
continue
new_client = TelnetClient(self._vm_name, sock, host, port)
self._clients[sock.fileno()] = new_client
if self._use_thread and not self._reader_thread:
self._reader_thread = threading.Thread(target=self._reader, daemon=True)
self._reader_thread.start()
elif not self._use_thread and sock_fileno == self._pipe.fileno():
data = self._read_from_pipe()
if not data:
log.warning("pipe has been closed!")
return False
for client in self._clients.values():
try:
client.send(data)
except OSError as e:
log.debug(e)
client.deactivate()
elif sock_fileno in self._clients:
try:
data = self._clients[sock_fileno].socket_recv()
if not data:
continue
# For some reason, windows likes to send "cr/lf" when you send a "cr".
# Strip that so we don't get a double prompt.
data = data.replace(b"\r\n", b"\n")
self._write_to_pipe(data)
except Exception as msg:
log.info(msg)
self._clients[sock_fileno].deactivate()
def _write_to_pipe(self, data):
"""
Writes data to the pipe.
:param data: data to write
"""
if sys.platform.startswith('win'):
win32file.WriteFile(self._pipe, data)
else:
self._pipe.sendall(data)
def _read_from_pipe(self):
"""
Reads data from the pipe.
:returns: data
"""
if sys.platform.startswith('win'):
(read, num_avail, num_message) = win32pipe.PeekNamedPipe(self._pipe, 0)
if num_avail > 0:
(error_code, output) = win32file.ReadFile(self._pipe, num_avail, None)
return output
return ""
else:
return self._pipe.recv(1024)
def _reader(self):
"""
Loops forever and copy everything from the pipe to the socket.
"""
log.debug("reader thread has started")
while self._alive:
try:
data = self._read_from_pipe()
if not data and not sys.platform.startswith('win'):
log.debug("pipe has been closed!")
break
self._write_lock.acquire()
try:
for client in self._clients.values():
client.send(data)
finally:
self._write_lock.release()
if sys.platform.startswith('win'):
# sleep every 10 ms
time.sleep(0.01)
except Exception:
log.debug("pipe has been closed!")
break
log.debug("reader thread exited")
self.stop()
def stop(self):
"""
Stops the server.
"""
if self._alive:
self._alive = False
for client in self._clients.values():
client.socket().close()
client.deactivate()
# Mostly from https://code.google.com/p/miniboa/source/browse/trunk/miniboa/telnet.py
# Telnet Commands
SE = 240 # End of sub-negotiation parameters
NOP = 241 # No operation
DATMK = 242 # Data stream portion of a sync.
BREAK = 243 # NVT Character BRK
IP = 244 # Interrupt Process
AO = 245 # Abort Output
AYT = 246 # Are you there
EC = 247 # Erase Character
EL = 248 # Erase Line
GA = 249 # The Go Ahead Signal
SB = 250 # Sub-option to follow
WILL = 251 # Will; request or confirm option begin
WONT = 252 # Wont; deny option request
DO = 253 # Do = Request or confirm remote option
DONT = 254 # Don't = Demand or confirm option halt
IAC = 255 # Interpret as Command
SEND = 1 # Sub-process negotiation SEND command
IS = 0 # Sub-process negotiation IS command
# Telnet Options
BINARY = 0 # Transmit Binary
ECHO = 1 # Echo characters back to sender
RECON = 2 # Reconnection
SGA = 3 # Suppress Go-Ahead
TMARK = 6 # Timing Mark
TTYPE = 24 # Terminal Type
NAWS = 31 # Negotiate About Window Size
LINEMO = 34 # Line Mode
class TelnetClient(object):
"""
Represents a Telnet client connection.
:param vm_name: VM name
:param sock: socket connection
:param host: IP of the Telnet client
:param port: port of the Telnet client
"""
def __init__(self, vm_name, sock, host, port):
self._active = True
self._sock = sock
self._host = host
self._port = port
sock.send(bytes([IAC, WILL, ECHO,
IAC, WILL, SGA,
IAC, WILL, BINARY,
IAC, DO, BINARY]))
welcome_msg = "{} console is now available... Press RETURN to get started.\r\n".format(vm_name)
sock.send(welcome_msg.encode('utf-8'))
def is_active(self):
"""
Returns either the client is active or not.
:return: boolean
"""
return self._active
def socket(self):
"""
Returns the socket for this Telnet client.
:returns: socket instance.
"""
return self._sock
def send(self, data):
"""
Sends data to the remote end.
:param data: data to send
"""
try:
self._sock.send(data)
except OSError as e:
self._active = False
raise Exception("Socket send: {}".format(e))
def deactivate(self):
"""
Sets the client to disconnect on the next server poll.
"""
self._active = False
def socket_recv(self):
"""
Called by Telnet Server when data is ready.
"""
try:
buf = self._sock.recv(1024, socket.MSG_DONTWAIT)
except BlockingIOError:
return None
except ConnectionResetError:
buf = b''
# is the connection closed?
if not buf:
raise Exception("connection closed by {}:{}".format(self._host, self._port))
# Process and remove any telnet commands from the buffer
if IAC in buf:
buf = self._IAC_parser(buf)
return buf
def _read_block(self, bufsize):
"""
Reads a block for data from the socket.
:param bufsize: size of the buffer
:returns: data read
"""
buf = self._sock.recv(1024, socket.MSG_WAITALL)
# If we don't get everything we were looking for then the
# client probably disconnected.
if len(buf) < bufsize:
raise Exception("connection closed by {}:{}".format(self._host, self._port))
return buf
def _IAC_parser(self, buf):
"""
Processes and removes any Telnet commands from the buffer.
:param buf: buffer
:returns: buffer minus Telnet commands
"""
skip_to = 0
while self._active:
# Locate an IAC to process
iac_loc = buf.find(IAC, skip_to)
if iac_loc < 0:
break
# Get the TELNET command
iac_cmd = bytearray([IAC])
try:
iac_cmd.append(buf[iac_loc + 1])
except IndexError:
buf.extend(self._read_block(1))
iac_cmd.append(buf[iac_loc + 1])
# Is this just a 2-byte TELNET command?
if iac_cmd[1] not in [WILL, WONT, DO, DONT]:
if iac_cmd[1] == AYT:
log.debug("Telnet server received Are-You-There (AYT)")
self._sock.send(b'\r\nYour Are-You-There received. I am here.\r\n')
elif iac_cmd[1] == IAC:
# It's data, not an IAC
iac_cmd.pop()
# This prevents the 0xff from being
# interrupted as yet another IAC
skip_to = iac_loc + 1
log.debug("Received IAC IAC")
elif iac_cmd[1] == NOP:
pass
else:
log.debug("Unhandled telnet command: "
"{0:#x} {1:#x}".format(*iac_cmd))
# This must be a 3-byte TELNET command
else:
try:
iac_cmd.append(buf[iac_loc + 2])
except IndexError:
buf.extend(self._read_block(1))
iac_cmd.append(buf[iac_loc + 2])
# We do ECHO, SGA, and BINARY. Period.
if iac_cmd[1] == DO and iac_cmd[2] not in [ECHO, SGA, BINARY]:
self._sock.send(bytes([IAC, WONT, iac_cmd[2]]))
log.debug("Telnet WON'T {:#x}".format(iac_cmd[2]))
else:
log.debug("Unhandled telnet command: "
"{0:#x} {1:#x} {2:#x}".format(*iac_cmd))
# Remove the entire TELNET command from the buffer
buf = buf.replace(iac_cmd, b'', 1)
# Return the new copy of the buffer, minus telnet commands
return buf
if __name__ == '__main__':
log.setLevel(logging.INFO)
if sys.platform.startswith('win'):
import msvcrt
pipe_name = r'\\.\pipe\VBOX\Linux_Microcore_3.8.2'
pipe = open(pipe_name, 'a+b')
telnet_server = TelnetServer("VBOX", msvcrt.get_osfhandle(pipe.fileno()), "127.0.0.1", 3900)
else:
pipe_name = "/tmp/pipe_test"
try:
unix_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
unix_socket.connect(pipe_name)
except OSError as e:
print("Could not connect to UNIX socket {}: {}".format(pipe_name, e))
sys.exit(False)
telnet_server = TelnetServer("VBOX", unix_socket, "127.0.0.1", 3900)
telnet_server.setDaemon(True)
telnet_server.start()
try:
telnet_server.join()
except KeyboardInterrupt:
telnet_server.stop()
telnet_server.join(timeout=3)

View File

@ -32,7 +32,7 @@ import time
from .virtualbox_error import VirtualBoxError
from .adapters.ethernet_adapter import EthernetAdapter
from ..attic import find_unused_port
from .pipe_proxy import PipeProxy
from .telnet_server import TelnetServer
if sys.platform.startswith('win'):
import msvcrt
@ -96,7 +96,7 @@ class VirtualBoxVM(object):
self._console_start_port_range = console_start_port_range
self._console_end_port_range = console_end_port_range
self._serial_pipe_thread = None
self._telnet_server_thread = None
self._serial_pipe = None
# VirtualBox settings
@ -626,6 +626,7 @@ class VirtualBoxVM(object):
args = [self._vmname, "--nictype{}".format(adapter_id + 1), vbox_adapter_type]
self._execute("modifyvm", args)
self._modify_vm("--nictrace{} off".format(adapter_id + 1))
nio = self._ethernet_adapters[adapter_id].get_nio(0)
if nio:
log.debug("setting UDP params on adapter {}".format(adapter_id))
@ -639,8 +640,6 @@ class VirtualBoxVM(object):
if nio.capturing:
self._modify_vm("--nictrace{} on".format(adapter_id + 1))
self._modify_vm("--nictracefile{} {}".format(adapter_id + 1, nio.pcap_output_file))
else:
self._modify_vm("--nictrace{} off".format(adapter_id + 1))
else:
# shutting down unused adapters...
self._modify_vm("--nic{} null".format(adapter_id + 1))
@ -681,30 +680,28 @@ class VirtualBoxVM(object):
self._serial_pipe = open(pipe_name, "a+b")
except OSError as e:
raise VirtualBoxError("Could not open the pipe {}: {}".format(pipe_name, e))
self._serial_pipe_thread = PipeProxy(self._vmname, msvcrt.get_osfhandle(self._serial_pipe.fileno()), self._host, self._console)
#self._serial_pipe_thread.setDaemon(True)
self._serial_pipe_thread.start()
self._telnet_server_thread = TelnetServer(self._vmname, msvcrt.get_osfhandle(self._serial_pipe.fileno()), self._host, self._console)
self._telnet_server_thread.start()
else:
try:
self._serial_pipe = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self._serial_pipe.connect(pipe_name)
except OSError as e:
raise VirtualBoxError("Could not connect to the pipe {}: {}".format(pipe_name, e))
self._serial_pipe_thread = PipeProxy(self._vmname, self._serial_pipe, self._host, self._console)
#self._serial_pipe_thread.setDaemon(True)
self._serial_pipe_thread.start()
self._telnet_server_thread = TelnetServer(self._vmname, self._serial_pipe, self._host, self._console)
self._telnet_server_thread.start()
def stop(self):
"""
Stops this VirtualBox VM.
"""
if self._serial_pipe_thread:
self._serial_pipe_thread.stop()
self._serial_pipe_thread.join(1)
if self._serial_pipe_thread.isAlive():
if self._telnet_server_thread:
self._telnet_server_thread.stop()
self._telnet_server_thread.join(timeout=3)
if self._telnet_server_thread.isAlive():
log.warn("Serial pire thread is still alive!")
self._serial_pipe_thread = None
self._telnet_server_thread = None
if self._serial_pipe:
if sys.platform.startswith('win'):