SimpleNTPServer.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.net.examples.ntp;

  18. import java.io.IOException;
  19. import java.net.DatagramPacket;
  20. import java.net.DatagramSocket;

  21. import org.apache.commons.net.ntp.NtpUtils;
  22. import org.apache.commons.net.ntp.NtpV3Impl;
  23. import org.apache.commons.net.ntp.NtpV3Packet;
  24. import org.apache.commons.net.ntp.TimeStamp;

  25. /**
  26.  * The SimpleNTPServer class is a UDP implementation of a server for the Network Time Protocol (NTP) version 3 as described in RFC 1305. It is a minimal NTP
  27.  * server that doesn't actually adjust the time but only responds to NTP datagram requests with response sent back to originating host with info filled out
  28.  * using the current clock time. To be used for debugging or testing.
  29.  *
  30.  * To prevent this from interfering with the actual NTP service it can be run from any local port.
  31.  */
  32. public class SimpleNTPServer implements Runnable {

  33.     public static void main(final String[] args) {
  34.         int port = NtpV3Packet.NTP_PORT;
  35.         if (args.length != 0) {
  36.             try {
  37.                 port = Integer.parseInt(args[0]);
  38.             } catch (final NumberFormatException nfe) {
  39.                 nfe.printStackTrace();
  40.             }
  41.         }
  42.         final SimpleNTPServer timeServer = new SimpleNTPServer(port);
  43.         try {
  44.             timeServer.start();
  45.         } catch (final IOException e) {
  46.             e.printStackTrace();
  47.         }
  48.     }

  49.     private int port;
  50.     private volatile boolean running;
  51.     private boolean started;

  52.     private DatagramSocket socket;

  53.     /**
  54.      * Creates SimpleNTPServer listening on default NTP port.
  55.      */
  56.     public SimpleNTPServer() {
  57.         this(NtpV3Packet.NTP_PORT);
  58.     }

  59.     /**
  60.      * Creates SimpleNTPServer.
  61.      *
  62.      * @param port the local port the server socket is bound to, or <code>zero</code> for a system selected free port.
  63.      * @throws IllegalArgumentException if port number less than 0
  64.      */
  65.     public SimpleNTPServer(final int port) {
  66.         if (port < 0) {
  67.             throw new IllegalArgumentException();
  68.         }
  69.         this.port = port;
  70.     }

  71.     /**
  72.      * Connects to server socket and listen for client connections.
  73.      *
  74.      * @throws IOException if an I/O error occurs when creating the socket.
  75.      */
  76.     public void connect() throws IOException {
  77.         if (socket == null) {
  78.             socket = new DatagramSocket(port);
  79.             // port = 0 is bound to available free port
  80.             if (port == 0) {
  81.                 port = socket.getLocalPort();
  82.             }
  83.             System.out.println("Running NTP service on port " + port + "/UDP");
  84.         }
  85.     }

  86.     public int getPort() {
  87.         return port;
  88.     }

  89.     /**
  90.      * Handles incoming packet. If NTP packet is client-mode then respond to that host with a NTP response packet otherwise ignore.
  91.      *
  92.      * @param request incoming DatagramPacket
  93.      * @param rcvTime time packet received
  94.      *
  95.      * @throws IOException if an I/O error occurs.
  96.      */
  97.     protected void handlePacket(final DatagramPacket request, final long rcvTime) throws IOException {
  98.         final NtpV3Packet message = new NtpV3Impl();
  99.         message.setDatagramPacket(request);
  100.         System.out.printf("NTP packet from %s mode=%s%n", request.getAddress().getHostAddress(), NtpUtils.getModeName(message.getMode()));
  101.         if (message.getMode() == NtpV3Packet.MODE_CLIENT) {
  102.             final NtpV3Packet response = new NtpV3Impl();

  103.             response.setStratum(1);
  104.             response.setMode(NtpV3Packet.MODE_SERVER);
  105.             response.setVersion(NtpV3Packet.VERSION_3);
  106.             response.setPrecision(-20);
  107.             response.setPoll(0);
  108.             response.setRootDelay(62);
  109.             response.setRootDispersion((int) (16.51 * 65.536));

  110.             // originate time as defined in RFC-1305 (t1)
  111.             response.setOriginateTimeStamp(message.getTransmitTimeStamp());
  112.             // Receive Time is time request received by server (t2)
  113.             response.setReceiveTimeStamp(TimeStamp.getNtpTime(rcvTime));
  114.             response.setReferenceTime(response.getReceiveTimeStamp());
  115.             response.setReferenceId(0x4C434C00); // LCL (Undisciplined Local Clock)

  116.             // Transmit time is time reply sent by server (t3)
  117.             response.setTransmitTime(TimeStamp.getNtpTime(System.currentTimeMillis()));

  118.             final DatagramPacket dp = response.getDatagramPacket();
  119.             dp.setPort(request.getPort());
  120.             dp.setAddress(request.getAddress());
  121.             socket.send(dp);
  122.         }
  123.         // otherwise if received packet is other than CLIENT mode then ignore it
  124.     }

  125.     /**
  126.      * Returns state of whether time service is running.
  127.      *
  128.      * @return true if time service is running
  129.      */
  130.     public boolean isRunning() {
  131.         return running;
  132.     }

  133.     /**
  134.      * Returns state of whether time service is running.
  135.      *
  136.      * @return true if time service is running
  137.      */
  138.     public boolean isStarted() {
  139.         return started;
  140.     }

  141.     /**
  142.      * Main method to service client connections.
  143.      */
  144.     @Override
  145.     public void run() {
  146.         running = true;
  147.         final byte[] buffer = new byte[48];
  148.         final DatagramPacket request = new DatagramPacket(buffer, buffer.length);
  149.         do {
  150.             try {
  151.                 socket.receive(request);
  152.                 final long rcvTime = System.currentTimeMillis();
  153.                 handlePacket(request, rcvTime);
  154.             } catch (final IOException e) {
  155.                 if (running) {
  156.                     e.printStackTrace();
  157.                 }
  158.                 // otherwise socket thrown exception during shutdown
  159.             }
  160.         } while (running);
  161.     }

  162.     /**
  163.      * Starts time service and provide time to client connections.
  164.      *
  165.      * @throws IOException if an I/O error occurs when creating the socket.
  166.      */
  167.     public void start() throws IOException {
  168.         if (socket == null) {
  169.             connect();
  170.         }
  171.         if (!started) {
  172.             started = true;
  173.             new Thread(this).start();
  174.         }
  175.     }

  176.     /**
  177.      * Closes server socket and stop listening.
  178.      */
  179.     public void stop() {
  180.         running = false;
  181.         if (socket != null) {
  182.             socket.close(); // force closing of the socket
  183.             socket = null;
  184.         }
  185.         started = false;
  186.     }

  187. }