def recv_into(self, buffer, nbytes=0, flags=0): """recv_into(buffer, [nbytes[, flags]]) -> nbytes_read A version of recv() that stores its data into a buffer rather than creating a new string. Receive up to buffersize bytes from the socket. If buffersize is not specified (or 0), receive up to the size available in the given buffer. See recv() for documentation about the flags.""" if nbytes == 0: # len returns the wrong thing for a resized ctypes buffer. But # ctypes.sizeof doesn't work on all buffery objects that len does. # So try both, in order of preference. try: nbytes = sizeof(buffer) except TypeError as e: nbytes = len(buffer) cbuf = (c_uint8 * nbytes).from_buffer(buffer) rx = efi.EFI_TCP4_RECEIVE_DATA() rx.DataLength = nbytes rx.FragmentCount = 1 rx.FragmentTable[0].FragmentLength = nbytes rx.FragmentTable[0].FragmentBuffer = addressof(cbuf) e = efi.event_signal() token = efi.EFI_TCP4_IO_TOKEN() token.CompletionToken.Event = e.event token.Packet.RxData = pointer(rx) efi.check_status(self._tcp4.Receive(self._tcp4, byref(token))) self._wait(e, token.CompletionToken) return rx.DataLength
def sendall(self, data, flags=0): """sendall(data[, flags]) Send a data string to the socket. For the optional flags argument, see the Unix manual. This calls send() repeatedly until all data is sent. If an error occurs, it's impossible to tell how much data has been sent.""" if isinstance(data, memoryview): data = data.tobytes() # ctypes can't handle memoryview directly data = (c_uint8 * len(data)).from_buffer_copy(data) tx = efi.EFI_TCP4_TRANSMIT_DATA() tx.DataLength = len(data) tx.FragmentCount = 1 tx.FragmentTable[0].FragmentLength = len(data) tx.FragmentTable[0].FragmentBuffer = cast(data, c_void_p) token = efi.EFI_TCP4_IO_TOKEN() send_status = [] def callback(): _ = data, tx # Reference objects EFI will access, to keep them alive send_status.append(token.CompletionToken.Status) self._events.close_event(efi.EFI_EVENT(token.CompletionToken.Event)) token.CompletionToken.Event = self._events.create_event(callback, abort=self._abort) token.Packet.TxData = pointer(tx) status = self._tcp4.Transmit(self._tcp4, byref(token)) if status: self._events.close_event(efi.EFI_EVENT(token.CompletionToken.Event)) efi.check_status(status) return while not send_status: self._poll() efi.check_status(send_status[0])
def __del__(self): global _tcp4sbp # Only clean up if __init__ finished and we have a protocol to destroy if hasattr(self, "_tcp4"): self._abort() efi.check_status( _tcp4sbp.DestroyChild(_tcp4sbp, self._tcp4._handle))
def _get_config(self): tcp4_state = efi.EFI_TCP4_CONNECTION_STATE() tcp4_config_data = efi.EFI_TCP4_CONFIG_DATA() ip4_mode_data = efi.EFI_IP4_MODE_DATA() mnp_config_data = efi.EFI_MANAGED_NETWORK_CONFIG_DATA() snp_mode_data = efi.EFI_SIMPLE_NETWORK_MODE() efi.check_status(self._tcp4.GetModeData(self._tcp4, byref(tcp4_state), byref(tcp4_config_data), byref(ip4_mode_data), byref(mnp_config_data), byref(snp_mode_data))) return tcp4_config_data
def _abort(self): if self._aborted: return # This cancels any outstanding completion tokens, to avoid accesses to # memory or events that we're about to free. efi.check_status(self._tcp4.Configure(self._tcp4, None)) self._events.close_all() self._aborted = True
def _close(self, abort=True): if hasattr(self, "closed") and self.closed: return e = efi.event_signal() token = efi.EFI_TCP4_CLOSE_TOKEN() token.CompletionToken.Event = e.event token.AbortOnClose = abort efi.check_status(self._tcp4.Close(self._tcp4, byref(token))) self._wait(e, token.CompletionToken) self.closed = True
def _init_sockets_ip4cp(): global _ip4cp, _done_event, _reconfig_event, _initialized ip4cp_get_configuration(early=True) if _initialized: return _done_event = efi.event_signal() _reconfig_event = efi.event_signal(abort=_ip4cp_stop_config) efi.check_status(_ip4cp.Start(_ip4cp, _done_event.event, _reconfig_event.event)) while not _done_event.signaled: pass ip4cp_get_configuration()
def _init_sockets_ip4cp(): global _ip4cp, _done_event, _reconfig_event, _initialized ip4cp_get_configuration(early=True) if _initialized: return _done_event = efi.event_signal() _reconfig_event = efi.event_signal(abort=_ip4cp_stop_config) efi.check_status( _ip4cp.Start(_ip4cp, _done_event.event, _reconfig_event.event)) while not _done_event.signaled: pass ip4cp_get_configuration()
def _get_config(self): tcp4_state = efi.EFI_TCP4_CONNECTION_STATE() tcp4_config_data = efi.EFI_TCP4_CONFIG_DATA() ip4_mode_data = efi.EFI_IP4_MODE_DATA() mnp_config_data = efi.EFI_MANAGED_NETWORK_CONFIG_DATA() snp_mode_data = efi.EFI_SIMPLE_NETWORK_MODE() efi.check_status( self._tcp4.GetModeData(self._tcp4, byref(tcp4_state), byref(tcp4_config_data), byref(ip4_mode_data), byref(mnp_config_data), byref(snp_mode_data))) return tcp4_config_data
def accept(self): """accept() -> (socket object, address info) Wait for an incoming connection. Return a new socket representing the connection, and the address of the client. For IP sockets, the address info is a pair (hostaddr, port).""" e = efi.event_signal() token = efi.EFI_TCP4_LISTEN_TOKEN() token.CompletionToken.Event = e.event efi.check_status(self._tcp4.Accept(self._tcp4, byref(token))) self._wait(e, token.CompletionToken) s = socket(family=self.family, type=self.type, proto=self.proto, _handle=token.NewChildHandle) return s, s.getpeername()
def _wait(self, es, ct): """Spin until the specified completion token completes or timeout elapses es is an efi.event_signal; ct is a completion token. If timeout elapses, raises socket.timeout. If the token completes, checks ct.Status for errors.""" if self.timeout < 0.0: while not es.status: efi.check_status(self._tcp4.Poll(self._tcp4)) else: start = time.time() attempt_cancel = True while not es.status: if attempt_cancel and (time.time() - start >= self.timeout): status = self._tcp4.Cancel(self._tcp4, byref(ct)) if status == efi.EFI_NOT_FOUND: pass # Keep waiting for es.status elif status == efi.EFI_UNSUPPORTED: print("Warning: socket timeout expired but EFI can't Cancel; ignoring timeout") attempt_cancel = False else: efi.check_status(status) raise timeout("timed out") efi.check_status(self._tcp4.Poll(self._tcp4)) efi.check_status(ct.Status)
def listen(self, backlog): """listen(backlog) Enable a server to accept connections. The backlog argument must be at least 0 (if it is lower, it is set to 0); it specifies the number of unaccepted connections that the system will allow before refusing new connections.""" # FIXME: Use queue depth as MaxSynBackLog global _subnet_mask, _routes if self._connect_status is not None: raise error("listen() called after connect()") if backlog < 0: backlog = 0 data = efi.EFI_TCP4_CONFIG_DATA() data.TypeOfService = 0 data.TimeToLive = 60 # UseDefaultAddress = True fails with EFI_ALREADY_STARTED, but using # the previously obtained address works. The UEFI 2.5 specification # does not explain this behavior or document this error code as a # possible return from Configure. data.AccessPoint.UseDefaultAddress = False # Use the local IP and port from bind if set try: # Special-case 0.0.0.0 because the EFI IP stack doesn't handle it if self._bind_ip == efi.EFI_IPv4_ADDRESS((0, 0, 0, 0)): data.AccessPoint.StationAddress = _ip_address else: data.AccessPoint.StationAddress = self._bind_ip data.AccessPoint.StationPort = self._bind_port except AttributeError as e: data.AccessPoint.StationAddress = _ip_address data.AccessPoint.StationPort = 0 data.AccessPoint.SubnetMask = _subnet_mask data.AccessPoint.ActiveFlag = False efi.check_status(self._tcp4.Configure(self._tcp4, byref(data))) # Contradicting the UEFI 2.5 specification, the EFI_TCP4_PROTOCOL does # not automatically use all of the underlying IP4 routes. Add them # manually, but ignore any failure caused by already having the route. for route in _routes: status = self._tcp4.Routes(self._tcp4, False, byref(route.SubnetAddress), byref(route.SubnetMask), byref(route.GatewayAddress)) if status != efi.EFI_ACCESS_DENIED: efi.check_status(status) self._is_listen_socket = True
def listen(self, backlog): """listen(backlog) Enable a server to accept connections. The backlog argument must be at least 0 (if it is lower, it is set to 0); it specifies the number of unaccepted connections that the system will allow before refusing new connections.""" # FIXME: Use queue depth as MaxSynBackLog global _subnet_mask, _routes if self._connect_status is not None: raise error("listen() called after connect()") if backlog < 0: backlog = 0 data = efi.EFI_TCP4_CONFIG_DATA() data.TypeOfService = 0 data.TimeToLive = 60 # UseDefaultAddress = True fails with EFI_ALREADY_STARTED, but using # the previously obtained address works. The UEFI 2.5 specification # does not explain this behavior or document this error code as a # possible return from Configure. data.AccessPoint.UseDefaultAddress = False # Use the local IP and port from bind if set try: # Special-case 0.0.0.0 because the EFI IP stack doesn't handle it if self._bind_ip == efi.EFI_IPv4_ADDRESS((0, 0, 0, 0)): data.AccessPoint.StationAddress = _ip_address else: data.AccessPoint.StationAddress = self._bind_ip data.AccessPoint.StationPort = self._bind_port except AttributeError as e: data.AccessPoint.StationAddress = _ip_address data.AccessPoint.StationPort = 0 data.AccessPoint.SubnetMask = _subnet_mask data.AccessPoint.ActiveFlag = False efi.check_status(self._tcp4.Configure(self._tcp4, byref(data))) # Contradicting the UEFI 2.5 specification, the EFI_TCP4_PROTOCOL does # not automatically use all of the underlying IP4 routes. Add them # manually, but ignore any failure caused by already having the route. for route in _routes: status = self._tcp4.Routes( self._tcp4, False, byref(route.SubnetAddress), byref(route.SubnetMask), byref(route.GatewayAddress) ) if status != efi.EFI_ACCESS_DENIED: efi.check_status(status) self._is_listen_socket = True
def recv_into(self, buffer, nbytes=0, flags=0): """recv_into(buffer, [nbytes[, flags]]) -> nbytes_read A version of recv() that stores its data into a buffer rather than creating a new string. Receive up to buffersize bytes from the socket. If buffersize is not specified (or 0), receive up to the size available in the given buffer. See recv() for documentation about the flags.""" if nbytes == 0: # len returns the wrong thing for a resized ctypes buffer. But # ctypes.sizeof doesn't work on all buffery objects that len does. # So try both, in order of preference. try: nbytes = sizeof(buffer) except TypeError as e: nbytes = len(buffer) start = time.time() while not self._read_ready(): if self.timeout >= 0 and (time.time() - start >= self.timeout): raise timeout(11, "timed out") # EAGAIN nbytes_read = 0 dest = (c_uint8 * nbytes).from_buffer(buffer) while self._recv_queue and nbytes_read != nbytes: src = self._recv_queue[0] if isinstance(src, (int, long)): # Error; return it if we haven't yet collected any data if nbytes_read: break efi.check_status(src) elif nbytes_read + sizeof(src) > nbytes: # Split src nbytes_to_copy = nbytes - nbytes_read memmove( addressof(dest) + nbytes_read, addressof(src), nbytes_to_copy) newsrc = create_string_buffer(sizeof(src) - nbytes_to_copy) memmove(addressof(newsrc), addressof(src) + nbytes_to_copy, sizeof(newsrc)) self._recv_queue[0] = newsrc nbytes_read = nbytes else: # Copy the entire src memmove( addressof(dest) + nbytes_read, addressof(src), sizeof(src)) self._recv_queue.pop(0) nbytes_read += sizeof(src) return nbytes_read
def ip4cp_get_configuration(early=False): global _ip4cp, _ip_address, _subnet_mask, _routes, _initialized data = efi.EFI_IP4_IPCONFIG_DATA() size = efi.UINTN(sizeof(data)) status = _ip4cp.GetData(_ip4cp, byref(size), byref(data)) if status == efi.EFI_BUFFER_TOO_SMALL: resize(data, size.value) status = _ip4cp.GetData(_ip4cp, byref(size), byref(data)) if early and status in (efi.EFI_NOT_STARTED, efi.EFI_NOT_READY): return efi.check_status(status) if data.StationAddress == efi.EFI_IPv4_ADDRESS((0, 0, 0, 0)): return _ip_address = data.StationAddress _subnet_mask = data.SubnetMask _routes = [efi.EFI_IP4_ROUTE_TABLE.from_buffer_copy(data.RouteTable[i]) for i in range(data.RouteTableSize)] _initialized = True
def accept(self): """accept() -> (socket object, address info) Wait for an incoming connection. Return a new socket representing the connection, and the address of the client. For IP sockets, the address info is a pair (hostaddr, port).""" if not self._is_listen_socket: raise error("accept() called without listen()") start = time.time() while not self._read_ready(): if self.timeout >= 0 and (time.time() - start >= self.timeout): raise timeout(11, "timed out") # EAGAIN success, value = self._accept_queue.pop(0) if success: s = socket(family=self.family, type=self.type, proto=self.proto, _handle=value) return s, s.getpeername() else: efi.check_status(value)
def _start_config(): global _configuration_started, _ip4cp, _tcp4sbp, _done_event, _reconfig_event if _configuration_started: return handles = list(efi.locate_handles(efi.EFI_IP4_CONFIG_PROTOCOL_GUID)) if not handles: raise IOError("EFI_IP4_CONFIG_PROTOCOL not available") _ip4cp = efi.EFI_IP4_CONFIG_PROTOCOL.from_handle(handles[0]) handles = list(efi.locate_handles(efi.EFI_TCP4_SERVICE_BINDING_PROTOCOL_GUID)) if not handles: raise IOError("EFI_TCP4_SERVICE_BINDING_PROTOCOL not available") _tcp4sbp = efi.EFI_TCP4_SERVICE_BINDING_PROTOCOL.from_handle(handles[0]) _done_event = efi.event_signal() _reconfig_event = efi.event_signal(abort=_stop_config) efi.check_status(_ip4cp.Start(_ip4cp, _done_event.event, _reconfig_event.event)) print("IP configuration started") _configuration_started = True
def close(self): """"close() Close the socket. It cannot be used after this call.""" if hasattr(self, "closed") and self.closed: return token = efi.EFI_TCP4_CLOSE_TOKEN() def callback(): if token.CompletionToken.Status: print("EFI_TCP4_PROTOCOL Close completed with an error:") print(efi.EFIException(token.CompletionToken.Status)) self._events.close_event(efi.EFI_EVENT(token.CompletionToken.Event)) token.CompletionToken.Event = self._events.create_event(callback, abort=self._abort) token.AbortOnClose = False status = self._tcp4.Close(self._tcp4, byref(token)) if status: self._events.close_event(efi.EFI_EVENT(token.CompletionToken.Event)) efi.check_status(status) self.closed = True
def recv_into(self, buffer, nbytes=0, flags=0): """recv_into(buffer, [nbytes[, flags]]) -> nbytes_read A version of recv() that stores its data into a buffer rather than creating a new string. Receive up to buffersize bytes from the socket. If buffersize is not specified (or 0), receive up to the size available in the given buffer. See recv() for documentation about the flags.""" if nbytes == 0: # len returns the wrong thing for a resized ctypes buffer. But # ctypes.sizeof doesn't work on all buffery objects that len does. # So try both, in order of preference. try: nbytes = sizeof(buffer) except TypeError as e: nbytes = len(buffer) start = time.time() while not self._read_ready(): if self.timeout >= 0 and (time.time() - start >= self.timeout): raise timeout(11, "timed out") # EAGAIN nbytes_read = 0 dest = (c_uint8 * nbytes).from_buffer(buffer) while self._recv_queue and nbytes_read != nbytes: src = self._recv_queue[0] if isinstance(src, (int, long)): # Error; return it if we haven't yet collected any data if nbytes_read: break efi.check_status(src) elif nbytes_read + sizeof(src) > nbytes: # Split src nbytes_to_copy = nbytes - nbytes_read memmove(addressof(dest) + nbytes_read, addressof(src), nbytes_to_copy) newsrc = create_string_buffer(sizeof(src) - nbytes_to_copy) memmove(addressof(newsrc), addressof(src) + nbytes_to_copy, sizeof(newsrc)) self._recv_queue[0] = newsrc nbytes_read = nbytes else: # Copy the entire src memmove(addressof(dest) + nbytes_read, addressof(src), sizeof(src)) self._recv_queue.pop(0) nbytes_read += sizeof(src) return nbytes_read
def _init_sockets(): global _initialized, _ip4cp, _done_event, _ip_address, _subnet_mask, _routes if _initialized: return _start_config() # Spin until configuration complete while not _done_event.signaled: pass data = efi.EFI_IP4_IPCONFIG_DATA() size = efi.UINTN(sizeof(data)) status = _ip4cp.GetData(_ip4cp, byref(size), byref(data)) if status == efi.EFI_BUFFER_TOO_SMALL: resize(data, size.value) status = _ip4cp.GetData(_ip4cp, byref(size), byref(data)) efi.check_status(status) _ip_address = data.StationAddress _subnet_mask = data.SubnetMask _routes = [efi.EFI_IP4_ROUTE_TABLE.from_buffer_copy(data.RouteTable[i]) for i in range(data.RouteTableSize)] print("IP configuration complete: {}/{}".format(data.StationAddress, data.SubnetMask)) _initialized = True
def ip4cp_get_configuration(early=False): global _ip4cp, _ip_address, _subnet_mask, _routes, _initialized data = efi.EFI_IP4_IPCONFIG_DATA() size = efi.UINTN(sizeof(data)) status = _ip4cp.GetData(_ip4cp, byref(size), byref(data)) if status == efi.EFI_BUFFER_TOO_SMALL: resize(data, size.value) status = _ip4cp.GetData(_ip4cp, byref(size), byref(data)) if early and status in (efi.EFI_NOT_STARTED, efi.EFI_NOT_READY): return efi.check_status(status) if data.StationAddress == efi.EFI_IPv4_ADDRESS((0, 0, 0, 0)): return _ip_address = data.StationAddress _subnet_mask = data.SubnetMask _routes = [ efi.EFI_IP4_ROUTE_TABLE.from_buffer_copy(data.RouteTable[i]) for i in range(data.RouteTableSize) ] _initialized = True
def sendall(self, data, flags=0): """sendall(data[, flags]) Send a data string to the socket. For the optional flags argument, see the Unix manual. This calls send() repeatedly until all data is sent. If an error occurs, it's impossible to tell how much data has been sent.""" tx = efi.EFI_TCP4_TRANSMIT_DATA() tx.DataLength = len(data) tx.FragmentCount = 1 tx.FragmentTable[0].FragmentLength = len(data) if isinstance(data, memoryview): data = data.tobytes() ptr = c_char_p(data) tx.FragmentTable[0].FragmentBuffer = cast(ptr, c_void_p) e = efi.event_signal() token = efi.EFI_TCP4_IO_TOKEN() token.CompletionToken.Event = e.event token.Packet.TxData = pointer(tx) efi.check_status(self._tcp4.Transmit(self._tcp4, byref(token))) self._wait(e, token.CompletionToken)
def get_key(): global stiex import efi import ctypes if stiex is None: stiex = efi.EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.from_handle(efi.system_table.ConsoleInHandle) key_data = efi.EFI_KEY_DATA() while True: ret = stiex.ReadKeyStrokeEx(stiex, ctypes.byref(key_data)) if ret == efi.EFI_NOT_READY: continue efi.check_status(ret) mod = 0 if key_data.KeyState.KeyShiftState & efi.EFI_SHIFT_STATE_VALID: mod = key_data.KeyState.KeyShiftState shift=bool(mod & (efi.EFI_LEFT_SHIFT_PRESSED | efi.EFI_RIGHT_SHIFT_PRESSED)) ctrl=bool(mod & (efi.EFI_LEFT_CONTROL_PRESSED | efi.EFI_RIGHT_CONTROL_PRESSED)) alt=bool(mod & (efi.EFI_LEFT_ALT_PRESSED | efi.EFI_RIGHT_ALT_PRESSED)) if key_data.Key.UnicodeChar != '\x00': key = key_data.Key.UnicodeChar else: key = key_data.Key.ScanCode return KEY(key, shift, ctrl, alt)
def sendall(self, data, flags=0): """sendall(data[, flags]) Send a data string to the socket. For the optional flags argument, see the Unix manual. This calls send() repeatedly until all data is sent. If an error occurs, it's impossible to tell how much data has been sent.""" if isinstance(data, memoryview): data = data.tobytes() # ctypes can't handle memoryview directly data = (c_uint8 * len(data)).from_buffer_copy(data) tx = efi.EFI_TCP4_TRANSMIT_DATA() tx.DataLength = len(data) tx.FragmentCount = 1 tx.FragmentTable[0].FragmentLength = len(data) tx.FragmentTable[0].FragmentBuffer = cast(data, c_void_p) token = efi.EFI_TCP4_IO_TOKEN() send_status = [] def callback(): _ = data, tx # Reference objects EFI will access, to keep them alive send_status.append(token.CompletionToken.Status) self._events.close_event(efi.EFI_EVENT( token.CompletionToken.Event)) token.CompletionToken.Event = self._events.create_event( callback, abort=self._abort) token.Packet.TxData = pointer(tx) status = self._tcp4.Transmit(self._tcp4, byref(token)) if status: self._events.close_event(efi.EFI_EVENT( token.CompletionToken.Event)) efi.check_status(status) return while not send_status: self._poll() efi.check_status(send_status[0])
def close(self): """"close() Close the socket. It cannot be used after this call.""" if hasattr(self, "closed") and self.closed: return token = efi.EFI_TCP4_CLOSE_TOKEN() def callback(): if token.CompletionToken.Status: print("EFI_TCP4_PROTOCOL Close completed with an error:") print(efi.EFIException(token.CompletionToken.Status)) self._events.close_event(efi.EFI_EVENT( token.CompletionToken.Event)) token.CompletionToken.Event = self._events.create_event( callback, abort=self._abort) token.AbortOnClose = False status = self._tcp4.Close(self._tcp4, byref(token)) if status: self._events.close_event(efi.EFI_EVENT( token.CompletionToken.Event)) efi.check_status(status) self.closed = True
def _init_sockets_ip4c2p(): global _ip4c2p, _initialized ip4c2p_get_configuration(early=True) if _initialized: return # To trigger DHCP, we need to change the policy to DHCP; if already set to # DHCP, we set it to static and then back to DHCP. data = efi.EFI_IP4_CONFIG2_POLICY() size = efi.UINTN(sizeof(data)) efi.check_status(_ip4c2p.GetData(_ip4c2p, efi.Ip4Config2DataTypePolicy, byref(size), byref(data))) if data.value == efi.Ip4Config2PolicyDhcp: data.value = efi.Ip4Config2PolicyStatic efi.check_status(_ip4c2p.SetData(_ip4c2p, efi.Ip4Config2DataTypePolicy, sizeof(data), byref(data))) data.value = efi.Ip4Config2PolicyDhcp status = _ip4c2p.SetData(_ip4c2p, efi.Ip4Config2DataTypePolicy, sizeof(data), byref(data)) if status != efi.EFI_NOT_READY: efi.check_status(status) while not _initialized: ip4c2p_get_configuration()
def connect(self, addr): """connect(address) Connect the socket to a remote address. For IP sockets, the address is a pair (host, port).""" global _ip_address, _subnet_mask, _routes host, port = addr ip = efi.EFI_IPv4_ADDRESS.from_buffer_copy(inet_aton(gethostbyname(host))) data = efi.EFI_TCP4_CONFIG_DATA() data.TypeOfService = 0 data.TimeToLive = 60 # UseDefaultAddress = True fails with EFI_ALREADY_STARTED, but using # the previously obtained address works. The UEFI 2.5 specification # does not explain this behavior or document this error code as a # possible return from Configure. data.AccessPoint.UseDefaultAddress = False # Use the local IP and port from bind if set try: data.AccessPoint.StationAddress = self._bind_ip data.AccessPoint.StationPort = self._bind_port except AttributeError as e: data.AccessPoint.StationAddress = _ip_address data.AccessPoint.StationPort = 0 data.AccessPoint.SubnetMask = _subnet_mask data.AccessPoint.RemoteAddress = ip data.AccessPoint.RemotePort = port data.AccessPoint.ActiveFlag = True efi.check_status(self._tcp4.Configure(self._tcp4, byref(data))) # Contradicting the UEFI 2.5 specification, the EFI_TCP4_PROTOCOL does # not automatically use all of the underlying IP4 routes. Add them # manually, but ignore any failure caused by already having the route. for route in _routes: status = self._tcp4.Routes(self._tcp4, False, byref(route.SubnetAddress), byref(route.SubnetMask), byref(route.GatewayAddress)) if status != efi.EFI_ACCESS_DENIED: efi.check_status(status) e = efi.event_signal() token = efi.EFI_TCP4_CONNECTION_TOKEN() token.CompletionToken.Event = e.event efi.check_status(self._tcp4.Connect(self._tcp4, byref(token))) self._wait(e, token.CompletionToken)
def _init_sockets_ip4c2p(): global _ip4c2p, _initialized ip4c2p_get_configuration(early=True) if _initialized: return # To trigger DHCP, we need to change the policy to DHCP; if already set to # DHCP, we set it to static and then back to DHCP. data = efi.EFI_IP4_CONFIG2_POLICY() size = efi.UINTN(sizeof(data)) efi.check_status( _ip4c2p.GetData(_ip4c2p, efi.Ip4Config2DataTypePolicy, byref(size), byref(data))) if data.value == efi.Ip4Config2PolicyDhcp: data.value = efi.Ip4Config2PolicyStatic efi.check_status( _ip4c2p.SetData(_ip4c2p, efi.Ip4Config2DataTypePolicy, sizeof(data), byref(data))) data.value = efi.Ip4Config2PolicyDhcp status = _ip4c2p.SetData(_ip4c2p, efi.Ip4Config2DataTypePolicy, sizeof(data), byref(data)) if status != efi.EFI_NOT_READY: efi.check_status(status) while not _initialized: ip4c2p_get_configuration()
def __del__(self): global _tcp4sbp # Only clean up if __init__ finished and we have a protocol to destroy if hasattr(self, "_tcp4"): self._abort() efi.check_status(_tcp4sbp.DestroyChild(_tcp4sbp, self._tcp4._handle))
def _poll(self): status = self._tcp4.Poll(self._tcp4) if status == efi.EFI_NOT_READY: return efi.check_status(status)
def connect(self, addr): """connect(address) Connect the socket to a remote address. For IP sockets, the address is a pair (host, port).""" global _ip_address, _subnet_mask, _routes if self._is_listen_socket: raise error("connect() called after listen()") if self._connect_status is not None: if self._connect_status: raise error(103, "Connection aborted") # ECONNABORTED else: raise error(106, "Already connected") # EISCONN host, port = addr ip = efi.EFI_IPv4_ADDRESS.from_buffer_copy(inet_aton(gethostbyname(host))) data = efi.EFI_TCP4_CONFIG_DATA() data.TypeOfService = 0 data.TimeToLive = 60 # UseDefaultAddress = True fails with EFI_ALREADY_STARTED, but using # the previously obtained address works. The UEFI 2.5 specification # does not explain this behavior or document this error code as a # possible return from Configure. data.AccessPoint.UseDefaultAddress = False # Use the local IP and port from bind if set try: data.AccessPoint.StationAddress = self._bind_ip data.AccessPoint.StationPort = self._bind_port except AttributeError as e: data.AccessPoint.StationAddress = _ip_address data.AccessPoint.StationPort = 0 data.AccessPoint.SubnetMask = _subnet_mask data.AccessPoint.RemoteAddress = ip data.AccessPoint.RemotePort = port data.AccessPoint.ActiveFlag = True efi.check_status(self._tcp4.Configure(self._tcp4, byref(data))) # Contradicting the UEFI 2.5 specification, the EFI_TCP4_PROTOCOL does # not automatically use all of the underlying IP4 routes. Add them # manually, but ignore any failure caused by already having the route. for route in _routes: status = self._tcp4.Routes(self._tcp4, False, byref(route.SubnetAddress), byref(route.SubnetMask), byref(route.GatewayAddress)) if status != efi.EFI_ACCESS_DENIED: efi.check_status(status) token = efi.EFI_TCP4_CONNECTION_TOKEN() def callback(): self._connect_status = token.CompletionToken.Status self._events.close_event(efi.EFI_EVENT(token.CompletionToken.Event)) token.CompletionToken.Event = self._events.create_event(callback, abort=self._abort) status = self._tcp4.Connect(self._tcp4, byref(token)) if status: token.CompletionToken.Status = status callback() if self.timeout == 0: raise error(115, "Operation now in progress") # EINPROGRESS start = time.time() while (self.timeout < 0) or (time.time() - start < self.timeout): if self._connect_status is not None: efi.check_status(self._connect_status) return self._poll() raise timeout(11, "timed out") # EAGAIN
def _stop_config(): global _ip4cp efi.check_status(_ip4cp.Stop(_ip4cp))
def _ip4cp_stop_config(): global _ip4cp efi.check_status(_ip4cp.Stop(_ip4cp))
def connect(self, addr): """connect(address) Connect the socket to a remote address. For IP sockets, the address is a pair (host, port).""" global _ip_address, _subnet_mask, _routes if self._is_listen_socket: raise error("connect() called after listen()") if self._connect_status is not None: if self._connect_status: raise error(103, "Connection aborted") # ECONNABORTED else: raise error(106, "Already connected") # EISCONN host, port = addr ip = efi.EFI_IPv4_ADDRESS.from_buffer_copy( inet_aton(gethostbyname(host))) data = efi.EFI_TCP4_CONFIG_DATA() data.TypeOfService = 0 data.TimeToLive = 60 # UseDefaultAddress = True fails with EFI_ALREADY_STARTED, but using # the previously obtained address works. The UEFI 2.5 specification # does not explain this behavior or document this error code as a # possible return from Configure. data.AccessPoint.UseDefaultAddress = False # Use the local IP and port from bind if set try: data.AccessPoint.StationAddress = self._bind_ip data.AccessPoint.StationPort = self._bind_port except AttributeError as e: data.AccessPoint.StationAddress = _ip_address data.AccessPoint.StationPort = 0 data.AccessPoint.SubnetMask = _subnet_mask data.AccessPoint.RemoteAddress = ip data.AccessPoint.RemotePort = port data.AccessPoint.ActiveFlag = True efi.check_status(self._tcp4.Configure(self._tcp4, byref(data))) # Contradicting the UEFI 2.5 specification, the EFI_TCP4_PROTOCOL does # not automatically use all of the underlying IP4 routes. Add them # manually, but ignore any failure caused by already having the route. for route in _routes: status = self._tcp4.Routes(self._tcp4, False, byref(route.SubnetAddress), byref(route.SubnetMask), byref(route.GatewayAddress)) if status != efi.EFI_ACCESS_DENIED: efi.check_status(status) token = efi.EFI_TCP4_CONNECTION_TOKEN() def callback(): self._connect_status = token.CompletionToken.Status self._events.close_event(efi.EFI_EVENT( token.CompletionToken.Event)) token.CompletionToken.Event = self._events.create_event( callback, abort=self._abort) status = self._tcp4.Connect(self._tcp4, byref(token)) if status: token.CompletionToken.Status = status callback() if self.timeout == 0: raise error(115, "Operation now in progress") # EINPROGRESS start = time.time() while (self.timeout < 0) or (time.time() - start < self.timeout): if self._connect_status is not None: efi.check_status(self._connect_status) return self._poll() raise timeout(11, "timed out") # EAGAIN