Fix FQDN checks, closes #3057 and #3056
diff --git a/certbot/tests/cli_test.py b/certbot/tests/cli_test.py
index 9c81c070bbd..89655083778 100644
--- a/certbot/tests/cli_test.py
+++ b/certbot/tests/cli_test.py
@@ -342,11 +342,11 @@ def test_check_config_sanity_domain(self):
         # FQDN
         self.assertRaises(errors.ConfigurationError,
                           self._call,
-                          ['-d', 'comma,gotwrong.tld'])
+                          ['-d', 'a' * 64])
         # FQDN 2
         self.assertRaises(errors.ConfigurationError,
                           self._call,
-                          ['-d', 'illegal.character=.tld'])
+                          ['-d', (('a' * 50) + '.') * 10])
         # Wildcard
         self.assertRaises(errors.ConfigurationError,
                           self._call,
diff --git a/certbot/tests/display/ops_test.py b/certbot/tests/display/ops_test.py
index 3aff37d86f3..26f67b69f90 100644
--- a/certbot/tests/display/ops_test.py
+++ b/certbot/tests/display/ops_test.py
@@ -248,9 +248,9 @@ def test_filter_names_cancel(self, mock_util):
     def test_get_valid_domains(self):
         from certbot.display.ops import get_valid_domains
         all_valid = ["example.com", "second.example.com",
-                     "also.example.com"]
-        all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "notFQDN",
-                       "uniçodé.com"]
+                     "also.example.com", "under_score.example.com",
+                     "justtld"]
+        all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "uniçodé.com"]
         two_valid = ["example.com", "xn--ls8h.tld", "also.example.com"]
         self.assertEqual(get_valid_domains(all_valid), all_valid)
         self.assertEqual(get_valid_domains(all_invalid), [])
@@ -276,19 +276,18 @@ def test_choose_manually(self, mock_util):
         mock_util().input.return_value = (display_util.OK,
                                           "xn--ls8h.tld")
         self.assertEqual(_choose_names_manually(), [])
-        # non-FQDN and no retry
-        mock_util().input.return_value = (display_util.OK,
-                                          "notFQDN")
-        self.assertEqual(_choose_names_manually(), [])
-        # Two valid domains
+        # Valid domains
         mock_util().input.return_value = (display_util.OK,
                                           ("example.com,"
+                                           "under_score.example.com,"
+                                           "justtld,"
                                            "valid.example.com"))
         self.assertEqual(_choose_names_manually(),
-                         ["example.com", "valid.example.com"])
+                         ["example.com", "under_score.example.com",
+                          "justtld", "valid.example.com"])
         # Three iterations
         mock_util().input.return_value = (display_util.OK,
-                                          "notFQDN")
+                                          "uniçodé.com")
         yn = mock.MagicMock()
         yn.side_effect = [True, True, False]
         mock_util().yesno = yn
diff --git a/certbot/util.py b/certbot/util.py
index 35c599737c7..301fc669b6c 100644
--- a/certbot/util.py
+++ b/certbot/util.py
@@ -423,14 +423,17 @@ def enforce_domain_sanity(domain):
         # It wasn't an IP address, so that's good
         pass
 
-    # FQDN checks from
-    # http://www.mkyong.com/regular-expressions/domain-name-regular-expression-example/
-    #  Characters used, domain parts < 63 chars, tld > 1 < 64 chars
-    #  first and last char is not "-"
-    fqdn = re.compile("^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,63}$")
-    if not fqdn.match(domain):
-        raise errors.ConfigurationError("Requested domain {0} is not a FQDN"
-                                        .format(domain))
+    # FQDN checks according to RFC 2181: domain name should be less than 255
+    # octets (inclusive). And each label is 1 - 63 octets (inclusive).
+    # https://tools.ietf.org/html/rfc2181#section-11
+    msg = "Requested domain {0} is not a FQDN because ".format(domain)
+    labels = domain.split('.')
+    for l in labels:
+        if not 0 < len(l) < 64:
+            raise errors.ConfigurationError(msg + "label {0} is too long.".format(l))
+    if len(domain) > 255:
+        raise errors.ConfigurationError(msg + "it is too long.")
+
     return domain
 
 

To test this fixes #3057 try

```
# ./certbot-auto certonly --standalone --email wkoszek@mycompany.com -d wkoszek_nc2.x.mycompany.com 
```

To test this fixes #3056 try

```
# ./certbot-auto certonly --standalone --email wkoszek@mycompany.com -d ai
```

These no longer raise configuration errors.

To fix this I changed the `certbot.le_util.enforce_domain_sanity`  check to match what [RFC 2181](https://tools.ietf.org/html/rfc2181#section-11) specifies as a valid domain name.
