/* * Copyrigth (C) 2010 Henrik Baastrup. * * Licensed under the GNU Lesser General Public License version 3; * you may not use this file except in compliance with the License. * You should have received a copy of the license together with this * file but can obtain a copy of the License at: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package javax.net.stun; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.Socket; import java.net.SocketException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Enumeration; /** * * @author Henrik Baastrup */ public class Utils { /** * Internal used. * @param key * @param text * @return */ public static byte[] hmac(byte key[], byte text[]) { return hmac(key, text, 64); } /** * Internal used. * @param key * @param text * @param blockSize * @return */ public static byte[] hmac(byte key[], byte text[], int blockSize) { byte key0[]; if (key.length==blockSize) key0 = key; else if (key.length < blockSize) { key0 = new byte[blockSize]; for (int i=0; i<key.length; i++) key0[i] = key[i]; for (int i=key.length; i<blockSize; i++) key0[i] = 0; } else { byte h[] = hash(key); key0 = new byte[blockSize]; int len = blockSize; if (h.length<blockSize) len = h.length; for (int i=0; i< len; i++) key0[i] = h[i]; for (int i=len; i<blockSize; i++) key0[i] = 0; } byte ipad[] = new byte[key0.length]; for (int i=0; i<key0.length; i++) ipad[i] = (byte) (key0[i] ^ 0x36); byte res[] = new byte[ipad.length + text.length]; for (int i=0; i<ipad.length; i++) res[i] = ipad[i]; for (int i=0; i<text.length; i++) res[i+ipad.length] = text[i]; byte h[] = hash(res); byte opad[] = new byte[key0.length]; for (int i=0; i<key0.length; i++) opad[i] = (byte) (key0[i] ^ 0x5c); res = new byte[opad.length+h.length]; for (int i=0; i<opad.length; i++) res[i] = opad[i]; for (int i=0; i<h.length; i++) res[i+opad.length] = h[i]; h = hash(res); // System.out.print("hmac: "); // for (int i=0; i<h.length; i++) System.out.print(Integer.toString( ( h[i] & 0xff ) + 0x100, 16).substring( 1 )); // System.out.println(""); // System.out.print("key: "); // for (int i=0; i<key.length; i++) System.out.print(Integer.toString( ( key[i] & 0xff ) + 0x100, 16).substring( 1 )); // System.out.println(""); // System.out.print("text: "); // for (int i=0; i<text.length; i++) System.out.print(Integer.toString( ( text[i] & 0xff ) + 0x100, 16).substring( 1 )); // System.out.println(""); return h; } private static byte[] hash(byte input[]) { MessageDigest md; try { md = MessageDigest.getInstance("SHA1"); } catch (NoSuchAlgorithmException ex) { ex.printStackTrace(); return null; } md.update(input); byte digest[] = md.digest(); return digest; } /** * Internal used. * @param errorCode * @return */ public static String createErrorString(int errorCode) { switch (errorCode) { case 0: return ""; case 401: return "(Unauthorized): The Binding Request did not contain a MESSAGEINTEGRITY attribute."; case 420: return "(Unknown Attribute): The server did not understand a mandatory attribute in the request."; case 430: return "(Stale Credentials): The Binding Request did contain a MESSAGEINTEGRITY attribute, but it used a shared secret that has expired. The client should obtain a new shared secret and try again."; case 431: return "(Integrity Check Failure): The Binding Request contained a MESSAGE-INTEGRITY attribute, but the HMAC failed verification. This could be a sign of a potential attack, or client implementation error."; case 432: return "(Missing Username): The Binding Request contained a MESSAGEINTEGRITY attribute, but not a USERNAME attribute. Both must be present for integrity checks."; case 433: return "(Use TLS): The Shared Secret request has to be sent over TLS, but was not received over TLS."; case 500: return "(Server Error): The server has suffered a temporary error. The client should try again."; case 600: return "(Global Failure:) The server is refusing to fulfill the request. The client should not retry."; case 400: default: return "(Bad Request): The request was malformed. The client should not retry the request without modification from the previous attempt."; } } /** * Will try to find the local IP V4 address there is not the loopback address * @return * @throws SocketException local IPV4 address. */ public static InetAddress getLocalAddr() throws SocketException { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface dev=interfaces.nextElement(); String devName = dev.getDisplayName(); Enumeration<InetAddress> addresses = dev.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress addr=addresses.nextElement(); if (addr instanceof Inet6Address) continue; //STUN might not be intresting with an IP V6 address String addrStr = addr.getHostAddress(); if (addrStr.startsWith("127."))continue; return addr; } } return null; } public static MessageHeader socketSendRecive(Socket sock, MessageHeader header) throws IOException { OutputStream outStream = null; InputStream inStream = null; byte message[] = null; try { inStream = sock.getInputStream(); outStream = sock.getOutputStream(); outStream.write(header.toBytes()); //get the message headder byte headerBuf[] = new byte[20]; int bufLen = 20; int read = 0; while (read<20) { int r = inStream.read(headerBuf, read, bufLen); if (r < 0) break; bufLen -= r; read += r; } //find message length if (headerBuf.length<4) throw new IOException("The header is not long enof to extract the Message Length"); int mesgLength = (0x000000FF & ((int)headerBuf[2])) << 8; mesgLength +=(0x000000FF & ((int) headerBuf[3])); //get the message body message = new byte[mesgLength+20]; bufLen = mesgLength; read = 20; while (read<mesgLength) { int r = inStream.read(message, read, bufLen); if (r < 0) break; bufLen -= r; read += r; } for (int i=0; i<20; i++) message[i] = headerBuf[i]; } finally { if (inStream!=null) inStream.close(); if (outStream!=null) outStream.close(); } return MessageHeader.create(message); } }