Code

Opened 6 years ago

Last modified 10 months ago

#6989 new Bug

Inability to define DNS_NAME in django.core.mail results in e-mail messages being rejected or marked as spam

Reported by: Franklin Owned by: nobody
Component: Core (Mail) Version: master
Severity: Normal Keywords: local_hostname, DNS_NAME, CachedDnsName, smtplib, SMTPConnection
Cc: jeremy+django@…, davidgrant@… Triage Stage: Accepted
Has patch: yes Needs documentation: yes
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

Summary
Being the case that the application server's hostname may not necessarily be a valid fully qualified domain name (FQDN) the use of socket.getfqdn() in defining DNS_NAME may yield a number of e-mail messages either rejected or marked as spam. Rejection issues arise if settings.EMAIL_USE_TLS is True - meaning smtplib.ehlo() will be called - and if a mail server is particularly strict (or misconfigured) and rejects any ehlo command followed by an invalid FQDN. Spam issues arise with DNS_NAME being used as the local_hostname argument to smtplib.SMTP() within the SMTPConnection.open() method, meaning the initial "Received:" e-mail header may be from an invalid FQDN. Further increasing the chance of being marked as spam is DNS_NAME being used by the make_msgid() method, leaving the "Message-ID:" e-mail header ending in an invalid FQDN.

Real World Scenario
A shared server has a hostname, which you may not and can not change, of "star-wars-reference". The socket.getfqdn() method fails to find info on its IP address and by default simply returns said hostname. E-mail is then sent by Django with a Message-ID of "200804....@star-wars-reference" from a server claiming to be "star-wars-reference ([server's IP address])" and is, of course, marked as spam.

Solution
Provide an optional settings variable such as EMAIL_LOCAL_HOSTNAME where Django developers may define a FQDN which maps to their application's server. EMAIL_LOCAL_HOSTNAME will be used by the class defining DNS_NAME in lieu of socket.getfqdn() if EMAIL_LOCAL_HOSTNAME exists in the settings file (this has the added benefit of preventing a call to socket.getfqdn() - further reducing overhead on restarts).

Attachments (2)

mail-local-hostname.diff (465 bytes) - added by Franklin 6 years ago.
mail-local-hostname2.diff (3.9 KB) - added by synack 3 years ago.

Download all attachments as: .zip

Change History (18)

Changed 6 years ago by Franklin

comment:1 Changed 6 years ago by edgarsj

  • Needs documentation set
  • Needs tests unset
  • Patch needs improvement set
  • Triage Stage changed from Unreviewed to Design decision needed

comment:2 Changed 4 years ago by stilldodge

This still appears to be an issue. Possibly this was unresolved because email servers used to accept an invalid FQDN. I can confirm that Exchange 2007 rejects the connection if the local host "calculated" name is not truly a valid FQDN

comment:3 Changed 4 years ago by mtredinnick

  • Triage Stage changed from Design decision needed to Accepted

Changed 3 years ago by synack

comment:4 Changed 3 years ago by synack

I've attached an updated and documented patch (mail-local-hostname2.diff) allowing the user to specify EMAIL_LOCAL_HOSTNAME. Please review and let me know if any changes are necessary to get this merged.

comment:5 Changed 3 years ago by synack

  • Cc jeremy+django@… added

comment:6 Changed 3 years ago by adrian

I'm just skimming this, but my immediate reaction is: introducing a new setting is a non-starter. Is there a way to accomplish what you want without a setting?

comment:7 Changed 3 years ago by synack

We could force anybody needing this to subclass the SMTP EmailBackend, but that seems a bit excessive to me.

comment:8 Changed 3 years ago by julien

  • Severity set to Normal
  • Type set to Bug

comment:9 Changed 2 years ago by aaugustin

  • UI/UX unset

Change UI/UX from NULL to False.

comment:10 Changed 2 years ago by aaugustin

  • Easy pickings unset

Change Easy pickings from NULL to False.

comment:11 Changed 12 months ago by Edwin

Here is a workaround.

Python's socket.getfqdn() resolution process works like this on Linux:

  1. Get hostname of server.
  2. Resolve IP address for hostname.
  3. Resolve name for IP address.
  4. If 2 or 3 fail, just use the hostname.

Therefore, if you add a line like this to your /etc/hosts, socket.getfqdn() will resolve myhostname to 127.0.0.1 and resolve 127.0.0.1 to myfqdn since that is the first name in the list. You also have to make sure that there are no other entries for 127.0.0.1 on previous lines.

127.0.0.1       myfqdn myhostname

comment:12 Changed 10 months ago by anonymous

That workaround only makes sense if the server is hosting one django project. I have one Linux server with several django projects. Each uses a different fqdn but the same IP.

comment:13 Changed 10 months ago by davidgrant@…

Can we get this in? Relying on socket.get_fqdn() seems very wrong and patch looks good.

comment:14 Changed 10 months ago by davidgrant@…

So where do we go from here? Instead of a setting we could use an environment variable. Other than that I don't see any other way.

comment:15 Changed 10 months ago by davidgrant@…

  • Cc davidgrant@… added

comment:16 Changed 10 months ago by russellm

@davidgrant - Where do we go? We go the same way we do with any other ticket that has stalled: Start a discussion on django-dev. Summarize the problem. Summarize the alternatives. Propose a solution.

I'll also note that Adrian's comment about settings -- we've historically avoided adding new settings wherever possible. But if it truly isn't possible (at all, or for all practical purposes) to avoid a setting, it's not completely off the cards.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as new
The owner will be changed from nobody to anonymous. Next status will be 'assigned'
as The resolution will be set. Next status will be 'closed'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.