2015-05-04 14:19:58 +00:00
|
|
|
import logging
|
2014-05-08 16:59:35 +00:00
|
|
|
import os
|
|
|
|
import re
|
|
|
|
|
|
|
|
from django.core.exceptions import ValidationError
|
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
|
2014-10-30 16:34:02 +00:00
|
|
|
from orchestra.core.validators import validate_hostname
|
2014-05-08 16:59:35 +00:00
|
|
|
from orchestra.utils import paths
|
2015-04-05 10:46:24 +00:00
|
|
|
from orchestra.utils.sys import run
|
2014-05-08 16:59:35 +00:00
|
|
|
|
2015-04-27 14:54:17 +00:00
|
|
|
from .. import domains
|
2014-05-08 16:59:35 +00:00
|
|
|
|
|
|
|
|
2015-05-04 14:19:58 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2014-05-08 16:59:35 +00:00
|
|
|
def validate_allowed_domain(value):
|
|
|
|
context = {
|
2015-03-31 12:39:08 +00:00
|
|
|
'site_dir': paths.get_site_dir()
|
2014-05-08 16:59:35 +00:00
|
|
|
}
|
2015-04-27 14:54:17 +00:00
|
|
|
fname = domains.settings.DOMAINS_FORBIDDEN
|
2014-05-08 16:59:35 +00:00
|
|
|
if fname:
|
|
|
|
fname = fname % context
|
|
|
|
with open(fname, 'r') as forbidden:
|
|
|
|
for domain in forbidden.readlines():
|
|
|
|
if re.match(r'^(.*\.)*%s$' % domain.strip(), value):
|
|
|
|
raise ValidationError(_("This domain name is not allowed"))
|
|
|
|
|
|
|
|
|
2014-10-30 16:34:02 +00:00
|
|
|
def validate_domain_name(value):
|
|
|
|
# SRV records may use '_' in the domain name
|
|
|
|
value = value.lstrip('*.').replace('_', '')
|
|
|
|
try:
|
|
|
|
validate_hostname(value)
|
|
|
|
except ValidationError:
|
|
|
|
raise ValidationError(_("Not a valid domain name."))
|
|
|
|
|
|
|
|
|
2014-05-08 16:59:35 +00:00
|
|
|
def validate_zone_interval(value):
|
|
|
|
try:
|
|
|
|
int(value)
|
|
|
|
except ValueError:
|
|
|
|
value, magnitude = value[:-1], value[-1]
|
|
|
|
if magnitude not in ('s', 'm', 'h', 'd', 'w') or not value.isdigit():
|
|
|
|
msg = _("%s is not an appropiate zone interval value") % value
|
|
|
|
raise ValidationError(msg)
|
|
|
|
|
|
|
|
|
|
|
|
def validate_zone_label(value):
|
|
|
|
"""
|
|
|
|
http://www.ietf.org/rfc/rfc1035.txt
|
|
|
|
The labels must follow the rules for ARPANET host names. They must
|
|
|
|
start with a letter, end with a letter or digit, and have as interior
|
|
|
|
characters only letters, digits, and hyphen. There are also some
|
|
|
|
restrictions on the length. Labels must be 63 characters or less.
|
|
|
|
"""
|
|
|
|
if not re.match(r'^[a-z][\.\-0-9a-z]*[\.0-9a-z]$', value):
|
|
|
|
msg = _("Labels must start with a letter, end with a letter or digit, "
|
|
|
|
"and have as interior characters only letters, digits, and hyphen")
|
|
|
|
raise ValidationError(msg)
|
|
|
|
if not value.endswith('.'):
|
|
|
|
msg = _("Use a fully expanded domain name ending with a dot")
|
|
|
|
raise ValidationError(msg)
|
|
|
|
if len(value) > 63:
|
|
|
|
raise ValidationError(_("Labels must be 63 characters or less"))
|
|
|
|
|
|
|
|
|
|
|
|
def validate_mx_record(value):
|
|
|
|
msg = _("%s is not an appropiate MX record value") % value
|
|
|
|
value = value.split()
|
|
|
|
if len(value) == 1:
|
|
|
|
value = value[0]
|
|
|
|
elif len(value) == 2:
|
|
|
|
try:
|
|
|
|
int(value[0])
|
|
|
|
except ValueError:
|
|
|
|
raise ValidationError(msg)
|
|
|
|
value = value[1]
|
|
|
|
elif len(value) > 2:
|
|
|
|
raise ValidationError(msg)
|
|
|
|
validate_zone_label(value)
|
|
|
|
|
|
|
|
|
|
|
|
def validate_srv_record(value):
|
|
|
|
# 1 0 9 server.example.com.
|
|
|
|
msg = _("%s is not an appropiate SRV record value") % value
|
|
|
|
value = value.split()
|
|
|
|
for i in [0,1,2]:
|
|
|
|
try:
|
|
|
|
int(value[i])
|
|
|
|
except ValueError:
|
|
|
|
raise ValidationError(msg)
|
|
|
|
validate_zone_label(value[-1])
|
|
|
|
|
|
|
|
|
|
|
|
def validate_soa_record(value):
|
|
|
|
# ns1.pangea.ORG. hostmaster.pangea.ORG. 2012010401 28800 7200 604800 86400
|
|
|
|
msg = _("%s is not an appropiate SRV record value") % value
|
|
|
|
values = value.split()
|
|
|
|
if len(values) != 7:
|
|
|
|
raise ValidationError(msg)
|
|
|
|
validate_zone_label(values[0])
|
|
|
|
validate_zone_label(values[1])
|
|
|
|
for value in values[2:]:
|
|
|
|
try:
|
|
|
|
int(value)
|
|
|
|
except ValueError:
|
|
|
|
raise ValidationError(msg)
|
|
|
|
|
|
|
|
|
|
|
|
def validate_zone(zone):
|
|
|
|
""" Ultimate zone file validation using named-checkzone """
|
|
|
|
zone_name = zone.split()[0][:-1]
|
2015-04-27 14:54:17 +00:00
|
|
|
zone_path = os.path.join(domains.settings.DOMAINS_ZONE_VALIDATION_TMP_DIR, zone_name)
|
|
|
|
checkzone = domains.settings.DOMAINS_CHECKZONE_BIN_PATH
|
2015-03-31 12:39:08 +00:00
|
|
|
try:
|
|
|
|
with open(zone_path, 'wb') as f:
|
2015-04-05 18:02:36 +00:00
|
|
|
f.write(zone.encode('ascii'))
|
2015-03-31 12:39:08 +00:00
|
|
|
# Don't use /dev/stdin becuase the 'argument list is too long' error
|
2015-05-09 17:08:45 +00:00
|
|
|
check = run(' '.join([checkzone, zone_name, zone_path]), valid_codes=(0,1,127), display=False)
|
2015-03-31 12:39:08 +00:00
|
|
|
finally:
|
|
|
|
os.unlink(zone_path)
|
2015-05-09 17:08:45 +00:00
|
|
|
if check.exit_code == 127:
|
2015-05-04 14:19:58 +00:00
|
|
|
logger.error("Cannot validate domain zone: %s not installed." % checkzone)
|
2015-05-09 17:08:45 +00:00
|
|
|
elif check.exit_code == 1:
|
2014-05-08 16:59:35 +00:00
|
|
|
errors = re.compile(r'zone.*: (.*)').findall(check.stdout)[:-1]
|
|
|
|
raise ValidationError(', '.join(errors))
|