All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aoindustries.aoserv.client.validator.DomainName Maven / Gradle / Ivy

There is a newer version: 1.92.0
Show newest version
/*
 * aoserv-client - Java client for the AOServ platform.
 * Copyright (C) 2010-2013, 2015, 2016  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of aoserv-client.
 *
 * aoserv-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * aoserv-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with aoserv-client.  If not, see .
 */
package com.aoindustries.aoserv.client.validator;

import com.aoindustries.aoserv.client.AOServObject;
import com.aoindustries.aoserv.client.DtoFactory;
import com.aoindustries.io.FastExternalizable;
import com.aoindustries.io.FastObjectInput;
import com.aoindustries.io.FastObjectOutput;
import com.aoindustries.util.Internable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInput;
import java.io.ObjectInputValidation;
import java.io.ObjectOutput;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Represents a DNS domain name.  Domain names must:
 * 
    *
  • Be non-null
  • *
  • Be non-empty
  • *
  • May not be "default" (case-insensitive)
  • *
  • * Confirm to definition in {@link http://en.wikipedia.org/wiki/Hostname#Internet_hostnames} * and {@link http://en.wikipedia.org/wiki/DNS_label#Parts_of_a_domain_name} *
  • *
  • May be "localhost" or "localhost.localdomain" - other checks that conflict with this are skipped.
  • *
  • Last domain label must be alphabetic (not be all numeric)
  • *
  • Last label must be a valid top level domain.
  • *
  • For reverse IP address delegation, if the domain ends with ".in-addr.arpa", the first label may also be in the format "##/##".
  • *
  • Not end with a period (.)
  • *
* * @author AO Industries, Inc. */ final public class DomainName implements Comparable, FastExternalizable, ObjectInputValidation, DtoFactory, Internable { /** * From http://data.iana.org/TLD/tlds-alpha-by-domain.txt * TODO: Update to full list (2015-09-24) */ public enum TopLevelDomain { AC, AD, AE, AERO, AF, AG, AI, AL, AM, AN, AO, AQ, AR, ARPA, AS, ASIA, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI, BIZ, BJ, BM, BN, BO, BR, BS, BT, BV, BW, BY, BZ, CA, CAT, CC, CD, CF, CG, CH, CI, CK, CL, CM, CN, CO, COM, COOP, CR, CU, CV, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EDU, EE, EG, ER, ES, ET, EU, FI, FJ, FK, FM, FO, FR, GA, GB, GD, GE, GF, GG, GH, GI, GL, GM, GN, GOV, GP, GQ, GR, GS, GT, GU, GW, GY, HK, HM, HN, HR, HT, HU, ID, IE, IL, IM, IN, INFO, INT, IO, IQ, IR, IS, IT, JE, JM, JO, JOBS, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, ME, MG, MH, MIL, MK, ML, MM, MN, MO, MOBI, MP, MQ, MR, MS, MT, MU, MUSEUM, MV, MW, MX, MY, MZ, NA, NAME, NC, NE, NET, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, ORG, PA, PE, PF, PG, PH, PK, PL, PM, PN, PR, PRO, PS, PT, PW, PY, QA, RE, RO, RS, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SJ, SK, SL, SM, SN, SO, SR, ST, SU, SV, SY, SZ, TC, TD, TEL, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TOP, TP, TR, TRAVEL, TT, TV, TW, TZ, UA, UG, UK, US, UY, UZ, VA, VC, VE, VG, VI, VN, VU, WF, WORK, WS, XN__0ZWM56D, XN__11B5BS3A9AJ6G, XN__3E0B707E, XN__45BRJ9C, XN__80AKHBYKNJ4F, XN__9T4B11YI5A, XN__CLCHC0EA0B2G2A9GCD, XN__DEBA0AD, XN__FIQS8S, XN__FIQZ9S, XN__FPCRJ9C3D, XN__FZC2C9E2C, XN__G6W251D, XN__GECRJ9C, XN__H2BRJ9C, XN__HGBK6AJ7F53BBA, XN__HLCJ6AYA9ESC7A, XN__J6W193G, XN__JXALPDLP, XN__KGBECHTV, XN__KPRW13D, XN__KPRY57D, XN__MGBAAM7A8H, XN__MGBAYH7GPA, XN__MGBBH1A71E, XN__MGBERP4A5D4AR, XN__O3CW4H, XN__OGBPF8FL, XN__P1AI, XN__PGBS0DH, XN__S9BRJ9C, XN__WGBH1C, XN__WGBL6A, XN__XKC2AL3HYE2A, XN__XKC2DL3A5EE0H, XN__YFRO4I67O, XN__YGBI2AMMX, XN__ZCKZAH, YE, YT, ZA, ZM, ZW; public static final String SOURCE_URL = "http://data.iana.org/TLD/tlds-alpha-by-domain.txt"; public static final Timestamp LAST_DOWNLOADED = new Timestamp(1300332279678L); // 2011-03-16 private final DomainLabel label; private TopLevelDomain() { try { label = DomainLabel.valueOf(name().toLowerCase(Locale.ROOT).replace('_', '-')).intern(); } catch(ValidationException exc) { throw new AssertionError(exc); } } @Override public String toString() { return label.toString(); } public DomainLabel getLabel() { return label; } private static final Map lowerTldMap; static { TopLevelDomain[] tlds = values(); lowerTldMap = new HashMap<>(tlds.length*4/3+1); for(TopLevelDomain tld : tlds) lowerTldMap.put(tld.label.toString(), tld); } /** * Provides a way to get the top level domain based on label (case insensitive). * Since the enum identifiers cannot contain hyphens (-), they have been replaced * with underscores. This also converts them back. */ public static TopLevelDomain getByLabel(String label) { return lowerTldMap.get(label.toLowerCase(Locale.ROOT)); } /** * Gets the top level domain for the provided label. * * @see #valueOfLabel(java.lang.String) */ public static TopLevelDomain getByLabel(DomainLabel label) { return getByLabel(label.toLowerCase()); } } public static final int MAX_LENGTH = 253; /* private static boolean isNumeric(String label) { return isNumeric(label, 0, label.length()); }*/ private static boolean isNumeric(String label, int start, int end) { if((end-start)<=0) throw new IllegalArgumentException("empty label"); for(int i=start; i'9') return false; } return true; } /** * Checks if is in the format numeric / numeric. */ private static boolean isArpaDelegationFirstLabel(String label, int beginIndex, int endIndex) { int slashPos = -1; for(int i=beginIndex; i=0 && domain.charAt(pos++) =='.' && ((ch=domain.charAt(pos++))=='i' || ch=='I') && ((ch=domain.charAt(pos++))=='n' || ch=='N') && domain.charAt(pos++) =='-' && ((ch=domain.charAt(pos++))=='a' || ch=='A') && ((ch=domain.charAt(pos++))=='d' || ch=='D') && ((ch=domain.charAt(pos++))=='d' || ch=='D') && ((ch=domain.charAt(pos++))=='r' || ch=='R') && domain.charAt(pos++) =='.' && ((ch=domain.charAt(pos++))=='a' || ch=='A') && ((ch=domain.charAt(pos++))=='r' || ch=='R') && ((ch=domain.charAt(pos++))=='p' || ch=='P') && ((ch=domain.charAt(pos ))=='a' || ch=='A') ; //return domain.toLowerCase(Locale.ENGLISH).endsWith(".in-addr.arpa"); } /* private static boolean isArpaBenchmark(String domain) { return domain.toLowerCase(Locale.ENGLISH).endsWith(".in-addr.arpa"); } private static void benchmark() { final int iterations = 10000000; long startTime = System.nanoTime(); for(int i=0; iMAX_LENGTH) return new InvalidResult(ApplicationResources.accessor, "DomainName.validate.tooLong", MAX_LENGTH, len); boolean isArpa = isArpa(domain); int labelStart = 0; for(int pos=0; pos interned = new ConcurrentHashMap<>(); /** * If domain is null, returns null. */ public static DomainName valueOf(String domain) throws ValidationException { if(domain==null) return null; //DomainName existing = interned.get(domain); //return existing!=null ? existing : new DomainName(domain); return new DomainName(domain); } private String domain; private String lowerDomain; private DomainName(String domain) throws ValidationException { this(domain, domain.toLowerCase(Locale.ROOT)); } private DomainName(String domain, String lowerDomain) throws ValidationException { this.domain = domain; this.lowerDomain = lowerDomain; validate(); } private void validate() throws ValidationException { ValidationResult result = validate(domain); if(!result.isValid()) throw new ValidationException(result); } @Override public boolean equals(Object O) { return O!=null && O instanceof DomainName && lowerDomain.equals(((DomainName)O).lowerDomain) ; } @Override public int hashCode() { return lowerDomain.hashCode(); } // TODO: Should not be public once all classes using validator types public static int compareLabels(String labels1, String labels2) { if(labels1==labels2) return 0; // Shortcut for interned while(labels1.length()>0 && labels2.length()>0) { int pos=labels1.lastIndexOf('.'); String section1; if(pos==-1) { section1=labels1; labels1=""; } else { section1=labels1.substring(pos+1); labels1=labels1.substring(0, pos); } pos=labels2.lastIndexOf('.'); String section2; if(pos==-1) { section2=labels2; labels2=""; } else { section2=labels2.substring(pos+1); labels2=labels2.substring(0, pos); } int diff=AOServObject.compareIgnoreCaseConsistentWithEquals(section1, section2); if(diff!=0) return diff; } return AOServObject.compareIgnoreCaseConsistentWithEquals(labels1, labels2); } /** * Sorts by top level domain, then subdomain, then sub-subdomain, ... */ @Override public int compareTo(DomainName other) { if(this==other) return 0; return compareLabels(domain, other.domain); } @Override public String toString() { return domain; } /** * Gets the lower-case form of the domain. If two different domains are * interned and their toLowerCase is the same String instance, then they are * equal in case-insensitive manner. */ public String toLowerCase() { return lowerDomain; } public boolean isArpa() { return isArpa(domain); } /** * Interns this domain much in the same fashion as String.intern(). * * @see String#intern() */ @Override public DomainName intern() { try { DomainName existing = interned.get(domain); if(existing==null) { String internedDomain = domain.intern(); String internedLowerDomain = lowerDomain.intern(); DomainName addMe = domain==internedDomain && lowerDomain==internedLowerDomain ? this : new DomainName(internedDomain); existing = interned.putIfAbsent(internedDomain, addMe); if(existing==null) existing = addMe; } return existing; } catch(ValidationException err) { // Should not fail validation since original object passed throw new AssertionError(err.getMessage()); } } @Override public com.aoindustries.aoserv.client.dto.DomainName getDto() { return new com.aoindustries.aoserv.client.dto.DomainName(domain); } // private static final long serialVersionUID = 2384488670340662487L; public DomainName() { } @Override public long getSerialVersionUID() { return serialVersionUID; } @Override public void writeExternal(ObjectOutput out) throws IOException { FastObjectOutput fastOut = FastObjectOutput.wrap(out); try { fastOut.writeFastUTF(domain); } finally { fastOut.unwrap(); } } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { if(domain!=null) throw new IllegalStateException(); FastObjectInput fastIn = FastObjectInput.wrap(in); try { domain = fastIn.readFastUTF(); lowerDomain = domain.toLowerCase(Locale.ROOT); } finally { fastIn.unwrap(); } } @Override public void validateObject() throws InvalidObjectException { try { validate(); } catch(ValidationException err) { InvalidObjectException newErr = new InvalidObjectException(err.getMessage()); newErr.initCause(err); throw newErr; } } // }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy