1 /* 2 * Hunt - A data validation for DLang based on hunt library. 3 * 4 * Copyright (C) 2015-2019, HuntLabs 5 * 6 * Website: https://www.huntlabs.net 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 module hunt.validation.util.DomainNameUtil; 12 13 import std.algorithm.searching; 14 import std.regex; 15 import std.string; 16 17 class DomainNameUtil { 18 19 /** 20 * This is the maximum length of a domain name. But be aware that each label (parts separated by a dot) of the 21 * domain name must be at most 63 characters long. This is verified by {@link IDN#toASCII(string)}. 22 */ 23 private static const int MAX_DOMAIN_PART_LENGTH = 255; 24 25 private static const string DOMAIN_CHARS_WITHOUT_DASH = "[a-z\u0080-\uFFFF0-9!#$%&'*+/=?^_`{|}~]"; 26 private static const string DOMAIN_LABEL = "(" ~ DOMAIN_CHARS_WITHOUT_DASH ~ "-*)*" ~ DOMAIN_CHARS_WITHOUT_DASH ~ "+"; 27 private static const string DOMAIN = DOMAIN_LABEL ~ "+(\\." ~ DOMAIN_LABEL ~ "+)*"; 28 29 private static const string IP_DOMAIN = "[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"; 30 //IP v6 regex taken from http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses 31 private static const string IP_V6_DOMAIN = "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"; 32 33 /** 34 * Regular expression for the domain part of an URL 35 * <p> 36 * A host string must be a domain string, an IPv4 address string, or "[", followed by an IPv6 address string, 37 * followed by "]". 38 */ 39 private static const string DOMAIN_PATTERN = 40 DOMAIN ~ "|\\[" ~ IP_V6_DOMAIN ~ "\\]"; 41 42 /** 43 * Regular expression for the domain part of an email address (everything after '@') 44 */ 45 private static const string EMAIL_DOMAIN_PATTERN = 46 DOMAIN ~ "|\\[" ~ IP_DOMAIN ~ "\\]|" ~ "\\[IPv6:" ~ IP_V6_DOMAIN ~ "\\]"; 47 48 private this() { 49 } 50 51 /** 52 * Checks the validity of the domain name used in an email. To be valid it should be either a valid host name, or an 53 * IP address wrapped in []. 54 * 55 * @param domain domain to check for validity 56 * @return {@code true} if the provided string is a valid domain, {@code false} otherwise 57 */ 58 public static bool isValidEmailDomainAddress(string domain) { 59 return isValidDomainAddress( domain, EMAIL_DOMAIN_PATTERN ); 60 } 61 62 /** 63 * Checks validity of a domain name. 64 * 65 * @param domain the domain to check for validity 66 * @return {@code true} if the provided string is a valid domain, {@code false} otherwise 67 */ 68 public static bool isValidDomainAddress(string domain) { 69 return isValidDomainAddress( domain, DOMAIN_PATTERN ); 70 } 71 72 private static bool isValidDomainAddress(string domain, string pattern) { 73 // if we have a trailing dot the domain part we have an invalid email address. 74 // the regular expression match would take care of this, but IDN.toASCII drops the trailing '.' 75 if ( domain.endsWith( "." ) ) { 76 return false; 77 } 78 79 auto matcher = matchAll( domain , regex(pattern)); 80 if ( matcher.empty() ) { 81 return false; 82 } 83 84 85 86 if ( domain.length > MAX_DOMAIN_PART_LENGTH ) { 87 return false; 88 } 89 90 return true; 91 } 92 93 }