Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ The table below shows which release corresponds to each branch, and what date th

## 5.0.0 (`dev`)

- [#2728][2728] fix(remote): make Ctrl-C interrupt blocking getaddrinfo() (#2540)
- [#2677][2677] refactor: replace unsafe eval with safeeval.const in ROP cache loading
- [#2675][2675] feat(term): add zellij support
- [#2652][2652] Make setting the context.terminal to kitty more user friendly
Expand Down Expand Up @@ -187,6 +188,7 @@ The table below shows which release corresponds to each branch, and what date th
[2720]: https://github.com/Gallopsled/pwntools/pull/2720
[2702]: https://github.com/Gallopsled/pwntools/pull/2702
[2722]: https://github.com/Gallopsled/pwntools/pull/2722
[2728]: https://github.com/Gallopsled/pwntools/pull/2728

## 4.15.1

Expand Down
34 changes: 33 additions & 1 deletion pwnlib/tubes/remote.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,42 @@
import contextlib
import signal
import socket
import socks
import threading

from pwnlib.log import getLogger
from pwnlib.timeout import Timeout
from pwnlib.tubes.sock import sock

log = getLogger(__name__)


@contextlib.contextmanager
def _interruptible_block():
"""Temporarily reset SIGINT to its default handler for the duration of the
block, then restore the previous handler.

This lets a Ctrl-C interrupt blocking C-level calls such as
:func:`socket.getaddrinfo`, which on glibc hold the GIL and are otherwise
uninterruptible from Python (#2540).

Signal handlers can only be changed from the main thread, so on any other
thread (or in embedded interpreters that refuse the change) this is a
no-op and the caller's existing semantics are preserved.
"""
if threading.current_thread() is not threading.main_thread():
yield
return
try:
old = signal.signal(signal.SIGINT, signal.SIG_DFL)
except ValueError:
yield
return
try:
yield
finally:
signal.signal(signal.SIGINT, old)

class remote(sock):
r"""Creates a TCP or UDP-connection to a remote host. It supports
both IPv4 and IPv6.
Expand Down Expand Up @@ -107,7 +137,9 @@ def _connect(self, fam, typ):
timeout = self.timeout

with self.waitfor('Opening connection to %s on port %s' % (self.rhost, self.rport)) as h:
for res in socket.getaddrinfo(self.rhost, self.rport, fam, typ, 0, socket.AI_PASSIVE):
with _interruptible_block():
addrinfos = socket.getaddrinfo(self.rhost, self.rport, fam, typ, 0, socket.AI_PASSIVE)
for res in addrinfos:
self.family, self.type, self.proto, _canonname, sockaddr = res

if self.type not in [socket.SOCK_STREAM, socket.SOCK_DGRAM]:
Expand Down
Loading