aiosmtpd icon indicating copy to clipboard operation
aiosmtpd copied to clipboard

Fix bind unspecified address controller will raise TimeoutError

Open wevsty opened this issue 3 years ago • 13 comments

What do these changes do?

Add a function is_unspecified_address() determine if the address is the unspecified address. Automatic replacement of unspecified address with local addresses.

Are there changes in behavior for the user?

This will be more compatible with the user-specified behavior.

Related issue number

issue #272

Checklist

  • [x] I think the code is well written
  • [x] Unit tests for the changes exist
  • [x] tox testenvs have been executed in the following environments:
    • [x] Linux (Ubuntu 18.04, Ubuntu 20.04, Arch): {py36,py37,py38,py39}-{nocov,cov,diffcov}, qa, docs
    • [x] Windows (7, 10): {py36,py37,py38,py39}-{nocov,cov,diffcov}
    • [ ] WSL 1.0 (Ubuntu 18.04): {py36,py37,py38,py39}-{nocov,cov,diffcov}, pypy3-{nocov,cov}, qa, docs
    • [ ] FreeBSD (12.2, 12.1, 11.4): {py36,pypy3}-{nocov,cov,diffcov}, qa
    • [ ] Cygwin: py36-{nocov,cov,diffcov}, qa, docs
  • [ ] Documentation reflects the changes
  • [x] Add a news fragment into the NEWS.rst file

wevsty avatar Jun 06 '21 18:06 wevsty

I'm not 100% sure if this is the correct fix - 0.0.0.0 should bind on all interfaces right? I'm on my phone atm so I might not grok correctrly...

waynew avatar Jun 06 '21 19:06 waynew

This pull request introduces 1 alert when merging 28f3763ebe6a92af05ff1fac85d33bd53aea3584 into 215b854447e2567bbc5e3665d9a648d7b1fa2c82 - view on LGTM.com

new alerts:

  • 1 for Syntax error

lgtm-com[bot] avatar Jun 06 '21 19:06 lgtm-com[bot]

I'm not 100% sure if this is the correct fix - 0.0.0.0 should bind on all interfaces right? I'm on my phone atm so I might not grok correctrly...

Listening 0.0.0.0 usually means listening to all IPV4 interfaces. However, 0.0.0.0 is not directly accessible, so there are errors that need special handling when detecting it. On IPV6 :: has the same problem

wevsty avatar Jun 06 '21 19:06 wevsty

Codecov Report

Merging #273 (5a0bde7) into master (e4bcd3f) will not change coverage. The diff coverage is 100.00%.

@@            Coverage Diff            @@
##            master      #273   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            7         7           
  Lines         1696      1713   +17     
  Branches       310       313    +3     
=========================================
+ Hits          1696      1713   +17     
Impacted Files Coverage Δ
aiosmtpd/controller.py 100.00% <100.00%> (ø)
aiosmtpd/main.py 100.00% <100.00%> (ø)
aiosmtpd/proxy_protocol.py 100.00% <100.00%> (ø)
aiosmtpd/smtp.py 100.00% <100.00%> (ø)

:mega: We’re building smart automated test selection to slash your CI/CD build times. Learn more

codecov[bot] avatar Jun 16 '21 17:06 codecov[bot]

I did my best to fix the problems with flake8 checks. On my machine I passed all the tests.

But test_byclient still doesn't pass the test in some environments,This should be due to a change in Python's behavior. see #219

My fix is special for python 3.8 and later

    def test_byclient(
        self, caplog, auth_peeker_controller, client, mechanism, init_resp
    ):
        self._ehlo(client)
        PW = "goodpasswd"
        client.user = "goodlogin"
        client.password = PW
        auth_meth = getattr(client, "auth_" + mechanism)
        if (mechanism, init_resp) == ("login", False):
            with pytest.raises(SMTPAuthenticationError):
                client.auth(mechanism, auth_meth, initial_response_ok=init_resp)
                # bpo-27820 has been fixed in python version 3.8 or later
                if sys.version_info >= (3, 8):
                    raise SMTPAuthenticationError(454, 'Expected failed')
            client.docmd("*")
            pytest.xfail(reason="smtplib.SMTP.auth_login is buggy (bpo-27820)")
        client.auth(mechanism, auth_meth, initial_response_ok=init_resp)
        peeker = auth_peeker_controller.handler
        assert isinstance(peeker, PeekerHandler)
        assert peeker.login == b"goodlogin"
        assert peeker.password == PW.encode("ascii")
        assert_nopassleak(PW, caplog.record_tuples)

I think this place needs to make some changes. @pepoluan

wevsty avatar Jun 16 '21 18:06 wevsty

@pepoluan

Shall we LGTM?

All checks does not seem to find any problems. If there are other questions please contact me.

wevsty avatar Oct 19 '21 13:10 wevsty

@pepoluan Any reason we shouldn't merge this?

waynew avatar Nov 02 '21 22:11 waynew

@pepoluan

Shall we LGTM?

All checks does not seem to find any problems. If there are other questions please contact me.

After one year (sorry) conflicts arise ...

Can we fix this and revisit?

pepoluan avatar Dec 21 '22 04:12 pepoluan

@pepoluan

It's been a bit of a busy couple of weeks, so I've procrastinated a bit. But I've resolved the conflicts submitted the new version and it should work fine now.

wevsty avatar Jan 08 '23 12:01 wevsty

Okay so I've drunk enough coffee for today, I can think again 😁

This converts "unspecified" to localhost, rather than current IP adress, I think we need to mention that in the documentation as well. Other software when given "unspecified" will bind to all existing addresses including localhost. Though that is not a 'standard behavior' I think being explicit will be good.

pepoluan avatar Jan 13 '23 06:01 pepoluan

Okay so I've drunk enough coffee for today, I can think again 😁

This converts "unspecified" to localhost, rather than current IP adress, I think we need to mention that in the documentation as well. Other software when given "unspecified" will bind to all existing addresses including localhost. Though that is not a 'standard behavior' I think being explicit will be good.

I don't think it needs to be deliberately stated in the documentation. Bind unspecified address is a common behavior, and on many platforms unspecified address is used instead of the IP address of all interfaces on the local machine. And the code is not replacing bind 0.0.0.0 with 127.0.0.1. It's just that aiosmtpd tries to connect to the bind address after the bind is done to make sure the bind is successful, but obviously most systems don't accept 0.0.0.0 as a connectable address, this PR just uses 127.0.0.1 when bind 0.0.0.0 to for connection testing, and does not change the external behavior.

wevsty avatar Jan 14 '23 10:01 wevsty

I don't think it needs to be deliberately stated in the documentation. Bind unspecified address is a common behavior, and on many platforms unspecified address is used instead of the IP address of all interfaces on the local machine. And the code is not replacing bind 0.0.0.0 with 127.0.0.1.

It may be common behavior, but I only learned about this just recently. I'm sure I'm not the only one

waynew avatar Jan 14 '23 16:01 waynew

And the code is not replacing bind 0.0.0.0 with 127.0.0.1. It's just that aiosmtpd tries to connect to the bind address after the bind is done to make sure the bind is successful, but obviously most systems don't accept 0.0.0.0 as a connectable address, this PR just uses 127.0.0.1 when bind 0.0.0.0 to for connection testing, and does not change the external behavior.

Ahh thanks for the explanation!

Was a bit confused when I just look at the snippets.

pepoluan avatar Jan 17 '23 10:01 pepoluan