001package org.kuali.common.dns.util;
002
003import static com.google.common.base.Preconditions.checkArgument;
004import static org.kuali.common.util.base.Precondition.checkNotBlank;
005
006import java.util.List;
007
008import org.kuali.common.util.Ascii;
009
010import com.google.common.base.Splitter;
011
012public class DnsUtils {
013
014        private static final char DOT = '.';
015        private static final char HYPHEN = '-';
016        private static final int MAX_FQDN_LENGTH = 253;
017        private static final int MAX_LABEL_LENGTH = 63;
018        private static final Splitter SPLITTER = Splitter.on(DOT);
019
020        /**
021         * <p>
022         * Verify <code>fqdn</code> is a syntactically valid DNS name.
023         * </p>
024         * 
025         * <p>
026         * See http://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax
027         * </p>
028         * 
029         * @throws IllegalArgumentException
030         *             If <code>fqdn</code> is not a syntactically valid DNS name.
031         */
032        public static void validateFQDN(String fqdn) {
033
034                // Null, the empty string, and pure whitespace are not allowed
035                checkNotBlank(fqdn, "fqdn");
036
037                // Max length is 253 characters
038                checkArgument(fqdn.length() <= MAX_FQDN_LENGTH, "[%s] is %s characters long.  Max is %s", fqdn, fqdn.length(), MAX_FQDN_LENGTH);
039
040                // Split up the string using dot as a separator
041                List<String> labels = SPLITTER.splitToList(fqdn);
042
043                // Validate each portion of the dns name
044                for (String label : labels) {
045                        validateLabel(label);
046                }
047        }
048
049        protected static void validateLabel(String label) {
050
051                // Null, the empty string, and pure whitespace are not allowed
052                checkNotBlank(label, "label");
053
054                // Max length for an individual label is 63 characters
055                checkArgument(label.length() <= MAX_LABEL_LENGTH, "[%s] is %s characters long.  Max is %s", label, label.length(), MAX_LABEL_LENGTH);
056
057                // Can't begin or end with a hyphen
058                checkArgument(label.charAt(0) != HYPHEN, "[%s] begins with %s", label, HYPHEN);
059                checkArgument(label.charAt(label.length() - 1) != HYPHEN, "[%s] ends with %s", label, HYPHEN);
060
061                // Only characters allowed are a..z, A..Z, 0..9, and the hyphen
062                checkArgument(isLetterDigitHyphen(label), "Only a..z, A..Z, 0..9, and the hyphen character are allowed");
063        }
064
065        protected static boolean isLetterDigitHyphen(String label) {
066                char[] chars = label.toCharArray();
067                for (char c : chars) {
068                        if (!isLetterDigitHyphen(c)) {
069                                return false;
070                        }
071                }
072                return true;
073        }
074
075        protected static boolean isLetterDigitHyphen(char c) {
076                return Ascii.isLetter(c) || c == HYPHEN || Ascii.isDigit(c);
077        }
078
079}