def import_ns(self, namespace): """ """ pythonshare._send(Request_ns(namespace), self._to_server) rv = pythonshare._recv(self._from_server) if isinstance(rv, Ns_rv) and rv.status: return True else: raise pythonshare.PythonShareError(rv.errormsg)
def _remote_execute(ns, exec_msg): rns = _g_remote_namespaces[ns] pythonshare._send(exec_msg, rns.to_remote) try: return pythonshare._recv(rns.from_remote) except AttributeError: # If another thread closes the connection between send/recv, # cPickle.load() may raise "'NoneType' has no attribute 'recv'". # Make this look like EOF (connection lost) raise EOFError()
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 pythonshare._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("r") self._to_server = self._s.makefile("w") 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 pythonshare._send(password, self._to_server) auth_rv = pythonshare._recv(self._from_server) try: auth_ok = auth_rv.success except AttributeError: auth_ok = False if not auth_ok: raise pythonshare.AuthenticationError("Permission denied")
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] pythonshare._send(exec_msg, rns.to_remote) from_remote = rns.from_remote # Must keep simultaneously two locks: # - send lock on to_client # - recv lock on from_remote pythonshare._acquire_recv_lock(from_remote) try: response = pythonshare._recv(from_remote, acquire_recv_lock=False) if not isinstance(response, messages.Data_info): # Got direct response without forward mode return (None, response) pythonshare._acquire_send_lock(to_client) if client_supports_rv_info: # send data_info to client pythonshare._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 = pythonshare._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: pythonshare._release_send_lock(to_client) finally: exec_msg.set_recv_cap_data_info(client_supports_rv_info) pythonshare._release_recv_lock(from_remote)
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("w") from_client = conn.makefile("r") 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 = pythonshare._recv(from_client) except Exception, 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: pythonshare._send(messages.Auth_rv(True), to_client) if opt_debug: daemon_log("%s:%s authentication ok" % peername) elif not received_password is None: pythonshare._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
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 pythonshare._acquire_recv_lock(from_remote) try: pythonshare._send(exec_msg, rns.to_remote) response = pythonshare._recv(from_remote, acquire_recv_lock=False) if not isinstance(response, messages.Data_info): # Got direct response without forward mode return (None, response) pythonshare._acquire_send_lock(to_client) if client_supports_rv_info: # send data_info to client pythonshare._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 = pythonshare._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: pythonshare._release_send_lock(to_client) finally: exec_msg.set_recv_cap_data_info(client_supports_rv_info) pythonshare._release_recv_lock(from_remote)
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. """ pythonshare._send(Drop_ns(namespace), self._to_server) rv = pythonshare._recv(self._from_server) if isinstance(rv, Ns_rv) and rv.status: return True else: raise pythonshare.PythonShareError(rv.errormsg)
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. """ pythonshare._send(Server_ctl("unlock", namespace), self._to_server) rv = pythonshare._recv(self._from_server) if isinstance(rv, Server_ctl_rv) and rv.status == 0: return True else: raise pythonshare.PythonShareError(rv.message)
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.) """ pythonshare._send(Register_ns(namespace), self._to_server) rv = pythonshare._recv(self._from_server) if isinstance(rv, Ns_rv) and rv.status: return True else: raise pythonshare.PythonShareError(rv.errormsg)
returned. The default is False. lock (boolean, optional) lock the namespace from others until this execution has finished. The default is True. Returns return value from expr or None. Raise RemoteExecError or RemoteEvalError if code or expr caused an exception in remote end, respectively. """ if namespace == None: namespace = self.namespace() try: pythonshare._send(Exec(namespace, code, expr, async=async, lock=lock), self._to_server) return self.make_local(pythonshare._recv(self._from_server)) except EOFError: raise pythonshare.PythonShareError( 'No connection to namespace "%s"' % (namespace,)) def eval_(self, expr, **kwargs): """Evaluate expr in the default namespace. See eval_in for optional parameters.""" return self.eval_in(self._ns, expr, **kwargs) def eval_in(self, namespace, expr, async=False, lock=True): """Evaluate expr in a namespace. Parameters:
_drop_remote_namespace(obj.ns) else: raise ValueError('Unknown namespace "%s"' % (obj.ns, )) pythonshare._send(messages.Ns_rv(True), to_client) except Exception, e: if opt_debug: daemon_log("namespace drop error: %s" % (e, )) pythonshare._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) pythonshare._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: exec_rv = _remote_execute(ns, obj) except (EOFError, socket.error): # connection lost 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:
auth_ok = True # no password required whitelist_local = conn_opts.get("whitelist_local", None) while auth_ok: try: obj = pythonshare._recv(from_client) if opt_debug: daemon_log("%s:%s => %s" % (peername + (obj,))) except (EOFError, pythonshare.socket.error): break if isinstance(obj, messages.Register_ns): try: _init_remote_namespace(obj.ns, conn, to_client, from_client) pythonshare._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, e: pythonshare._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,))
def _remote_execute(ns, exec_msg): rns = _g_remote_namespaces[ns] pythonshare._send(exec_msg, rns.to_remote) # _recv raises EOFError() if disconnected, # let it raise through. return pythonshare._recv(rns.from_remote)
elif obj.ns in _g_remote_namespaces: _drop_remote_namespace(obj.ns) else: raise ValueError('Unknown namespace "%s"' % (obj.ns,)) pythonshare._send(messages.Ns_rv(True), to_client) except Exception, e: if opt_debug: daemon_log("namespace drop error: %s" % (e,)) pythonshare._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) pythonshare._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: exec_rv = _remote_execute(ns, obj) except (EOFError, socket.error): # connection lost 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:
def kill_server(self, namespace=None): """Send server shutdown message""" if namespace == None: namespace = self.namespace() pythonshare._send(Server_ctl("die", namespace), self._to_server) return True
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("w") from_client = conn.makefile("r") 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 received_password = pythonshare._recv(from_client) 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: pythonshare._send(messages.Auth_rv(True), to_client) if opt_debug: daemon_log("%s:%s authentication ok" % peername) else: pythonshare._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 = pythonshare._recv(from_client) if opt_debug: daemon_log("%s:%s => %s" % (peername + (obj,))) except (EOFError, pythonshare.socket.error): break if isinstance(obj, messages.Register_ns): try: _init_remote_namespace(obj.ns, conn, to_client, from_client) pythonshare._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, e: pythonshare._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,)) pythonshare._send(messages.Ns_rv(True), to_client) except Exception, e: if opt_debug: daemon_log("namespace drop error: %s" % (e,)) pythonshare._send(messages.Ns_rv(False, exception2string(sys.exc_info())), to_client)
lock (boolean, optional) lock the namespace from others until this execution has finished. The default is True. Returns return value from expr or None. Raise RemoteExecError or RemoteEvalError if code or expr caused an exception in remote end, respectively. """ if namespace == None: namespace = self.namespace() pythonshare._check_hook("before:client.exec_in", {"code": code, "expr": expr, "namespace": namespace, "async": async, "lock": lock}) try: pythonshare._send(Exec( namespace, code, expr, async=async, lock=lock, recv_caps=pythonshare.messages.RECV_CAP_COMPRESSION), self._to_server) response = pythonshare._recv_with_info(self._from_server) return self.make_local(response) except EOFError: raise pythonshare.PythonShareError( 'No connection to namespace "%s"' % (namespace,)) def eval_(self, expr, **kwargs): """Evaluate expr in the default namespace. See eval_in for optional parameters.""" return self.eval_in(self._ns, expr, **kwargs) def eval_in(self, namespace, expr, async=False, lock=True):
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("w") from_client = conn.makefile("r") 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 received_password = pythonshare._recv(from_client) 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: pythonshare._send(messages.Auth_rv(True), to_client) if opt_debug: daemon_log("%s:%s authentication ok" % peername) else: pythonshare._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 = pythonshare._recv(from_client) if opt_debug: daemon_log("%s:%s => %s" % (peername + (obj, ))) except (EOFError, pythonshare.socket.error): break if isinstance(obj, messages.Register_ns): try: _init_remote_namespace(obj.ns, conn, to_client, from_client) pythonshare._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, e: pythonshare._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, )) pythonshare._send(messages.Ns_rv(True), to_client) except Exception, e: if opt_debug: daemon_log("namespace drop error: %s" % (e, )) pythonshare._send( messages.Ns_rv(False, exception2string(sys.exc_info())), to_client)