Esempio n. 1
0
 def import_ns(self, namespace):
     """
     """
     python3share._send(Request_ns(namespace), self._to_server)
     rv = python3share._recv(self._from_server)
     if isinstance(rv, Ns_rv) and rv.status:
         return True
     else:
         raise python3share.PythonShareError(rv.errormsg)
Esempio n. 2
0
def _remote_execute_and_forward(ns, exec_msg, to_client, peername=None):
    """returns (forward_status, info)
    forward_status values:
       True:  everything successfully forwarded,
              info contains pair (forwarded byte count, full length).
       False: not everything forwarded,
              info contains pair (forwarded byte count, full length).
              to_client file/socket is not functional.
       None:  no forwarding,
              info contains Exec_rv that should be sent normally.
    Raises EOFError if connection to remote namespace is not functional.

    The peername parameter is used for logging only.
    """
    client_supports_rv_info = exec_msg.recv_cap_data_info()
    exec_msg.set_recv_cap_data_info(True)
    rns = _g_remote_namespaces[ns]
    from_remote = rns.from_remote
    # Must keep simultaneously two locks:
    # - send lock on to_client
    # - recv lock on from_remote
    python3share._acquire_recv_lock(from_remote)
    try:
        python3share._send(exec_msg, rns.to_remote)
        response = python3share._recv(from_remote, acquire_recv_lock=False)
        if not isinstance(response, messages.Data_info):
            # Got direct response without forward mode
            return (None, response)
        python3share._acquire_send_lock(to_client)
        if client_supports_rv_info:
            # send data_info to client
            python3share._send(response, to_client, acquire_send_lock=False)
        try:
            if opt_debug and peername:
                daemon_log("%s:%s <= Exec_rv([forwarding %s B])" %
                           (peername + (response.data_length, )))
            forwarded_bytes = python3share._forward(from_remote,
                                                    to_client,
                                                    response.data_length,
                                                    acquire_recv_lock=False,
                                                    acquire_send_lock=False)
            if forwarded_bytes == response.data_length:
                return (True, (forwarded_bytes, response.data_length))
            else:
                return (False, (forwarded_bytes, response.data_length))
        finally:
            python3share._release_send_lock(to_client)
    finally:
        exec_msg.set_recv_cap_data_info(client_supports_rv_info)
        python3share._release_recv_lock(from_remote)
Esempio n. 3
0
    def unlock_ns(self, namespace):
        """Unlock namespace on the remote peer

        Parameters:

          namespace (string)
                  Namespace to be unlocked, can be local or
                  remote to the server.

        Returns True on success or raises an exception.
        """
        python3share._send(Server_ctl("unlock", namespace), self._to_server)
        rv = python3share._recv(self._from_server)
        if isinstance(rv, Server_ctl_rv) and rv.status == 0:
            return True
        else:
            raise python3share.PythonShareError(rv.message)
Esempio n. 4
0
    def drop_ns(self, namespace):
        """Delete namespace from the remote peer

        Parameters:

          namespace (string)
                  Namespace to be dropped, can be local or
                  remote to the server.

        Returns True on success or raises an exception.
        """
        python3share._send(Drop_ns(namespace), self._to_server)
        rv = python3share._recv(self._from_server)
        if isinstance(rv, Ns_rv) and rv.status:
            return True
        else:
            raise python3share.PythonShareError(rv.errormsg)
Esempio n. 5
0
    def export_ns(self, namespace):
        """Export namespace to remote peer

        Parameters:

          namespace (string)
                  Namespace to be exported, can be local or
                  remote to current host.

        Returns True on success or raises an exception.  If succeeded,
        this connection becomes a server for requests from remote
        peer. (The remote peer accesses registered namespace through
        this connection object.)
        """
        python3share._send(Register_ns(namespace), self._to_server)
        rv = python3share._recv(self._from_server)
        if isinstance(rv, Ns_rv) and rv.status:
            return True
        else:
            raise python3share.PythonShareError(rv.errormsg)
