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`)

- [#2727][2727] feat(cli): add 'pwn errno --list' + perror-string lookup
- [#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
[2727]: https://github.com/Gallopsled/pwntools/pull/2727

## 4.15.1

Expand Down
101 changes: 82 additions & 19 deletions pwnlib/commandline/errno.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,100 @@
)

parser.add_argument(
'error', help='Error message or value', type=str
'error', help='Error message or value (errno number, errno name, or perror string)',
nargs='?', type=str
)
parser.add_argument(
'-l', '--list', action='store_true',
help='List every known errno (number, name, perror string).'
)
parser.add_argument(
'-s', '--search', action='store_true',
help='Treat the argument as a perror substring and print every matching errno.'
)


def _iter_known_errnos():
"""Yield ``(value, name, message)`` tuples for every errno code visible to
the standard library on this platform, sorted by numeric value.
"""
for value, name in sorted(errno.errorcode.items()):
yield value, name, os.strerror(value)


def _print_errno(value, name=None, message=None):
if name is None:
name = errno.errorcode.get(value, '')
if message is None:
message = os.strerror(value)
if name:
print('#define %s %d' % (name, value))
else:
print('#define <unknown> %d' % value)
print(message)


def _search_errnos(needle):
"""Return errnos whose ``strerror`` text contains ``needle`` (case-insensitive)."""
needle = needle.lower()
return [(v, n, m) for v, n, m in _iter_known_errnos() if needle in m.lower()]


def main(args):
try:
value = int(args.error, 0)
if args.list:
for value, name, message in _iter_known_errnos():
print('%-3d %-10s %s' % (value, name, message))
return

if args.error is None:
parser.error('the following arguments are required: error (or pass --list)')

if args.search:
matches = _search_errnos(args.error)
if not matches:
print("No errno matched %r" % args.error)
return
for value, name, message in matches:
_print_errno(value, name, message)
print()
return

if value < 0:
value = -value
try:
value = int(args.error, 0)

if 0x100000000 - value < 0x200:
value = 0x100000000 - value
if value < 0:
value = -value

if value not in errno.errorcode:
print("No errno for %s" % value)
return
if 0x100000000 - value < 0x200:
value = 0x100000000 - value

name = errno.errorcode[value]
if value not in errno.errorcode:
print("No errno for %s" % value)
return

except ValueError:
name = args.error.upper()
name = errno.errorcode[value]

if not hasattr(errno, name):
print("No errno for %s" % name)
return
except ValueError:
candidate = args.error.upper()

value = getattr(errno, name)
if hasattr(errno, candidate):
name = candidate
value = getattr(errno, name)
else:
# Fall back to a substring search against perror messages so
# ``pwn errno 'Cannot allocate memory'`` resolves to ENOMEM.
matches = _search_errnos(args.error)
if not matches:
print("No errno for %s" % args.error)
return
for value, name, message in matches:
_print_errno(value, name, message)
if len(matches) > 1:
print()
return

_print_errno(value, name)

print('#define', name, value)
print(os.strerror(value))

if __name__ == '__main__':
common.main(__file__, main)
Loading