Skip to content

getaddrinfo fails on Illumos, inconsistent elsewhere if port is, but type & protocol are not specified #123832

Closed
@zyv

Description

@zyv

Bug report

Bug description:

I have discovered that

  1. On Solaris OpenIndiana, if you don't specify at least socket type or protocol for the service name, address resolution fails (see below).
  2. Linux & MacOS happily ignore this and continue with the resolution anyway.
  3. Specifying the socket family doesn't help.

I've filed a PR to fix the problem at the call site of the software that exposed it (miguelgrinberg/Flask-SocketIO#2088), but the maintainer argues that it's a bug in CPython and is unwilling to accept a patch that specifies the protocol.

His position that Python's high-level APIs should provide consistent behavior across platforms is understandable, although one could argue that getaddrinfo("127.0.0.1", 5000) is a borderline invalid use of the API. However, the documentation as it stands suggests that None can be passed, but doesn't explain why you would want to do so, and the paragraph below suggests that keyword arguments act as AND filters and can be safely omitted.

Unfortunately, I don't see any fantastic way to fix this in CPython that would provide a consistent interface across platforms. I think we could do a configure check to detect this getaddrinfo behavior (might also be the case on AIX, Tru64, etc., although it's just a speculation) and then call the C function twice at Python level with (type=socket.SOCK_STREAM and type=socket.SOCK_DGRAM) to merge the results (which is already done for another reason):

def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):

Or, if you don't mind, we can just always do it where protocol AND socket type are unspecified, but port is specified and is numeric. This is very simple, the overhead (I think) is negligible, and it is consistent across platforms.

I would appreciate an opinion from Python developers and/or Solaris engineers before I put more work into this.

OpenIndiana

$ python3
Python 3.9.19 (main, Mar 26 2024, 20:30:24) [GCC 13.2.0] on sunos5
Type "help", "copyright", "credits" or "license" for more information.

>>> socket.getaddrinfo("127.0.0.1", 5000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.9/socket.py", line 954, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno 9] service name not available for the specified socket type

>>> socket.getaddrinfo("127.0.0.1", 5000, family=socket.AF_INET)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.9/socket.py", line 954, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno 9] service name not available for the specified socket type

>>> socket.getaddrinfo("127.0.0.1", None)
[(<AddressFamily.AF_INET: 2>, 0, 0, '', ('127.0.0.1', 0))]

>>> socket.getaddrinfo("127.0.0.1", 5000, type=socket.SOCK_STREAM)
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 2>, 6, '', ('127.0.0.1', 5000))]

>>> socket.getaddrinfo("127.0.0.1", 5000, proto=socket.IPPROTO_TCP)
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 2>, 6, '', ('127.0.0.1', 5000))]

macOS

% python3
Python 3.12.5 (main, Aug  6 2024, 19:08:49) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

>>> socket.getaddrinfo("127.0.0.1", 5000)
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_DGRAM: 2>, 17, '', ('127.0.0.1', 5000)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('127.0.0.1', 5000))]

AIX

-bash-5.1$ python3
Python 3.9.19 (main, Apr  5 2024, 05:08:51) 
[GCC 10.3.0] on aix
Type "help", "copyright", "credits" or "license" for more information.

>>> socket.getaddrinfo("127.0.0.1", 5000)
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_DGRAM: 2>, 17, '', ('127.0.0.1', 5000))]

>>> socket.getaddrinfo("127.0.0.1", 5000, type=socket.SOCK_STREAM)
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('127.0.0.1', 5000))]

Solaris

$ python3
Python 3.9.19 (main, Sep 23 2024, 16:24:38) 
[GCC 13.2.0] on sunos5
Type "help", "copyright", "credits" or "license" for more information.

>>> socket.getaddrinfo("127.0.0.1", 5000)
[(<AddressFamily.AF_INET: 2>, 0, 0, '', ('127.0.0.1', 5000))]
>>> socket.getaddrinfo("127.0.0.1", 5000, family=socket.AF_INET)
[(<AddressFamily.AF_INET: 2>, 0, 0, '', ('127.0.0.1', 5000))]
>>> socket.getaddrinfo("127.0.0.1", None)
[(<AddressFamily.AF_INET: 2>, 0, 0, '', ('127.0.0.1', 0))]
>>> socket.getaddrinfo("127.0.0.1", 5000, type=socket.SOCK_STREAM)
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 2>, 6, '', ('127.0.0.1', 5000))]
>>> socket.getaddrinfo("127.0.0.1", 5000, proto=socket.IPPROTO_TCP)
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 2>, 6, '', ('127.0.0.1', 5000))]

FreeBSD 14.1

[zaytsev@freebsd ~]$ python3
Python 3.11.9 (main, Oct  5 2024, 11:59:33) [Clang 18.1.5 (https://github.com/llvm/llvm-project.git llvmorg-18.1.5-0-g617a15 on freebsd14
Type "help", "copyright", "credits" or "license" for more information.

>>> socket.getaddrinfo("127.0.0.1", 5000)
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_DGRAM: 2>, 17, '', ('127.0.0.1', 5000)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('127.0.0.1', 5000)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_SEQPACKET: 5>, 132, '', ('127.0.0.1', 5000))]

>>> socket.getaddrinfo("127.0.0.1", None)
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_DGRAM: 2>, 17, '', ('127.0.0.1', 0)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('127.0.0.1', 0)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_SEQPACKET: 5>, 132, '', ('127.0.0.1', 0))]

Linux (Fedora)

zaytsev@fedora:~$ python3
Python 3.12.4 (main, Jun  7 2024, 00:00:00) [GCC 14.1.1 20240607 (Red Hat 14.1.1-5)] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> socket.getaddrinfo("127.0.0.1", 5000)
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('127.0.0.1', 5000)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_DGRAM: 2>, 17, '', ('127.0.0.1', 5000)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_RAW: 3>, 0, '', ('127.0.0.1', 5000))]

Summary

  • Linux: STREAM, DGRAM, RAW
  • macOS: STREAM, DGRAM
  • OpenBSD: STREAM, DGRAM
  • FreeBSD with Cheri: STREAM, DGRAM, SEQPACKET
  • AIX: DGRAM (but STREAM supported, obviously!)
  • Solaris/Illumos: exception

/cc @kulikjak

CPython versions tested on:

3.9

Operating systems tested on:

Other

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    docsDocumentation in the Doc dirextension-modulesC modules in the Modules dir

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions