1/* 2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26/* 27 * 28 * (C) Copyright IBM Corp. 1999 All Rights Reserved. 29 * Copyright 1997 The Open Group Research Institute. All rights reserved. 30 */ 31 32package sun.security.krb5.internal; 33 34import java.io.*; 35import java.net.*; 36import sun.security.util.IOUtils; 37 38public abstract class NetClient implements AutoCloseable { 39 public static NetClient getInstance(String protocol, String hostname, int port, 40 int timeout) throws IOException { 41 if (protocol.equals("TCP")) { 42 return new TCPClient(hostname, port, timeout); 43 } else { 44 return new UDPClient(hostname, port, timeout); 45 } 46 } 47 48 abstract public void send(byte[] data) throws IOException; 49 abstract public byte[] receive() throws IOException; 50 abstract public void close() throws IOException; 51} 52 53class TCPClient extends NetClient { 54 55 private Socket tcpSocket; 56 private BufferedOutputStream out; 57 private BufferedInputStream in; 58 59 TCPClient(String hostname, int port, int timeout) 60 throws IOException { 61 tcpSocket = new Socket(); 62 tcpSocket.connect(new InetSocketAddress(hostname, port), timeout); 63 out = new BufferedOutputStream(tcpSocket.getOutputStream()); 64 in = new BufferedInputStream(tcpSocket.getInputStream()); 65 tcpSocket.setSoTimeout(timeout); 66 } 67 68 @Override 69 public void send(byte[] data) throws IOException { 70 byte[] lenField = new byte[4]; 71 intToNetworkByteOrder(data.length, lenField, 0, 4); 72 out.write(lenField); 73 74 out.write(data); 75 out.flush(); 76 } 77 78 @Override 79 public byte[] receive() throws IOException { 80 byte[] lenField = new byte[4]; 81 int count = readFully(lenField, 4); 82 83 if (count != 4) { 84 if (Krb5.DEBUG) { 85 System.out.println( 86 ">>>DEBUG: TCPClient could not read length field"); 87 } 88 return null; 89 } 90 91 int len = networkByteOrderToInt(lenField, 0, 4); 92 if (Krb5.DEBUG) { 93 System.out.println( 94 ">>>DEBUG: TCPClient reading " + len + " bytes"); 95 } 96 if (len <= 0) { 97 if (Krb5.DEBUG) { 98 System.out.println( 99 ">>>DEBUG: TCPClient zero or negative length field: "+len); 100 } 101 return null; 102 } 103 104 try { 105 return IOUtils.readFully(in, len, true); 106 } catch (IOException ioe) { 107 if (Krb5.DEBUG) { 108 System.out.println( 109 ">>>DEBUG: TCPClient could not read complete packet (" + 110 len + "/" + count + ")"); 111 } 112 return null; 113 } 114 } 115 116 @Override 117 public void close() throws IOException { 118 tcpSocket.close(); 119 } 120 121 /** 122 * Read requested number of bytes before returning. 123 * @return The number of bytes actually read; -1 if none read 124 */ 125 private int readFully(byte[] inBuf, int total) throws IOException { 126 int count, pos = 0; 127 128 while (total > 0) { 129 count = in.read(inBuf, pos, total); 130 131 if (count == -1) { 132 return (pos == 0? -1 : pos); 133 } 134 pos += count; 135 total -= count; 136 } 137 return pos; 138 } 139 140 /** 141 * Returns the integer represented by 4 bytes in network byte order. 142 */ 143 private static int networkByteOrderToInt(byte[] buf, int start, 144 int count) { 145 if (count > 4) { 146 throw new IllegalArgumentException( 147 "Cannot handle more than 4 bytes"); 148 } 149 150 int answer = 0; 151 152 for (int i = 0; i < count; i++) { 153 answer <<= 8; 154 answer |= ((int)buf[start+i] & 0xff); 155 } 156 return answer; 157 } 158 159 /** 160 * Encodes an integer into 4 bytes in network byte order in the buffer 161 * supplied. 162 */ 163 private static void intToNetworkByteOrder(int num, byte[] buf, 164 int start, int count) { 165 if (count > 4) { 166 throw new IllegalArgumentException( 167 "Cannot handle more than 4 bytes"); 168 } 169 170 for (int i = count-1; i >= 0; i--) { 171 buf[start+i] = (byte)(num & 0xff); 172 num >>>= 8; 173 } 174 } 175} 176 177class UDPClient extends NetClient { 178 InetAddress iaddr; 179 int iport; 180 int bufSize = 65507; 181 DatagramSocket dgSocket; 182 DatagramPacket dgPacketIn; 183 184 UDPClient(String hostname, int port, int timeout) 185 throws UnknownHostException, SocketException { 186 iaddr = InetAddress.getByName(hostname); 187 iport = port; 188 dgSocket = new DatagramSocket(); 189 dgSocket.setSoTimeout(timeout); 190 dgSocket.connect(iaddr, iport); 191 } 192 193 @Override 194 public void send(byte[] data) throws IOException { 195 DatagramPacket dgPacketOut = new DatagramPacket(data, data.length, 196 iaddr, iport); 197 dgSocket.send(dgPacketOut); 198 } 199 200 @Override 201 public byte[] receive() throws IOException { 202 byte[] ibuf = new byte[bufSize]; 203 dgPacketIn = new DatagramPacket(ibuf, ibuf.length); 204 try { 205 dgSocket.receive(dgPacketIn); 206 } 207 catch (SocketException e) { 208 if (e instanceof PortUnreachableException) { 209 throw e; 210 } 211 dgSocket.receive(dgPacketIn); 212 } 213 byte[] data = new byte[dgPacketIn.getLength()]; 214 System.arraycopy(dgPacketIn.getData(), 0, data, 0, 215 dgPacketIn.getLength()); 216 return data; 217 } 218 219 @Override 220 public void close() { 221 dgSocket.close(); 222 } 223} 224