001/*
002 * Copyright (C) 2017 Neo Visionaries Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package NeoVisionaries.WebSockets;
017
018
019import javax.net.ssl.SSLSocket;
020
021
022/**
023 * The certificate of the peer does not match the expected hostname.
024 * 
025 * <EMBED CLASS='external-html' DATA-FILE-ID=LICENSE><BR />
026 *
027 * <p>
028 * {@link #getError()} of this class returns {@link WebSocketError#HOSTNAME_UNVERIFIED
029 * HOSTNAME_UNVERIFIED}.
030 * </p>
031 *
032 * <p>
033 * See <a href='https://github.com/TakahikoKawasaki/nv-websocket-client/pull/107'
034 * >Verify that certificate is valid for server hostname (#107)</a>.
035 * </p>
036 *
037 * @since 2.1
038 */
039public class HostnameUnverifiedException extends WebSocketException
040{
041    private static final long serialVersionUID = 1L;
042
043
044    private final SSLSocket mSSLSocket;
045    private final String mHostname;
046
047
048    /**
049     * Constructor with the SSL socket and the expected hostname.
050     *
051     * @param socket
052     *         The SSL socket against which the hostname verification failed.
053     *
054     * @param hostname
055     *         The expected hostname.
056     */
057    public HostnameUnverifiedException(SSLSocket socket, String hostname)
058    {
059        super(WebSocketError.HOSTNAME_UNVERIFIED,
060                String.format("The certificate of the peer%s does not match the expected hostname (%s)",
061                        stringifyPrincipal(socket), hostname));
062
063        mSSLSocket = socket;
064        mHostname  = hostname;
065    }
066
067
068    private static String stringifyPrincipal(SSLSocket socket)
069    {
070        try
071        {
072            return String.format(" (%s)", socket.getSession().getPeerPrincipal().toString());
073        }
074        catch (Exception e)
075        {
076            // Principal information is not available.
077            return "";
078        }
079    }
080
081
082    /**
083     * Get the SSL socket against which the hostname verification failed.
084     *
085     * @return
086     *         The SSL socket.
087     */
088    public SSLSocket getSSLSocket()
089    {
090        return mSSLSocket;
091    }
092
093
094    /**
095     * Get the expected hostname.
096     *
097     * @return
098     *         The expected hostname.
099     */
100    public String getHostname()
101    {
102        return mHostname;
103    }
104}