zilencer: Disambiguate no MX records from the domain not existing.

This switches to dnspython, since it offers the higher-level
`resolve_name` method to look up both A and AAAA records, and set
timeouts.
This commit is contained in:
Alex Vandiver 2024-05-23 18:31:31 +00:00 committed by Tim Abbott
parent 9388805471
commit e5a0b3b3c5
2 changed files with 32 additions and 18 deletions

View File

@ -8,7 +8,6 @@ from typing import Any, Dict, Iterator, List, Mapping, Optional, Tuple, Union
from unittest import mock, skipUnless
import aioapns
import DNS
import orjson
import responses
import time_machine
@ -19,6 +18,7 @@ from django.http.response import ResponseHeaders
from django.test import override_settings
from django.utils.crypto import get_random_string
from django.utils.timezone import now
from dns.resolver import NoAnswer as DNSNoAnswer
from requests.exceptions import ConnectionError
from requests.models import PreparedRequest
from typing_extensions import override
@ -5247,17 +5247,24 @@ class PushBouncerSignupTest(ZulipTestCase):
self.assert_json_error(result, "Please use your real email address.")
request["contact_email"] = "admin@zulip.com"
with mock.patch("DNS.mxlookup", side_effect=DNS.Base.ServerError("test", 1)):
with mock.patch("zilencer.views.dns_resolver.Resolver") as resolver:
resolver.return_value.resolve.side_effect = DNSNoAnswer
resolver.return_value.resolve_name.return_value = ["whee"]
result = self.client_post("/api/v1/remotes/server/register", request)
self.assert_json_error(
result, "zulip.com does not exist or is not configured to accept email."
result, "zulip.com is invalid because it does not have any MX records"
)
with mock.patch("DNS.mxlookup", return_value=[]):
with mock.patch("zilencer.views.dns_resolver.Resolver") as resolver:
resolver.return_value.resolve.side_effect = DNSNoAnswer
resolver.return_value.resolve_name.side_effect = DNSNoAnswer
result = self.client_post("/api/v1/remotes/server/register", request)
self.assert_json_error(
result, "zulip.com does not exist or is not configured to accept email."
)
self.assert_json_error(result, "zulip.com does not exist")
with mock.patch("zilencer.views.dns_resolver.Resolver") as resolver:
resolver.return_value.resolve.return_value = ["whee"]
result = self.client_post("/api/v1/remotes/server/register", request)
self.assert_json_success(result)
class TestUserPushIdentityCompat(ZulipTestCase):

View File

@ -5,7 +5,6 @@ from email.headerregistry import Address
from typing import Any, Dict, List, Optional, Type, TypedDict, TypeVar, Union
from uuid import UUID
import DNS
import orjson
from django.conf import settings
from django.core.exceptions import ValidationError
@ -18,6 +17,8 @@ from django.utils.timezone import now as timezone_now
from django.utils.translation import gettext as _
from django.utils.translation import gettext as err_
from django.views.decorators.csrf import csrf_exempt
from dns import resolver as dns_resolver
from dns.exception import DNSException
from pydantic import BaseModel, ConfigDict, Json, StringConstraints
from pydantic.functional_validators import AfterValidator
from typing_extensions import Annotated
@ -170,19 +171,25 @@ def register_remote_server(
raise JsonableError(_("Invalid email address."))
# Check if the domain has an MX record
resolver = dns_resolver.Resolver()
resolver.timeout = 3
dns_mx_check_successful = False
try:
records = DNS.mxlookup(contact_email_domain)
dns_mx_check_successful = True
if not records:
dns_mx_check_successful = False
except DNS.Base.ServerError:
dns_mx_check_successful = False
if resolver.resolve(contact_email_domain, "MX"):
dns_mx_check_successful = True
except DNSException:
pass
if not dns_mx_check_successful:
raise JsonableError(
_("{domain} does not exist or is not configured to accept email.").format(
domain=contact_email_domain
# Check if the A/AAAA exist, for better error reporting
try:
resolver.resolve_name(contact_email_domain)
raise JsonableError(
_("{domain} is invalid because it does not have any MX records").format(
domain=contact_email_domain
)
)
)
except DNSException:
raise JsonableError(_("{domain} does not exist").format(domain=contact_email_domain))
try:
validate_uuid(zulip_org_id)