summaryrefslogtreecommitdiff
path: root/src/leap/OpenVPNConnection.py
diff options
context:
space:
mode:
authorantialias <antialias@leap.se>2012-08-16 16:06:53 -0700
committerantialias <antialias@leap.se>2012-08-16 16:06:53 -0700
commit451189d369f6661a67a1692945e68b5128cb9a65 (patch)
treed4ce0b1d8e210054f8b7247e7414e65192cdc9ac /src/leap/OpenVPNConnection.py
parent07ed489ed46140d6de814667ab3e64c6076f3776 (diff)
Cleaned up files and file names using the PEP 8 style guide.
Diffstat (limited to 'src/leap/OpenVPNConnection.py')
-rw-r--r--src/leap/OpenVPNConnection.py408
1 files changed, 0 insertions, 408 deletions
diff --git a/src/leap/OpenVPNConnection.py b/src/leap/OpenVPNConnection.py
deleted file mode 100644
index a26059a7..00000000
--- a/src/leap/OpenVPNConnection.py
+++ /dev/null
@@ -1,408 +0,0 @@
-"""
-OpenVPN Connection
-"""
-
-from __future__ import (print_function)
-import logging
-import os
-import socket
-import telnetlib
-import time
-from functools import partial
-
-logger = logging.getLogger(name=__name__)
-
-from leap.utils.coroutines import spawn_and_watch_process
-from leap.baseapp.config import get_config
-from leap.Connection import Connection
-
-class OpenVPNConnection(Connection):
- """
- All related to invocation
- of the openvpn binary
- """
- # Connection Methods
-
- def __init__(self, config_file=None, watcher_cb=None,host="/tmp/.eip.sock", port="unix", password=None):
- #XXX FIXME
- #change watcher_cb to line_observer
- """
- :param config_file: configuration file to read from
- :param watcher_cb: callback to be \
-called for each line in watched stdout
- :param signal_map: dictionary of signal names and callables \
-to be triggered for each one of them.
- :type config_file: str
- :type watcher_cb: function
- :type signal_map: dict
- """
-
- self.config_file = config_file
- self.watcher_cb = watcher_cb
- #self.signal_maps = signal_maps
-
- self.subp = None
- self.watcher = None
-
- self.server = None
- self.port = None
- self.proto = None
-
- self.autostart = True
-
- self._get_config()
-
- #Get this info from the Configuration Class
- #XXX hardcoded host here. change.
- self.host = host
- if isinstance(port, str) and port.isdigit():
- port = int(port)
- self.port = port
- self.password = password
- self.tn = None
-
- #XXX workaround for signaling
- #the ui that we don't know how to
- #manage a connection error
- self.with_errors = False
-
-
- def _set_command_mockup(self):
- """
- sets command and args for a command mockup
- that just mimics the output from the real thing
- """
- command, args = get_vpn_stdout_mockup()
- self.command, self.args = command, args
-
- def _get_config(self):
- """
- retrieves the config options from defaults or
- home file, or config file passed in command line.
- """
- config = get_config(config_file=self.config_file)
- self.config = config
-
- if config.has_option('openvpn', 'command'):
- commandline = config.get('openvpn', 'command')
- if commandline == "mockup":
- self._set_command_mockup()
- return
- command_split = commandline.split(' ')
- command = command_split[0]
- if len(command_split) > 1:
- args = command_split[1:]
- else:
- args = []
- self.command = command
- #print("debug: command = %s" % command)
- self.args = args
- else:
- self._set_command_mockup()
-
- if config.has_option('openvpn', 'autostart'):
- autostart = config.get('openvpn', 'autostart')
- self.autostart = autostart
-
- def _launch_openvpn(self):
- """
- invocation of openvpn binaries in a subprocess.
- """
- #XXX TODO:
- #deprecate watcher_cb,
- #use _only_ signal_maps instead
-
- if self.watcher_cb is not None:
- linewrite_callback = self.watcher_cb
- else:
- #XXX get logger instead
- linewrite_callback = lambda line: print('watcher: %s' % line)
-
- observers = (linewrite_callback,
- partial(self.status_watcher, self.status))
- subp, watcher = spawn_and_watch_process(
- self.command,
- self.args,
- observers=observers)
- self.subp = subp
- self.watcher = watcher
-
- conn_result = self.status.CONNECTED
- return conn_result
-
- def _try_connection(self):
- """
- attempts to connect
- """
- if self.subp is not None:
- print('cowardly refusing to launch subprocess again')
- return
- self._launch_openvpn()
-
- def cleanup(self):
- """
- terminates child subprocess
- """
- if self.subp:
- self.subp.terminate()
-
-
- #Here are the actual code to manage OpenVPN Connection
- #TODO: Look into abstraction them and moving them up into base class
- # this code based on code from cube-routed project
-
- """
- Run commands over OpenVPN management interface
- and parses the output.
- """
- # XXX might need a lock to avoid
- # race conditions here...
-
- def forget_errors(self):
- print('forgetting errors')
- self.with_errors = False
-
- def connect(self):
- """Connect to openvpn management interface"""
- try:
- self.close()
- except:
- #XXX don't like this general
- #catch here.
- pass
- if self.connected():
- return True
- self.tn = UDSTelnet(self.host, self.port)
-
- # XXX make password optional
- # specially for win plat. we should generate
- # the pass on the fly when invoking manager
- # from conductor
-
- #self.tn.read_until('ENTER PASSWORD:', 2)
- #self.tn.write(self.password + '\n')
- #self.tn.read_until('SUCCESS:', 2)
-
- self._seek_to_eof()
- self.forget_errors()
- return True
-
- def _seek_to_eof(self):
- """
- Read as much as available. Position seek pointer to end of stream
- """
- b = self.tn.read_eager()
- while b:
- b = self.tn.read_eager()
-
- def connected(self):
- """
- Returns True if connected
- rtype: bool
- """
- #return bool(getattr(self, 'tn', None))
- try:
- assert self.tn
- return True
- except:
- #XXX get rid of
- #this pokemon exception!!!
- return False
-
- def close(self, announce=True):
- """
- Close connection to openvpn management interface
- """
- if announce:
- self.tn.write("quit\n")
- self.tn.read_all()
- self.tn.get_socket().close()
- del self.tn
-
- def _send_command(self, cmd, tries=0):
- """
- Send a command to openvpn and return response as list
- """
- if tries > 3:
- return []
- if not self.connected():
- try:
- self.connect()
- except MissingSocketError:
- #XXX capture more helpful error
- #messages
- #pass
- return self.make_error()
- try:
- self.tn.write(cmd + "\n")
- except socket.error:
- logger.error('socket error')
- print('socket error!')
- self.close(announce=False)
- self._send_command(cmd, tries=tries + 1)
- return []
- buf = self.tn.read_until(b"END", 2)
- self._seek_to_eof()
- blist = buf.split('\r\n')
- if blist[-1].startswith('END'):
- del blist[-1]
- return blist
- else:
- return []
-
- def _send_short_command(self, cmd):
- """
- parse output from commands that are
- delimited by "success" instead
- """
- if not self.connected():
- self.connect()
- self.tn.write(cmd + "\n")
- # XXX not working?
- buf = self.tn.read_until(b"SUCCESS", 2)
- self._seek_to_eof()
- blist = buf.split('\r\n')
- return blist
-
- #
- # useful vpn commands
- #
-
- def pid(self):
- #XXX broken
- return self._send_short_command("pid")
-
- def make_error(self):
- """
- capture error and wrap it in an
- understandable format
- """
- #XXX get helpful error codes
- self.with_errors = True
- now = int(time.time())
- return '%s,LAUNCHER ERROR,ERROR,-,-' % now
-
- def state(self):
- """
- OpenVPN command: state
- """
- state = self._send_command("state")
- if not state:
- return None
- if isinstance(state, str):
- return state
- if isinstance(state, list):
- if len(state) == 1:
- return state[0]
- else:
- return state[-1]
-
- def status(self):
- """
- OpenVPN command: status
- """
- status = self._send_command("status")
- return status
-
- def status2(self):
- """
- OpenVPN command: last 2 statuses
- """
- return self._send_command("status 2")
-
- #
- # parse info
- #
-
- def get_status_io(self):
- status = self.status()
- if isinstance(status, str):
- lines = status.split('\n')
- if isinstance(status, list):
- lines = status
- try:
- (header, when, tun_read, tun_write,
- tcp_read, tcp_write, auth_read) = tuple(lines)
- except ValueError:
- return None
-
- when_ts = time.strptime(when.split(',')[1], "%a %b %d %H:%M:%S %Y")
- sep = ','
- # XXX cleanup!
- tun_read = tun_read.split(sep)[1]
- tun_write = tun_write.split(sep)[1]
- tcp_read = tcp_read.split(sep)[1]
- tcp_write = tcp_write.split(sep)[1]
- auth_read = auth_read.split(sep)[1]
-
- # XXX this could be a named tuple. prettier.
- return when_ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read)
-
- def get_connection_state(self):
- state = self.state()
- if state is not None:
- ts, status_step, ok, ip, remote = state.split(',')
- ts = time.gmtime(float(ts))
- # XXX this could be a named tuple. prettier.
- return ts, status_step, ok, ip, remote
-
- def status_watcher(self, cs, line):
- """
- a wrapper that calls to ConnectionStatus object
- :param cs: a EIPConnectionStatus instance
- :type cs: EIPConnectionStatus object
- :param line: a single line of the watched output
- :type line: str
- """
- #print('status watcher watching')
-
- # from the mullvad code, should watch for
- # things like:
- # "Initialization Sequence Completed"
- # "With Errors"
- # "Tap-Win32"
-
- if "Completed" in line:
- cs.change_to(cs.CONNECTED)
- return
-
- if "Initial packet from" in line:
- cs.change_to(cs.CONNECTING)
- return
-
-
-
-class MissingSocketError(Exception):
- pass
-
-
-class ConnectionRefusedError(Exception):
- pass
-
-class UDSTelnet(telnetlib.Telnet):
-
- def open(self, host, port=23, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
- """Connect to a host. If port is 'unix', it
- will open a connection over unix docmain sockets.
-
- The optional second argument is the port number, which
- defaults to the standard telnet port (23).
-
- Don't try to reopen an already connected instance.
- """
- self.eof = 0
- self.host = host
- self.port = port
- self.timeout = timeout
-
- if self.port == "unix":
- # unix sockets spoken
- if not os.path.exists(self.host):
- raise MissingSocketError
- self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- try:
- self.sock.connect(self.host)
- except socket.error:
- raise ConnectionRefusedError
- else:
- self.sock = socket.create_connection((host, port), timeout)