/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2008-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * OpenNMS(R) 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 for more details. * * You should have received a copy of the GNU General Public License * along with OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.netmgt.provision.detector.simple.client; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.provision.detector.simple.request.TrivialTimeRequest; import org.opennms.netmgt.provision.detector.simple.response.TrivialTimeResponse; import org.opennms.netmgt.provision.support.Client; public class TrivialTimeClient implements Client<TrivialTimeRequest, TrivialTimeResponse> { /** * Seconds to subtract from a 1970-01-01 00:00:00-based UNIX timestamp * to make it comparable to a 1900-01-01 00:00:00-based timestamp from * the trivial time service (actually adding a negative value) */ private static final int EPOCH_ADJ_FACTOR = 2085978496; private String protocol; private int allowedSkew; private int retries; private Socket tcpSocket; private DatagramSocket udpSocket; private DatagramPacket udpPacket; public TrivialTimeClient(String protocol, int allowedSkew) { super(); this.protocol = protocol; this.allowedSkew = allowedSkew; } @Override public void connect(InetAddress address, int port, int timeout) throws IOException, Exception { // Validate Protocol if (!isTcp() && !isUdp()) { throw new IllegalArgumentException("Unsupported protocol, only TCP and UDP currently supported"); } else if (isUdp()) { log().warn("UDP support is largely untested"); } // Initialize Socket if (isTcp()) { tcpSocket = new Socket(); tcpSocket.connect(new InetSocketAddress(address, port), timeout); tcpSocket.setSoTimeout(timeout); } else { udpSocket = new DatagramSocket(); udpSocket.setSoTimeout(timeout); udpPacket = new DatagramPacket(new byte[]{}, 0, address, port); // Empty datagram per RFC868 } log().debug("Connected to host: " + address + " on " + protocol.toUpperCase() + " port: " + port); } @Override public void close() { try { if (isTcp() && tcpSocket != null) { tcpSocket.close(); } if (isUdp() && udpSocket != null) { udpSocket.close(); } } catch (Exception e) { log().error("Can't close detector sockets.", e); } } @Override public TrivialTimeResponse receiveBanner() throws IOException, Exception { return null; } @Override public TrivialTimeResponse sendRequest(TrivialTimeRequest request) throws IOException, Exception { boolean gotTime = false; int remoteTime = 0; int localTime = 0; for (int i = 0; i < retries && !gotTime; i++) { // Try to read from the socket byte[] timeBytes = new byte[4]; ByteBuffer timeByteBuffer = ByteBuffer.wrap(timeBytes); int bytesRead = 0; if (isTcp()) { bytesRead = tcpSocket.getInputStream().read(timeBytes); } if (isUdp()) { // Send an empty datagram per RFC868 udpSocket.send(udpPacket); DatagramPacket timePacket = new DatagramPacket(timeBytes, timeBytes.length); // Try to receive a response from the remote socket udpSocket.receive(timePacket); bytesRead = timePacket.getLength(); } if (bytesRead != 4) continue; if (log().isDebugEnabled()) { log().debug("sendRequest: " + protocol + " bytes read = " + bytesRead); } try { remoteTime = timeByteBuffer.getInt(); } catch (BufferUnderflowException bue) { log().error("Encountered buffer underflow while reading time from remote socket."); remoteTime = 0; continue; // to next iteration of for() loop } localTime = (int)(System.currentTimeMillis() / 1000) - EPOCH_ADJ_FACTOR; gotTime = true; } return gotTime ? new TrivialTimeResponse(remoteTime, localTime, allowedSkew) : new TrivialTimeResponse(); } public void setRetries(int retries) { this.retries = retries; } private boolean isTcp() { return protocol.equalsIgnoreCase("tcp"); } private boolean isUdp() { return protocol.equalsIgnoreCase("udp"); } protected ThreadCategory log() { return ThreadCategory.getInstance(getClass()); } }