eventlet / eventlet

Concurrent networking library for Python
https://eventlet.net
Other
1.24k stars 320 forks source link

Discrepancy with getaddrinfo with AI_CANONNAME compare to standard library implementation #764

Open mtomaska opened 2 years ago

mtomaska commented 2 years ago

Version: Eventlet 0.33.1 (also confirmed in 0.30.3 just because it was available)

Its seems that greendns.py:getaddrinfo() seems to ignore looking into /etc/hosts when querying with flags=socket.AI_CANONNAME ?

Here is a simple reproducing code

from eventlet import monkey_patch
import socket
monkey_patch()

def call_getaddinfo():
    # /etc/hosts contains "10.0.0.0 compute-0.localdomain compute-0"
    addrinfo = socket.getaddrinfo(host="compute-0",
                                port=None,
                                family=socket.AF_UNSPEC,
                                flags=socket.AI_CANONNAME)

    print(addrinfo)

if __name__ == "__main__":
    # with eventlet
    try:
        call_getaddinfo()
    except IOError as err:
        print("Error {} when calling with eventlet".format(err))

    # undo monkey_patch
    from importlib import reload
    reload(socket)

    # with standard library
    call_getaddinfo()

Output:

Error [Errno -2] Name or service not known when calling with eventlet
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, 'compute-0.localdomain', ('10.0.0.0', 0)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_DGRAM: 2>, 17, '', ('10.0.0.0', 0)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_RAW: 3>, 0, '', ('10.0.0.0', 0))]

I would expect that getaddrinfo() would return the same value as the one in socket standard library.

temoto commented 2 years ago

Thanks for great bug report. I think pinpointed problem to greendns.HostsResolver.query. Not sure what's proper solution yet.

FYI: you can't reliably undo monkey patch, but it doesn't change test result.

Note to contributor or myself: start with tests/isolated/greendns_getaddrinfo.py like this

__test__ = False

def fun(family=0, flags=0):
    try:
        addrinfo = socket.getaddrinfo(host="localhost", port=None, family=family, flags=flags)
    except IOError as err:
        return "error: {}".format(err)
    return str(addrinfo)

if __name__ == "__main__":
    import socket

    configs = [
        ("default", dict()),
        ("AF_UNSPEC", dict(family=socket.AF_UNSPEC)),
        ("AI_CANONNAME", dict(flags=socket.AI_CANONNAME)),
    ]

    results_stdlib = [(tag, fun(**kwargs)) for tag, kwargs in configs]

    import eventlet
    eventlet.monkey_patch()

    results_patched = [(tag, fun(**kwargs)) for tag, kwargs in configs]

    for stdlib, patched in zip(results_stdlib, results_patched, strict=True):
        assert patched == stdlib, "{}\t{}".format(*patched)

    print("pass")
mtomaska commented 2 years ago

For what is worth. The reason that /etc/hosts content is not queried is because greendns.py#L388 rdtype in _hosts_rdtypes evaluates to False because rdtype in my case is AI_CANONNAME I believe I would get my result if that condition was True. But I dont understand the code enough to judge if thats a proper fix or not.

And thanks for the heads up about monkey_patch undo. I was not sure if there was a proper way or not, so I just tried reload

mtomaska commented 2 years ago

@temoto as a workaround, is there a safe way to use socket.getaddrinfo() from python standard library while keeping other socket methods patched by eventlet? Thanks

temoto commented 1 year ago

eventlet.patcher.original("socket") is the supported way to access unmodified modules. Sorry for the inconvenience.

On Fri, Jul 8, 2022, 19:10 Miro @.***> wrote:

@temoto https://github.com/temoto as a workaround, is there a safe way to use socket.getaddrinfo() from python standard library while keeping other socket methods patched by eventlet? Thanks

— Reply to this email directly, view it on GitHub https://github.com/eventlet/eventlet/issues/764#issuecomment-1179155795, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAGTMLZJJBWKB7OCOLATNDVTBHINANCNFSM526YPFGQ . You are receiving this because you were mentioned.Message ID: @.***>