Esempio n. 6
0
def _serve_connection(conn, conn_opts):
    global _g_async_rv_counter
    global _g_server_shutdown
    if isinstance(conn, client.Connection):
        to_client = conn._to_server
        from_client = conn._from_server
    else:  # conn is a connected socket
        to_client = conn.makefile("wb")
        from_client = conn.makefile("rb")
    try:
        peername = conn.getpeername()
    except socket.error:
        peername = ("unknown", "?")
    if opt_debug:
        daemon_log("connected %s:%s" % peername)
    conn_id = "%s-%s" % (timestamp(), id(conn))
    auth_ok = False
    passwords = [k for k in conn_opts.keys() if k.startswith("password.")]
    kill_server_on_close = conn_opts.get("kill-server-on-close", False)
    if passwords:
        # password authentication is required for this connection
        try:
            received_password = python3share._recv(from_client)
        except Exception as e:
            daemon_log('error receiving password: %r' % (e, ))
            received_password = None
        for password_type in passwords:
            algorithm = password_type.split(".")[1]
            if type(received_password) == str:
                if (algorithm == "plaintext"
                        and received_password == conn_opts[password_type]):
                    auth_ok = True
                elif (hasattr(hashlib, algorithm) and getattr(
                        hashlib, algorithm)(received_password).hexdigest()
                      == conn_opts[password_type]):
                    auth_ok = True
        try:
            if auth_ok:
                python3share._send(messages.Auth_rv(True), to_client)
                if opt_debug:
                    daemon_log("%s:%s authentication ok" % peername)
            elif not received_password is None:
                python3share._send(messages.Auth_rv(False), to_client)
                if opt_debug:
                    daemon_log("%s:%s authentication failed" % peername)
        except socket.error:
            daemon_log("authentication failed due to socket error")
            auth_ok = False
    else:
        auth_ok = True  # no password required

    whitelist_local = conn_opts.get("whitelist_local", None)

    while auth_ok:
        try:
            obj = python3share._recv(from_client)
            if opt_debug:
                daemon_log("%s:%s => %s" % (peername + (obj, )))
        except (EOFError, python3share.socket.error):
            break

        if isinstance(obj, messages.Register_ns):
            try:
                _init_remote_namespace(obj.ns, conn, to_client, from_client)
                python3share._send(messages.Ns_rv(True), to_client)
                # from this point on, this connection is reserved for
                # sending remote namespace traffic. The connection will be
                # used by other threads, this thread stops here.
                return
            except Exception as e:
                python3share._send(
                    messages.Ns_rv(False, exception2string(sys.exc_info())),
                    to_client)

        elif isinstance(obj, messages.Drop_ns):
            try:
                if obj.ns in _g_local_namespaces:
                    _drop_local_namespace(obj.ns)
                elif obj.ns in _g_remote_namespaces:
                    _drop_remote_namespace(obj.ns)
                else:
                    raise ValueError('Unknown namespace "%s"' % (obj.ns, ))
                python3share._send(messages.Ns_rv(True), to_client)
            except Exception as e:
                if opt_debug:
                    daemon_log("namespace drop error: %s" % (e, ))
                python3share._send(
                    messages.Ns_rv(False, exception2string(sys.exc_info())),
                    to_client)

        elif isinstance(obj, messages.Request_ns):
            ns = obj.ns
            if (ns in _g_remote_namespaces or ns in _g_local_namespaces):
                _register_exported_namespace(ns, conn)
                python3share._send(messages.Ns_rv(True), to_client)
                # from this point on, this connection is reserved for
                # receiving executions on requested namespace. This
                # thread starts serving the connection.

        elif isinstance(obj, messages.Exec):
            ns = obj.namespace
            if ns in _g_remote_namespaces:  # execute in remote namespace
                try:
                    _fwd_status, _fwd_info = _remote_execute_and_forward(
                        ns, obj, to_client, peername)
                    if _fwd_status == True:
                        # successfully forwarded
                        if opt_debug:
                            daemon_log("%s:%s forwarded %s B" %
                                       (peername + (_fwd_info[0], )))
                        exec_rv = None  # return value fully forwarded
                    elif _fwd_status == False:
                        # connection to client is broken
                        if opt_debug:
                            daemon_log("%s:%s error after forwarding %s/%s B" %
                                       (peername + _fwd_info))
                        break
                    elif _fwd_status is None:
                        # nothing forwarded, send return value by normal means
                        exec_rv = _fwd_info
                except EOFError:
                    daemon_log('connection lost to "%s"' % (ns, ))
                    _drop_remote_namespace(ns)
                    break
            else:  # execute in local namespace
                if whitelist_local == None or ns in whitelist_local:
                    _init_local_namespace(ns)
                if getattr(obj, "async"):
                    # asynchronous execution, return handle (Async_rv)
                    _g_async_rv_counter += 1
                    rvid = datetime.datetime.now().strftime("%s.%f") + str(
                        _g_async_rv_counter)
                    exec_rv = messages.Async_rv(ns, rvid)
                    _g_async_rvs[ns][rvid] = python3share.InProgress()
                    _thread.start_new_thread(_local_async_execute,
                                             (exec_rv, obj))
                else:
                    # synchronous execution, return true return value
                    exec_rv = _local_execute(obj, conn_id)
            if not exec_rv is None:
                if opt_debug:
                    daemon_log("%s:%s <= %s" % (peername + (exec_rv, )))
                try:
                    try:
                        if obj.recv_cap_data_info():
                            info = python3share._send_opt(
                                exec_rv, to_client, obj.recv_caps)
                            if info:
                                sent_info = " %s B, format:%s" % (
                                    info.data_length, info.data_format)
                            else:
                                sent_info = ""
                        else:
                            python3share._send(exec_rv, to_client)
                            sent_info = ""
                        if opt_debug:
                            daemon_log("%s:%s sent%s" % (peername +
                                                         (sent_info, )))
                    except (EOFError, socket.error):
                        break
                except (TypeError, ValueError,
                        pickle.PicklingError):  # pickling rv fails
                    exec_rv.expr_rv = messages.Unpicklable(exec_rv.expr_rv)
                    try:
                        python3share._send(exec_rv, to_client)
                    except (EOFError, socket.error):
                        break

        elif isinstance(obj, messages.Server_ctl):
            if obj.command == "die":
                ns = obj.args[0]
                if ns in _g_remote_namespaces:
                    try:
                        rv = _remote_execute(ns, obj)
                        if opt_debug:
                            daemon_log("%s:%s <= %s" % (peername + (rv, )))
                        python3share._send(rv, to_client)
                    except (EOFError, socket.error):  # connection lost
                        daemon_log('connection lost to "%s"' % (ns, ))
                        _drop_remote_namespace(ns)
                        break
                else:
                    _g_server_shutdown = True
                    server_ctl_rv = messages.Server_ctl_rv(0, "shutting down")
                    python3share._send(server_ctl_rv, to_client)
                    if _g_wake_server_function:
                        _g_wake_server_function()
                    break
            elif obj.command == "unlock":
                try:
                    ns = obj.args[0]
                    if ns in _g_remote_namespaces:
                        try:
                            rv = _remote_execute(ns, obj)
                        except (EOFError, socket.error):  # connection lost
                            daemon_log('connection lost to "%s"' % (ns, ))
                            _drop_remote_namespace(ns)
                            break
                    elif ns in _g_local_namespace_locks:
                        try:
                            _g_local_namespace_locks[ns].release()
                            server_ctl_rv = messages.Server_ctl_rv(
                                0, "%s unlocked" % (repr(ns), ))
                        except _thread.error as e:
                            server_ctl_rv = messages.Server_ctl_rv(
                                1, "%s already unlocked" % (repr(ns), ))
                    elif ns in _g_local_namespaces:
                        server_ctl_rv = messages.Server_ctl_rv(
                            2, "namespace %s is not locked" % (repr(ns), ))
                    else:
                        server_ctl_rv = messages.Server_ctl_rv(
                            -1, "unknown namespace %s" % (repr(ns), ))
                    if opt_debug:
                        daemon_log("%s:%s <= %s" % (peername +
                                                    (server_ctl_rv, )))
                    python3share._send(server_ctl_rv, to_client)
                except Exception as e:
                    if opt_debug:
                        daemon_log("Exception in handling %s: %s" % (obj, e))
        else:
            daemon_log("unknown message type: %s in %s" % (type(obj), obj))
            python3share._send(messages.Auth_rv(False), to_client)
            auth_ok = False
    if opt_debug:
        daemon_log("disconnected %s:%s" % peername)
    _connection_lost(conn_id, to_client, from_client, conn)
    if kill_server_on_close:
        _g_server_shutdown = True
        if _g_wake_server_function:
            _g_wake_server_function()
Esempio n. 7
0
def _remote_execute(ns, exec_msg):
    rns = _g_remote_namespaces[ns]
    python3share._send(exec_msg, rns.to_remote)
    # _recv raises EOFError() if disconnected,
    # let it raise through.
    return python3share._recv(rns.from_remote)
Esempio n. 8
0
    def __init__(self,
                 host_or_from_server,
                 port_or_to_server,
                 password=None,
                 namespace="default"):
        """Connect to a pythonshare server

        The server is listening to connections at host:port, or it can be
        communicated via file-like objects from_server and to_server.

        Parameters:

          host_or_from_server (string or file-like object)
                  string: host
                  file: file for receiving messages from the server

          port_or_to_server (int or file-like object)
                  int: port number
                  file: file for sending messages to the server

          password (string, optional)
                  server password. The default is None, that is,
                  do not send password to the server when connecting.

          namespace (string, optional)
                  the default namespace that is used on eval_() and exec_().
                  The default is "default".
        """
        self.set_namespace(namespace)

        if isinstance(host_or_from_server, str) and isinstance(
                port_or_to_server, int):
            host = host_or_from_server
            port = port_or_to_server
            python3share._check_hook("before:client.socket.connect", {
                "host": host,
                "port": port
            })
            self._s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self._s.connect((host, port))
            self._from_server = self._s.makefile("rb")
            self._to_server = self._s.makefile("wb")
        elif isinstance(host_or_from_server, file) and isinstance(
                port_or_to_server, file):
            self._s = None
            self._to_server = port_or_to_server
            self._from_server = host_or_from_server
        else:
            raise ValueError(
                "invalid host:port (str:int) or to_server:from_server (file:file)"
            )

        if password:
            # authenticate to server
            python3share._send(password, self._to_server)
            auth_rv = python3share._recv(self._from_server)
            try:
                auth_ok = auth_rv.success
            except AttributeError:
                auth_ok = False
            if not auth_ok:
                raise python3share.AuthenticationError("Permission denied")