# HG changeset patch # User weijun # Date 1289262851 -28800 # Node ID 46ec4c2dbc16350410a4289530a2d29c77a90ad3 # Parent 5a56e43d896c76ac022a7a251a436f37cb9a6a59 6952519: kdc_timeout is not being honoured when using TCP Reviewed-by: valeriep diff -r 5a56e43d896c -r 46ec4c2dbc16 jdk/src/share/classes/sun/security/krb5/KrbKdcReq.java --- a/jdk/src/share/classes/sun/security/krb5/KrbKdcReq.java Mon Nov 08 09:29:18 2010 -0800 +++ b/jdk/src/share/classes/sun/security/krb5/KrbKdcReq.java Tue Nov 09 08:34:11 2010 +0800 @@ -36,8 +36,7 @@ import java.security.Security; import java.util.Locale; import sun.security.krb5.internal.Krb5; -import sun.security.krb5.internal.UDPClient; -import sun.security.krb5.internal.TCPClient; +import sun.security.krb5.internal.NetClient; import java.io.IOException; import java.net.SocketTimeoutException; import java.util.StringTokenizer; @@ -349,12 +348,16 @@ byte[] ibuf = null; - if (useTCP) { - TCPClient kdcClient = new TCPClient(kdc, port); + for (int i=1; i <= retries; i++) { + String proto = useTCP?"TCP":"UDP"; + NetClient kdcClient = NetClient.getInstance( + proto, kdc, port, timeout); if (DEBUG) { System.out.println(">>> KDCCommunication: kdc=" + kdc - + " TCP:" - + port + + " " + proto + ":" + + port + ", timeout=" + + timeout + + ",Attempt =" + i + ", #bytes=" + obuf.length); } try { @@ -366,51 +369,19 @@ * And get a response. */ ibuf = kdcClient.receive(); + break; + } catch (SocketTimeoutException se) { + if (DEBUG) { + System.out.println ("SocketTimeOutException with " + + "attempt: " + i); + } + if (i == retries) { + ibuf = null; + throw se; + } } finally { kdcClient.close(); } - - } else { - // For each KDC we try defaultKdcRetryLimit times to - // get the response - for (int i=1; i <= retries; i++) { - UDPClient kdcClient = new UDPClient(kdc, port, timeout); - - if (DEBUG) { - System.out.println(">>> KDCCommunication: kdc=" + kdc - + (useTCP ? " TCP:":" UDP:") - + port + ", timeout=" - + timeout - + ",Attempt =" + i - + ", #bytes=" + obuf.length); - } - try { - /* - * Send the data to the kdc. - */ - - kdcClient.send(obuf); - - /* - * And get a response. - */ - try { - ibuf = kdcClient.receive(); - break; - } catch (SocketTimeoutException se) { - if (DEBUG) { - System.out.println ("SocketTimeOutException with " + - "attempt: " + i); - } - if (i == retries) { - ibuf = null; - throw se; - } - } - } finally { - kdcClient.close(); - } - } } return ibuf; } diff -r 5a56e43d896c -r 46ec4c2dbc16 jdk/src/share/classes/sun/security/krb5/internal/NetClient.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/security/krb5/internal/NetClient.java Tue Nov 09 08:34:11 2010 +0800 @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import java.io.*; +import java.net.*; + +public abstract class NetClient { + public static NetClient getInstance(String protocol, String hostname, int port, + int timeout) throws IOException { + if (protocol.equals("TCP")) { + return new TCPClient(hostname, port, timeout); + } else { + return new UDPClient(hostname, port, timeout); + } + } + + abstract public void send(byte[] data) throws IOException; + + abstract public byte[] receive() throws IOException; + + abstract public void close() throws IOException; +} + +class TCPClient extends NetClient { + + private Socket tcpSocket; + private BufferedOutputStream out; + private BufferedInputStream in; + + TCPClient(String hostname, int port, int timeout) + throws IOException { + tcpSocket = new Socket(hostname, port); + out = new BufferedOutputStream(tcpSocket.getOutputStream()); + in = new BufferedInputStream(tcpSocket.getInputStream()); + tcpSocket.setSoTimeout(timeout); + } + + @Override + public void send(byte[] data) throws IOException { + byte[] lenField = new byte[4]; + intToNetworkByteOrder(data.length, lenField, 0, 4); + out.write(lenField); + + out.write(data); + out.flush(); + } + + @Override + public byte[] receive() throws IOException { + byte[] lenField = new byte[4]; + int count = readFully(lenField, 4); + + if (count != 4) { + if (Krb5.DEBUG) { + System.out.println( + ">>>DEBUG: TCPClient could not read length field"); + } + return null; + } + + int len = networkByteOrderToInt(lenField, 0, 4); + if (Krb5.DEBUG) { + System.out.println( + ">>>DEBUG: TCPClient reading " + len + " bytes"); + } + if (len <= 0) { + if (Krb5.DEBUG) { + System.out.println( + ">>>DEBUG: TCPClient zero or negative length field: "+len); + } + return null; + } + + byte data[] = new byte[len]; + count = readFully(data, len); + if (count != len) { + if (Krb5.DEBUG) { + System.out.println( + ">>>DEBUG: TCPClient could not read complete packet (" + + len + "/" + count + ")"); + } + return null; + } else { + return data; + } + } + + @Override + public void close() throws IOException { + tcpSocket.close(); + } + + /** + * Read requested number of bytes before returning. + * @return The number of bytes actually read; -1 if none read + */ + private int readFully(byte[] inBuf, int total) throws IOException { + int count, pos = 0; + + while (total > 0) { + count = in.read(inBuf, pos, total); + + if (count == -1) { + return (pos == 0? -1 : pos); + } + pos += count; + total -= count; + } + return pos; + } + + /** + * Returns the integer represented by 4 bytes in network byte order. + */ + private static int networkByteOrderToInt(byte[] buf, int start, + int count) { + if (count > 4) { + throw new IllegalArgumentException( + "Cannot handle more than 4 bytes"); + } + + int answer = 0; + + for (int i = 0; i < count; i++) { + answer <<= 8; + answer |= ((int)buf[start+i] & 0xff); + } + return answer; + } + + /** + * Encodes an integer into 4 bytes in network byte order in the buffer + * supplied. + */ + private static void intToNetworkByteOrder(int num, byte[] buf, + int start, int count) { + if (count > 4) { + throw new IllegalArgumentException( + "Cannot handle more than 4 bytes"); + } + + for (int i = count-1; i >= 0; i--) { + buf[start+i] = (byte)(num & 0xff); + num >>>= 8; + } + } +} + +class UDPClient extends NetClient { + InetAddress iaddr; + int iport; + int bufSize = 65507; + DatagramSocket dgSocket; + DatagramPacket dgPacketIn; + + UDPClient(String hostname, int port, int timeout) + throws UnknownHostException, SocketException { + iaddr = InetAddress.getByName(hostname); + iport = port; + dgSocket = new DatagramSocket(); + dgSocket.setSoTimeout(timeout); + } + + @Override + public void send(byte[] data) throws IOException { + DatagramPacket dgPacketOut = new DatagramPacket(data, data.length, + iaddr, iport); + dgSocket.send(dgPacketOut); + } + + @Override + public byte[] receive() throws IOException { + byte ibuf[] = new byte[bufSize]; + dgPacketIn = new DatagramPacket(ibuf, ibuf.length); + try { + dgSocket.receive(dgPacketIn); + } + catch (SocketException e) { + dgSocket.receive(dgPacketIn); + } + byte[] data = new byte[dgPacketIn.getLength()]; + System.arraycopy(dgPacketIn.getData(), 0, data, 0, + dgPacketIn.getLength()); + return data; + } + + @Override + public void close() { + dgSocket.close(); + } +} diff -r 5a56e43d896c -r 46ec4c2dbc16 jdk/src/share/classes/sun/security/krb5/internal/TCPClient.java --- a/jdk/src/share/classes/sun/security/krb5/internal/TCPClient.java Mon Nov 08 09:29:18 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * - * (C) Copyright IBM Corp. 1999 All Rights Reserved. - * Copyright 1997 The Open Group Research Institute. All rights reserved. - */ - -package sun.security.krb5.internal; - -import java.io.*; -import java.net.*; - -public class TCPClient { - - private Socket tcpSocket; - private BufferedOutputStream out; - private BufferedInputStream in; - - public TCPClient(String hostname, int port) throws IOException { - tcpSocket = new Socket(hostname, port); - out = new BufferedOutputStream(tcpSocket.getOutputStream()); - in = new BufferedInputStream(tcpSocket.getInputStream()); - } - - public void send(byte[] data) throws IOException { - byte[] lenField = new byte[4]; - intToNetworkByteOrder(data.length, lenField, 0, 4); - out.write(lenField); - - out.write(data); - out.flush(); - } - - public byte[] receive() throws IOException { - byte[] lenField = new byte[4]; - int count = readFully(lenField, 4); - - if (count != 4) { - if (Krb5.DEBUG) { - System.out.println( - ">>>DEBUG: TCPClient could not read length field"); - } - return null; - } - - int len = networkByteOrderToInt(lenField, 0, 4); - if (Krb5.DEBUG) { - System.out.println( - ">>>DEBUG: TCPClient reading " + len + " bytes"); - } - if (len <= 0) { - if (Krb5.DEBUG) { - System.out.println( - ">>>DEBUG: TCPClient zero or negative length field: "+len); - } - return null; - } - - byte data[] = new byte[len]; - count = readFully(data, len); - if (count != len) { - if (Krb5.DEBUG) { - System.out.println( - ">>>DEBUG: TCPClient could not read complete packet (" + - len + "/" + count + ")"); - } - return null; - } else { - return data; - } - } - - public void close() throws IOException { - tcpSocket.close(); - } - - /** - * Read requested number of bytes before returning. - * @return The number of bytes actually read; -1 if none read - */ - private int readFully(byte[] inBuf, int total) throws IOException { - int count, pos = 0; - - while (total > 0) { - count = in.read(inBuf, pos, total); - - if (count == -1) { - return (pos == 0? -1 : pos); - } - pos += count; - total -= count; - } - return pos; - } - - /** - * Returns the integer represented by 4 bytes in network byte order. - */ - private static final int networkByteOrderToInt(byte[] buf, int start, - int count) { - if (count > 4) { - throw new IllegalArgumentException( - "Cannot handle more than 4 bytes"); - } - - int answer = 0; - - for (int i = 0; i < count; i++) { - answer <<= 8; - answer |= ((int)buf[start+i] & 0xff); - } - return answer; - } - - /** - * Encodes an integer into 4 bytes in network byte order in the buffer - * supplied. - */ - private static final void intToNetworkByteOrder(int num, byte[] buf, - int start, int count) { - if (count > 4) { - throw new IllegalArgumentException( - "Cannot handle more than 4 bytes"); - } - - for (int i = count-1; i >= 0; i--) { - buf[start+i] = (byte)(num & 0xff); - num >>>= 8; - } - } -} diff -r 5a56e43d896c -r 46ec4c2dbc16 jdk/src/share/classes/sun/security/krb5/internal/UDPClient.java --- a/jdk/src/share/classes/sun/security/krb5/internal/UDPClient.java Mon Nov 08 09:29:18 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * - * (C) Copyright IBM Corp. 1999 All Rights Reserved. - * Copyright 1997 The Open Group Research Institute. All rights reserved. - */ - -package sun.security.krb5.internal; - -import java.io.*; -import java.net.*; - -public class UDPClient { - InetAddress iaddr; - int iport; - int bufSize = 65507; - DatagramSocket dgSocket; - DatagramPacket dgPacketIn; - - public UDPClient(InetAddress newIAddr, int port) - throws SocketException { - iaddr = newIAddr; - iport = port; - dgSocket = new DatagramSocket(); - } - - public UDPClient(String hostname, int port) - throws UnknownHostException, SocketException { - iaddr = InetAddress.getByName(hostname); - iport = port; - dgSocket = new DatagramSocket(); - } - - public UDPClient(String hostname, int port, int timeout) - throws UnknownHostException, SocketException { - iaddr = InetAddress.getByName(hostname); - iport = port; - dgSocket = new DatagramSocket(); - dgSocket.setSoTimeout(timeout); - } - - public void setBufSize(int newBufSize) { - bufSize = newBufSize; - } - - public InetAddress getInetAddress() { - if (dgPacketIn != null) - return dgPacketIn.getAddress(); - return null; - } - - public void send(byte[] data) throws IOException { - DatagramPacket dgPacketOut = new DatagramPacket(data, data.length, - iaddr, iport); - dgSocket.send(dgPacketOut); - } - - public byte[] receive() throws IOException { - byte ibuf[] = new byte[bufSize]; - dgPacketIn = new DatagramPacket(ibuf, ibuf.length); - try { - dgSocket.receive(dgPacketIn); - } - catch (SocketException e) { - dgSocket.receive(dgPacketIn); - } - byte[] data = new byte[dgPacketIn.getLength()]; - System.arraycopy(dgPacketIn.getData(), 0, data, 0, - dgPacketIn.getLength()); - return data; - } - - public void close() { - dgSocket.close(); - } -} diff -r 5a56e43d896c -r 46ec4c2dbc16 jdk/test/sun/security/krb5/auto/TcpTimeout.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/krb5/auto/TcpTimeout.java Tue Nov 09 08:34:11 2010 +0800 @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6952519 + * @run main/timeout=40/othervm TcpTimeout + * @summary kdc_timeout is not being honoured when using TCP + */ + +import java.io.*; +import java.net.ServerSocket; +import sun.security.krb5.Config; + +public class TcpTimeout { + public static void main(String[] args) + throws Exception { + + System.setProperty("sun.security.krb5.debug", "true"); + final int p1 = 10000 + new java.util.Random().nextInt(10000); + final int p2 = 20000 + new java.util.Random().nextInt(10000); + final int p3 = 30000 + new java.util.Random().nextInt(10000); + + KDC k = new KDC(OneKDC.REALM, OneKDC.KDCHOST, p3, true); + k.addPrincipal(OneKDC.USER, OneKDC.PASS); + k.addPrincipalRandKey("krbtgt/" + OneKDC.REALM); + + // Start two listener that does not communicate, simulate timeout + new Thread() { + public void run() { + try { + new ServerSocket(p1).accept(); + } catch (Exception e) { + }} + }.start(); + new Thread() { + public void run() { + try { + new ServerSocket(p2).accept(); + } catch (Exception e) { + }} + }.start(); + + FileWriter fw = new FileWriter("alternative-krb5.conf"); + + fw.write("[libdefaults]\n" + + "udp_preference_limit = 1\n" + + "max_retries = 2\n" + + "default_realm = " + OneKDC.REALM + "\n" + + "kdc_timeout = 5000\n"); + fw.write("[realms]\n" + OneKDC.REALM + " = {\n" + + "kdc = " + OneKDC.KDCHOST + ":" + p1 + "\n" + + "kdc = " + OneKDC.KDCHOST + ":" + p2 + "\n" + + "kdc = " + OneKDC.KDCHOST + ":" + p3 + "\n" + + "}\n"); + + fw.close(); + System.setProperty("java.security.krb5.conf", "alternative-krb5.conf"); + Config.refresh(); + + // The correct behavior should be: + // 5 sec on p1, 5 sec on p1, fail + // 5 sec on p2, 5 sec on p2, fail + // p3 ok, p3 ok again for preauth. + // The total time should be 20sec + 2x. x is processing time for AS-REQ. + int count = 6; + long start = System.nanoTime(); + + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + PrintStream oldout = System.out; + System.setOut(new PrintStream(bo)); + Context c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); + System.setOut(oldout); + + String[] lines = new String(bo.toByteArray()).split("\n"); + for (String line: lines) { + if (line.startsWith(">>> KDCCommunication")) { + System.out.println(line); + count--; + } + } + if (count != 0) { + throw new Exception("Retry count is " + count + " less"); + } + + long end = System.nanoTime(); + if ((end - start)/1000000000L < 20) { + throw new Exception("Too fast? " + (end - start)/1000000000L); + } + } + + private static KDC on(int p) throws Exception { + KDC k = new KDC(OneKDC.REALM, OneKDC.KDCHOST, p, true); + k.addPrincipal(OneKDC.USER, OneKDC.PASS); + k.addPrincipalRandKey("krbtgt/" + OneKDC.REALM); + return k; + } + + private static void addFakeKDCs() + throws Exception { + BufferedReader fr = new BufferedReader(new FileReader(OneKDC.KRB5_CONF)); + FileWriter fw = new FileWriter("alternative-krb5.conf"); + while (true) { + String s = fr.readLine(); + if (s == null) { + break; + } + if (s.trim().startsWith("kdc = ")) { + fw.write(" kdc = localhost:33333\n"); + fw.write(" kdc = localhost:22222\n"); + } + fw.write(s + "\n"); + } + fr.close(); + fw.close(); + sun.security.krb5.Config.refresh(); + } +}