程序遇到DNS解析问题导致服务启动失败排查过程

2020-12-23  本文已影响0人  _火山_

前言

因为组件涉及公司信息,所以我这里就自己写个demo模拟实现当时的报错现象了。

demo代码

package dns;
import sun.security.x509.DNSName;
import java.io.IOException;
public class DNSTest {
    public static void main(String[] args) {
        try {
            DNSName dnsName = new DNSName("hdp1.test.123.com");
            System.out.println(dnsName.getName());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

错误详情:

java.io.IOException: DNSName components must begin with a letter
意思是说DNS域名的每个组成部分(组成部分以.分隔)都必须以字母开头,否则就会抛出这个异常。

根据错误堆栈跟进代码里

public DNSName(String var1, boolean var2) throws IOException {
    if (var1 != null && var1.length() != 0) {
        if (var1.indexOf(32) != -1) {
            throw new IOException("DNS names or NameConstraints with blank components are not permitted");
        } else if (var1.charAt(0) != '.' && var1.charAt(var1.length() - 1) != '.') {
            int var3;
            for(int var4 = 0; var4 < var1.length(); var4 = var3 + 1) {
                var3 = var1.indexOf(46, var4);
                if (var3 < 0) {
                    var3 = var1.length();
                }

                if (var3 - var4 < 1) {
                    throw new IOException("DNSName SubjectAltNames with empty components are not permitted");
                }

                if (!var2) {
                    if ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".indexOf(var1.charAt(var4)) < 0) {
                        throw new IOException("DNSName components must begin with a letter");
                    }
                } else {
                    char var5 = var1.charAt(var4);
                    if ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".indexOf(var5) < 0 && !Character.isDigit(var5)) {
                        throw new IOException("DNSName components must begin with a letter or digit");
                    }
                }

                for(int var7 = var4 + 1; var7 < var3; ++var7) {
                    char var6 = var1.charAt(var7);
                    if ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-".indexOf(var6) < 0) {
                        throw new IOException("DNSName components must consist of letters, digits, and hyphens");
                    }
                }
            }

            this.name = var1;
        } else {
            throw new IOException("DNS names or NameConstraints may not begin or end with a .");
        }
    } else {
        throw new IOException("DNS name must not be null");
    }
}

可以看到抛异常的代码

if ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".indexOf(var1.charAt(var4)) < 0) {
    throw new IOException("DNSName components must begin with a letter");
}

从if判断条件里可以知道,这个比较是只跟字母进行比较的,如果不在这个字母序列内,即非字母的话就抛异常。

知道了问题的原因后,我将DNS域名的每个组成部分都换成以字母开头的,程序可以正常返回了。

这个只允许字母开头的问题后来在jdk15做了修改,组成部分允许字母、数字开头了。

看下jdk15的代码

# 定义的这个常量是包含字母数字的
private static final String alphaDigits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

public DNSName(String name, boolean allowWildcard) throws IOException {
    if (name == null || name.isEmpty())
        throw new IOException("DNSName must not be null or empty");
    if (name.contains(" "))
        throw new IOException("DNSName with blank components is not permitted");
    if (name.startsWith(".") || name.endsWith("."))
        throw new IOException("DNSName may not begin or end with a .");
    /*
     * Name will consist of label components separated by "."
     * startIndex is the index of the first character of a component
     * endIndex is the index of the last character of a component plus 1
     */
    for (int endIndex,startIndex = 0; startIndex < name.length(); startIndex = endIndex+1) {
        endIndex = name.indexOf('.', startIndex);
        if (endIndex < 0) {
            endIndex = name.length();
        }
        if (endIndex - startIndex < 1)
            throw new IOException("DNSName with empty components are not permitted");

        if (allowWildcard) {
            // RFC 1123: DNSName components must begin with a letter or digit
            // or RFC 4592: the first component of a DNSName can have only a wildcard
            // character * (asterisk), i.e. *.example.com. Asterisks at other components
            // will not be allowed as a wildcard.
            if (alphaDigits.indexOf(name.charAt(startIndex)) < 0) {
                // Checking to make sure the wildcard only appears in the first component,
                // and it has to be at least 3-char long with the form of *.[alphaDigit]
                if ((name.length() < 3) || (name.indexOf('*', 0) != 0) ||
                    (name.charAt(startIndex+1) != '.') ||
                    (alphaDigits.indexOf(name.charAt(startIndex+2)) < 0))
                    throw new IOException("DNSName components must begin with a letter, digit, "
                        + "or the first component can have only a wildcard character *");
            }
        } else {
            // RFC 1123: DNSName components must begin with a letter or digit
            if (alphaDigits.indexOf(name.charAt(startIndex)) < 0)
                throw new IOException("DNSName components must begin with a letter or digit");
        }

        //nonStartIndex: index for characters in the component beyond the first one
        for (int nonStartIndex=startIndex+1; nonStartIndex < endIndex; nonStartIndex++) {
            char x = name.charAt(nonStartIndex);
            if ((alphaDigits).indexOf(x) < 0 && x != '-')
                throw new IOException("DNSName components must consist of letters, digits, and hyphens");
        }
    }
    this.name = name;
}

摘出检查组成元素的这部分代码

if ((name.length() < 3) || (name.indexOf('*', 0) != 0) || (name.charAt(startIndex+1) != '.') || (alphaDigits.indexOf(name.charAt(startIndex+2)) < 0))
    throw new IOException("DNSName components must begin with a letter, digit, or the first component can have only a wildcard character *");

可以看到它是允许字母数字开头的了,并且只有域名的第一个组成部分允许为*。

上一篇下一篇

猜你喜欢

热点阅读