diff --git a/scapy/automaton.py b/scapy/automaton.py index a0ad54232be..0cdd92cf3ba 100644 --- a/scapy/automaton.py +++ b/scapy/automaton.py @@ -95,69 +95,76 @@ def select_objects(inputs, remain): events_to_inputs = [] created = [] results = set() - for i in inputs: - fno = i.fileno() - if fno < 0: - # Special case: On Windows, we consider that an object that returns - # a negative fileno (impossible), is always readable. This is used - # in very few places but important (e.g. PcapReader), where we have - # no valid fileno (and will stop on EOFError). - results.add(i) - remain = 0 - continue - if getattr(i, "__selectable_force_select__", False): - # Native socket.socket object. We would normally use select.select. - evt = ctypes.windll.ws2_32.WSACreateEvent() - created.append(evt) - res = ctypes.windll.ws2_32.WSAEventSelect( - ctypes.c_void_p(fno), - evt, - FD_READ - ) - if res == 0: - # Was a socket - event = evt + try: + for i in inputs: + fno = i.fileno() + if fno < 0: + # Special case: On Windows, we consider that an object that returns + # a negative fileno (impossible), is always readable. This is used + # in very few places but important (e.g. PcapReader), where we have + # no valid fileno (and will stop on EOFError). + results.add(i) + remain = 0 + continue + if getattr(i, "__selectable_force_select__", False): + # Native socket.socket object. We would normally use select.select. + evt = ctypes.windll.ws2_32.WSACreateEvent() + created.append(evt) + res = ctypes.windll.ws2_32.WSAEventSelect( + ctypes.c_void_p(fno), + evt, + FD_READ + ) + if res == 0: + # Was a socket + event = evt + else: + # Fallback to normal event + event = fno else: - # Fallback to normal event event = fno - else: - event = fno - events.append(event) - events_to_inputs.append(i) - if events: - # 0xFFFFFFFF = INFINITE - remainms = int(remain * 1000 if remain is not None else 0xFFFFFFFF) - if len(events) == 1: - res = ctypes.windll.kernel32.WaitForSingleObject( - ctypes.c_void_p(events[0]), - remainms - ) - else: - # Sadly, the only way to emulate select() is to first check - # if any object is available using WaitForMultipleObjects - # then poll the others. - res = ctypes.windll.kernel32.WaitForMultipleObjects( - len(events), - (ctypes.c_void_p * len(events))( - *events - ), - False, - remainms - ) - if res != 0xFFFFFFFF and res != 0x00000102: # Failed or Timeout - results.add(events_to_inputs[res]) - if len(events) > 1: - # Now poll the others, if any - for i, evt in enumerate(events): - res = ctypes.windll.kernel32.WaitForSingleObject( - ctypes.c_void_p(evt), - 0 # poll: don't wait - ) - if res == 0: - results.add(events_to_inputs[i]) - # Cleanup created events, if any - for evt in created: - ctypes.windll.ws2_32.WSACloseEvent(evt) + events.append(event) + events_to_inputs.append(i) + if events: + # 0xFFFFFFFF = INFINITE + remainms = int(remain * 1000 if remain is not None else 0xFFFFFFFF) + if len(events) == 1: + res = ctypes.windll.kernel32.WaitForSingleObject( + ctypes.c_void_p(events[0]), + remainms + ) + else: + # Sadly, the only way to emulate select() is to first check + # if any object is available using WaitForMultipleObjects + # then poll the others. + res = ctypes.windll.kernel32.WaitForMultipleObjects( + len(events), + (ctypes.c_void_p * len(events))( + *events + ), + False, + remainms + ) + # ctypes' default signed c_int restype returns WAIT_FAILED as -1. + if res == -1 or res == 0xFFFFFFFF: + raise ctypes.WinError() + if res != 0x00000102: # Timeout + results.add(events_to_inputs[res]) + if len(events) > 1: + # Now poll the others, if any + for i, evt in enumerate(events): + res = ctypes.windll.kernel32.WaitForSingleObject( + ctypes.c_void_p(evt), + 0 # poll: don't wait + ) + if res == -1 or res == 0xFFFFFFFF: + raise ctypes.WinError() + if res == 0: + results.add(events_to_inputs[i]) + finally: + # Cleanup created events, if any + for evt in created: + ctypes.windll.ws2_32.WSACloseEvent(evt) return list(results)