diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py
index a35a71c57..9d428d251 100644
--- a/gns3server/modules/dynamips/__init__.py
+++ b/gns3server/modules/dynamips/__init__.py
@@ -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():
diff --git a/gns3server/modules/virtualbox/pipe_proxy.py b/gns3server/modules/virtualbox/pipe_proxy.py
deleted file mode 100644
index 619a48fdc..000000000
--- a/gns3server/modules/virtualbox/pipe_proxy.py
+++ /dev/null
@@ -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 .
-
-# 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()
diff --git a/gns3server/modules/virtualbox/telnet_server.py b/gns3server/modules/virtualbox/telnet_server.py
new file mode 100644
index 000000000..da324708e
--- /dev/null
+++ b/gns3server/modules/virtualbox/telnet_server.py
@@ -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 .
+
+
+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)
diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py
index 129ad8b0a..717774563 100644
--- a/gns3server/modules/virtualbox/virtualbox_vm.py
+++ b/gns3server/modules/virtualbox/virtualbox_vm.py
@@ -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'):