001/*
002 * $HeadURL: file:///opt/dev/not-yet-commons-ssl-SVN-repo/tags/commons-ssl-0.3.17/src/java/org/apache/commons/ssl/Util.java $
003 * $Revision: 180 $
004 * $Date: 2014-09-23 11:33:47 -0700 (Tue, 23 Sep 2014) $
005 *
006 * ====================================================================
007 * Licensed to the Apache Software Foundation (ASF) under one
008 * or more contributor license agreements.  See the NOTICE file
009 * distributed with this work for additional information
010 * regarding copyright ownership.  The ASF licenses this file
011 * to you under the Apache License, Version 2.0 (the
012 * "License"); you may not use this file except in compliance
013 * with the License.  You may obtain a copy of the License at
014 *
015 *   http://www.apache.org/licenses/LICENSE-2.0
016 *
017 * Unless required by applicable law or agreed to in writing,
018 * software distributed under the License is distributed on an
019 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020 * KIND, either express or implied.  See the License for the
021 * specific language governing permissions and limitations
022 * under the License.
023 * ====================================================================
024 *
025 * This software consists of voluntary contributions made by many
026 * individuals on behalf of the Apache Software Foundation.  For more
027 * information on the Apache Software Foundation, please see
028 * <http://www.apache.org/>.
029 *
030 */
031
032package org.apache.commons.ssl;
033
034import org.apache.commons.ssl.util.ByteArrayReadLine;
035import org.apache.commons.ssl.util.IPAddressParser;
036
037import java.io.ByteArrayInputStream;
038import java.io.File;
039import java.io.FileInputStream;
040import java.io.IOException;
041import java.io.InputStream;
042import java.io.OutputStream;
043import java.net.InetAddress;
044import java.net.UnknownHostException;
045import java.security.KeyStore;
046import java.security.KeyStoreException;
047import java.security.cert.Certificate;
048import java.util.Arrays;
049import java.util.Enumeration;
050import java.util.LinkedList;
051import java.util.Map;
052import java.util.Set;
053import java.util.StringTokenizer;
054import java.util.TreeMap;
055import java.util.TreeSet;
056
057/**
058 * @author Credit Union Central of British Columbia
059 * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
060 * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
061 * @since 28-Feb-2006
062 */
063public class Util {
064    public final static int SIZE_KEY = 0;
065    public final static int LAST_READ_KEY = 1;
066
067    /**
068     * True if the Keystores have the same # of entries, have the same set of aliases, and all the certificate-chains
069     * (of the certificate entries) match.   Does not check the private keys for equality, since we
070     * don't bother taking the passwords to get at them.
071     */
072    public static boolean equals(KeyStore ks1, KeyStore ks2) throws KeyStoreException {
073        if (ks1 == null || ks2 == null) {
074            return ks1 == null && ks2 == null;
075        }
076        Set<String> aliases1 = aliases(ks1);
077        Set<String> aliases2 = aliases(ks2);
078        if (aliases1.equals(aliases2)) {
079            for (String s : aliases1) {
080                if (ks1.isCertificateEntry(s) != ks2.isCertificateEntry(s)) {
081                    return false;
082                }
083                if (ks1.isKeyEntry(s) != ks2.isKeyEntry(s)) {
084                    return false;
085                }
086                if (ks1.isCertificateEntry(s)) {
087                    Certificate[] cc1 = ks1.getCertificateChain(s);
088                    Certificate[] cc2 = ks2.getCertificateChain(s);
089                    if (!Arrays.equals(cc1, cc2)) {
090                        return false;
091                    }
092
093                    Certificate c1 = ks1.getCertificate(s);
094                    Certificate c2 = ks2.getCertificate(s);
095                    if (!c1.equals(c2)) {
096                        return false;
097                    }
098                }
099
100                // should we bother checking keys?   maybe one day....
101            }
102        }
103        return true;
104    }
105
106    private static Set<String> aliases(KeyStore ks) throws KeyStoreException {
107        Set<String> aliases = new TreeSet<String>();
108        Enumeration<String> en = ks.aliases();
109        while (en.hasMoreElements()) {
110            aliases.add(en.nextElement());
111        }
112        return aliases;
113    }
114
115    public static boolean isYes(String yesString) {
116        if (yesString == null) {
117            return false;
118        }
119        String s = yesString.trim().toUpperCase();
120        return "1".equals(s) || "YES".equals(s) || "TRUE".equals(s) ||
121               "ENABLE".equals(s) || "ENABLED".equals(s) || "Y".equals(s) ||
122               "ON".equals(s);
123    }
124
125    public static String trim(final String s) {
126        if (s == null || "".equals(s)) {
127            return s;
128        }
129        int i = 0;
130        int j = s.length() - 1;
131        while (isWhiteSpace(s.charAt(i))) {
132            i++;
133        }
134        while (isWhiteSpace(s.charAt(j))) {
135            j--;
136        }
137        return j >= i ? s.substring(i, j + 1) : "";
138    }
139
140    public static boolean isWhiteSpace(final char c) {
141        switch (c) {
142            case 0:
143            case ' ':
144            case '\t':
145            case '\n':
146            case '\r':
147            case '\f':
148                return true;
149            default:
150                return false;
151        }
152    }
153
154    public static void pipeStream(InputStream in, OutputStream out)
155        throws IOException {
156        pipeStream(in, out, true);
157    }
158
159    public static void pipeStream(InputStream in, OutputStream out,
160                                  boolean autoClose)
161        throws IOException {
162        byte[] buf = new byte[8192];
163        IOException ioe = null;
164        try {
165            int bytesRead = in.read(buf);
166            while (bytesRead >= 0) {
167                if (bytesRead > 0) {
168                    out.write(buf, 0, bytesRead);
169                }
170                bytesRead = in.read(buf);
171            }
172        }
173        finally {
174            // Probably it's best to let consumer call "close", but I'm usually
175            // the consumer, and I want to be lazy.  [Julius, November 20th, 2006]
176            try { in.close(); } catch (IOException e) { ioe = e; }
177            if (autoClose) {
178                try { out.close(); } catch (IOException e) { ioe = e; }
179            }
180        }
181        if (ioe != null) {
182            throw ioe;
183        }
184    }
185
186    public static byte[] fileToBytes(final File f) throws IOException {
187        return streamToBytes(new FileInputStream(f));
188    }
189
190    public static byte[] streamToBytes(final ByteArrayInputStream in,
191                                       int maxLength) {
192        byte[] buf = new byte[maxLength];
193        int[] status = fill(buf, 0, in);
194        int size = status[SIZE_KEY];
195        if (buf.length != size) {
196            byte[] smallerBuf = new byte[size];
197            System.arraycopy(buf, 0, smallerBuf, 0, size);
198            buf = smallerBuf;
199        }
200        return buf;
201    }
202
203    public static byte[] streamToBytes(final InputStream in, int maxLength)
204        throws IOException {
205        byte[] buf = new byte[maxLength];
206        int[] status = fill(buf, 0, in);
207        int size = status[SIZE_KEY];
208        if (buf.length != size) {
209            byte[] smallerBuf = new byte[size];
210            System.arraycopy(buf, 0, smallerBuf, 0, size);
211            buf = smallerBuf;
212        }
213        return buf;
214    }
215
216    public static byte[] streamToBytes(final InputStream in) throws IOException {
217        byte[] buf = new byte[4096];
218        try {
219            int[] status = fill(buf, 0, in);
220            int size = status[SIZE_KEY];
221            int lastRead = status[LAST_READ_KEY];
222            while (lastRead != -1) {
223                buf = resizeArray(buf);
224                status = fill(buf, size, in);
225                size = status[SIZE_KEY];
226                lastRead = status[LAST_READ_KEY];
227            }
228            if (buf.length != size) {
229                byte[] smallerBuf = new byte[size];
230                System.arraycopy(buf, 0, smallerBuf, 0, size);
231                buf = smallerBuf;
232            }
233        }
234        finally {
235            in.close();
236        }
237        return buf;
238    }
239
240    public static byte[] streamToBytes(final ByteArrayInputStream in) {
241        byte[] buf = new byte[4096];
242        int[] status = fill(buf, 0, in);
243        int size = status[SIZE_KEY];
244        int lastRead = status[LAST_READ_KEY];
245        while (lastRead != -1) {
246            buf = resizeArray(buf);
247            status = fill(buf, size, in);
248            size = status[SIZE_KEY];
249            lastRead = status[LAST_READ_KEY];
250        }
251        if (buf.length != size) {
252            byte[] smallerBuf = new byte[size];
253            System.arraycopy(buf, 0, smallerBuf, 0, size);
254            buf = smallerBuf;
255        }
256        // in.close();  <-- this is a no-op on ByteArrayInputStream.
257        return buf;
258    }
259
260    public static int[] fill(final byte[] buf, final int offset,
261                             final InputStream in)
262        throws IOException {
263        int read = in.read(buf, offset, buf.length - offset);
264        int lastRead = read;
265        if (read == -1) {
266            read = 0;
267        }
268        while (lastRead != -1 && read + offset < buf.length) {
269            lastRead = in.read(buf, offset + read, buf.length - read - offset);
270            if (lastRead != -1) {
271                read += lastRead;
272            }
273        }
274        return new int[]{offset + read, lastRead};
275    }
276
277    public static int[] fill(final byte[] buf, final int offset,
278                             final ByteArrayInputStream in) {
279        int read = in.read(buf, offset, buf.length - offset);
280        int lastRead = read;
281        if (read == -1) {
282            read = 0;
283        }
284        while (lastRead != -1 && read + offset < buf.length) {
285            lastRead = in.read(buf, offset + read, buf.length - read - offset);
286            if (lastRead != -1) {
287                read += lastRead;
288            }
289        }
290        return new int[]{offset + read, lastRead};
291    }
292
293    public static byte[] resizeArray(final byte[] bytes) {
294        byte[] biggerBytes = new byte[bytes.length * 2];
295        System.arraycopy(bytes, 0, biggerBytes, 0, bytes.length);
296        return biggerBytes;
297    }
298
299    public static String pad(String s, final int length, final boolean left) {
300        if (s == null) {
301            s = "";
302        }
303        int diff = length - s.length();
304        if (diff == 0) {
305            return s;
306        } else if (diff > 0) {
307            StringBuffer sb = new StringBuffer();
308            if (left) {
309                for (int i = 0; i < diff; i++) {
310                    sb.append(' ');
311                }
312            }
313            sb.append(s);
314            if (!left) {
315                for (int i = 0; i < diff; i++) {
316                    sb.append(' ');
317                }
318            }
319            return sb.toString();
320        } else {
321            return s;
322        }
323    }
324
325    public static Map parseArgs(final String[] cargs) {
326        Map args = new TreeMap();
327        Map ARGS_MATCH = Ping.ARGS_MATCH;
328
329        int l = cargs.length;
330        final String[] EMPTY_VALUES = {""};
331        for (int i = 0; i < l; i++) {
332            String k = cargs[i];
333            Ping.Arg a = (Ping.Arg) ARGS_MATCH.get(k);
334            if (l > i + 1) {
335                String v = cargs[++i];
336                while (ARGS_MATCH.containsKey(v)) {
337                    args.put(a, EMPTY_VALUES);
338                    a = (Ping.Arg) ARGS_MATCH.get(v);
339                    v = "";
340                    if (l > i + 1) {
341                        v = cargs[++i];
342                    }
343                }
344                String[] values = new String[1];
345                values[0] = v;
346                args.put(a, values);
347                if (l > i + 1 && !ARGS_MATCH.containsKey(cargs[i + 1])) {
348                    LinkedList list = new LinkedList();
349                    list.add(v);
350                    while (l > i + 1 && !ARGS_MATCH.containsKey(cargs[i + 1])) {
351                        v = cargs[++i];
352                        list.add(v);
353                    }
354                    args.put(a, list.toArray(new String[list.size()]));
355                }
356            } else {
357                args.put(a, EMPTY_VALUES);
358            }
359        }
360        return args;
361    }
362
363    public static HostPort toAddress(final String target,
364                                     final int defaultPort)
365        throws UnknownHostException {
366        String host = target;
367        int port = defaultPort;
368        StringTokenizer st = new StringTokenizer(target, ":");
369        if (st.hasMoreTokens()) {
370            host = st.nextToken().trim();
371        }
372        if (st.hasMoreTokens()) {
373            port = Integer.parseInt(st.nextToken().trim());
374        }
375        if (st.hasMoreTokens()) {
376            throw new IllegalArgumentException("Invalid host: " + target);
377        }
378        return new HostPort(host, port);
379    }
380
381    public static String cipherToAuthType(String cipher) {
382        if (cipher == null) {
383            return null;
384        }
385
386        // SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA  ==> "DHE_DSS_EXPORT"
387        // SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA      ==> "DHE_DSS"
388        // SSL_RSA_WITH_3DES_EDE_CBC_SHA          ==> "RSA"
389
390        StringTokenizer st = new StringTokenizer(cipher.trim(), "_");
391        if (st.hasMoreTokens()) {
392            st.nextToken();  // always skip first token
393        }
394        if (st.hasMoreTokens()) {
395            String tok = st.nextToken();
396            StringBuffer buf = new StringBuffer();
397            buf.append(tok);
398            if (st.hasMoreTokens()) {
399                tok = st.nextToken();
400                while (!"WITH".equalsIgnoreCase(tok)) {
401                    buf.append('_');
402                    buf.append(tok);
403                    tok = st.nextToken();
404                }
405            }
406            return buf.toString();
407        }
408        throw new IllegalArgumentException("not a valid cipher: " + cipher);
409    }
410
411    /**
412     * Utility method to make sure IP-literals don't trigger reverse-DNS lookups.
413     */
414    public static InetAddress toInetAddress(String s) throws UnknownHostException {
415        byte[] ip = IPAddressParser.parseIPv4Literal(s);
416        if (ip == null) {
417            ip = IPAddressParser.parseIPv6Literal(s);
418        }
419        if (ip != null) {
420            // Strangely, this prevents Java's annoying SSL reverse-DNS lookup that it
421            // normally does, even with literal IP addresses.
422            return InetAddress.getByAddress(s, ip);
423        } else {
424            return InetAddress.getByName(s);
425        }
426    }
427
428    public static void main(String[] args) throws Exception {
429        String s = "line1\n\rline2\n\rline3";
430        ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes());
431        ByteArrayReadLine readLine = new ByteArrayReadLine(in);
432        String line = readLine.next();
433        while (line != null) {
434            System.out.println(line);
435            line = readLine.next();
436        }
437
438        System.out.println("--------- test 2 ----------");
439
440        s = "line1\n\rline2\n\rline3\n\r\n\r";
441        in = new ByteArrayInputStream(s.getBytes());
442        readLine = new ByteArrayReadLine(in);
443        line = readLine.next();
444        while (line != null) {
445            System.out.println(line);
446            line = readLine.next();
447        }
448
449    }
450
451
452}