package org.bouncycastle.jsse;

import java.io.UnsupportedEncodingException;
import java.util.Locale;
import java.util.regex.Pattern;

public final class BCSNIHostName extends BCSNIServerName
{
    public static BCSNIMatcher createSNIMatcher(String regex)
    {
        if (regex == null)
        {
            throw new NullPointerException("'regex' cannot be null");
        }

        return new BCSNIHostNameMatcher(regex);
    }

    private final String hostName;

    private static String fromAscii(byte[] bs)
    {
        try
        {
            return new String(bs, "ASCII");
        }
        catch (UnsupportedEncodingException e)
        {
            throw new RuntimeException(e);
        }
    }

    private static byte[] toAscii(String s)
    {
        try
        {
            return s.getBytes("ASCII");
        }
        catch (UnsupportedEncodingException e)
        {
            throw new RuntimeException(e);
        }
    }

    public BCSNIHostName(String hostName)
    {
        super(BCStandardConstants.SNI_HOST_NAME, toAscii(hostName));

        this.hostName = hostName;
    }

    public BCSNIHostName(byte[] asciiEncoding)
    {
        super(BCStandardConstants.SNI_HOST_NAME, asciiEncoding);

        this.hostName = fromAscii(asciiEncoding);
    }

    public String getAsciiName()
    {
        return hostName;
    }

    public boolean equals(Object obj)
    {
        if (this == obj)
        {
            return true;
        }
        if (!(obj instanceof BCSNIHostName))
        {
            return false;
        }
        BCSNIHostName other = (BCSNIHostName)obj;
        return hostName.equalsIgnoreCase(other.hostName);
    }

    public int hashCode()
    {
        return hostName.toUpperCase(Locale.ENGLISH).hashCode();
    }

    private static final class BCSNIHostNameMatcher
        extends BCSNIMatcher
    {
        private final Pattern pattern;

        BCSNIHostNameMatcher(String regex)
        {
            super(BCStandardConstants.SNI_HOST_NAME);

            this.pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        }

        public boolean matches(BCSNIServerName serverName)
        {
            if (serverName == null || serverName.getType() != BCStandardConstants.SNI_HOST_NAME)
            {
                return false;
            }

            String hostName = fromAscii(serverName.getEncoded());

            return pattern.matcher(hostName).matches();
        }
    }
}

@@ -41,96 +41,137 @@
 ava.
-io.UnsupportedEncodingException;%0Aimport java.util.Locale;%0Aimport java.util.regex.Pattern
+util.Locale;%0Aimport java.util.regex.Pattern;%0A%0Aimport org.bouncycastle.jsse.provider.IDNUtil;%0Aimport org.bouncycastle.util.Strings
 ;%0A%0Ap
@@ -509,493 +509,8 @@
 e;%0A%0A
-    private static String fromAscii(byte%5B%5D bs)%0A    %7B%0A        try%0A        %7B%0A            return new String(bs, %22ASCII%22);%0A        %7D%0A        catch (UnsupportedEncodingException e)%0A        %7B%0A            throw new RuntimeException(e);%0A        %7D%0A    %7D%0A%0A    private static byte%5B%5D toAscii(String s)%0A    %7B%0A        try%0A        %7B%0A            return s.getBytes(%22ASCII%22);%0A        %7D%0A        catch (UnsupportedEncodingException e)%0A        %7B%0A            throw new RuntimeException(e);%0A        %7D%0A    %7D%0A%0A
     
@@ -606,15 +606,56 @@
 ME, 
-toAscii
+Strings.toByteArray(hostName = normalizeHostName
 (hos
@@ -661,16 +661,17 @@
 stName))
+)
 ;%0A%0A     
@@ -738,21 +738,20 @@
 (byte%5B%5D 
-ascii
+utf8
 Encoding
@@ -807,21 +807,20 @@
 T_NAME, 
-ascii
+utf8
 Encoding
@@ -851,23 +851,56 @@
 e = 
-fromAscii(ascii
+normalizeHostName(Strings.fromUTF8ByteArray(utf8
 Enco
@@ -904,16 +904,17 @@
 ncoding)
+)
 ;%0A    %7D%0A
@@ -1400,32 +1400,618 @@
 hCode();%0A    %7D%0A%0A
+    private static String normalizeHostName(String hostName)%0A    %7B%0A        if (null == hostName)%0A        %7B%0A            throw new NullPointerException(%22'hostName' cannot be null%22);%0A        %7D%0A%0A        hostName = IDNUtil.toASCII(hostName, IDNUtil.USE_STD3_ASCII_RULES);%0A%0A        if (hostName.length() %3C 1)%0A        %7B%0A            throw new IllegalArgumentException(%22SNI host_name cannot be empty%22);%0A        %7D%0A        if (hostName.endsWith(%22.%22))%0A        %7B%0A            throw new IllegalArgumentException(%22SNI host_name cannot end with a separator%22);%0A        %7D%0A%0A        return hostName;%0A    %7D%0A%0A
     private stat
@@ -2406,29 +2406,15 @@
 if (
-serverName == 
 null 
-%7C%7C
+==
  ser
@@ -2424,21 +2424,11 @@
 Name
-.getType() !=
+ %7C%7C
  BCS
@@ -2457,16 +2457,40 @@
 OST_NAME
+ != serverName.getType()
 )%0A      
@@ -2565,109 +2565,912 @@
 ing 
-hostName = fromAscii(serverName.getEncoded());%0A%0A            return pattern.matcher(hostName).matches(
+asciiName;%0A            try%0A            %7B%0A                asciiName = getAsciiHostName(serverName);%0A            %7D%0A            catch (RuntimeException e)%0A            %7B%0A                return false;%0A            %7D%0A%0A            if (pattern.matcher(asciiName).matches())%0A            %7B%0A                return true;%0A            %7D%0A%0A            String unicodeName = IDNUtil.toUnicode(asciiName, 0);%0A            if (!asciiName.equals(unicodeName)%0A                && pattern.matcher(unicodeName).matches())%0A            %7B%0A                return true;%0A            %7D%0A%0A            return false;%0A        %7D%0A%0A        private String getAsciiHostName(BCSNIServerName serverName)%0A        %7B%0A            if (serverName instanceof BCSNIHostName)%0A            %7B%0A                return ((BCSNIHostName)serverName).getAsciiName();%0A            %7D%0A%0A            return normalizeHostName(Strings.fromUTF8ByteArray(serverName.getEncoded())
 );%0A 

Update BCSNIHostName for compatibility with SNIHostName