View Javadoc

1   /*******************************************************************************
2    * Copyright (c) 2007, 2014 Massimiliano Ziccardi
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *******************************************************************************/
16  package it.jnrpe.client;
17  
18  import it.jnrpe.ReturnValue;
19  import it.jnrpe.Status;
20  import it.jnrpe.net.JNRPERequest;
21  import it.jnrpe.net.JNRPEResponse;
22  
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.net.InetSocketAddress;
26  import java.net.Socket;
27  import java.net.SocketTimeoutException;
28  import java.security.SecureRandom;
29  import java.security.cert.CertificateException;
30  import java.security.cert.X509Certificate;
31  
32  import javax.net.SocketFactory;
33  import javax.net.ssl.SSLContext;
34  import javax.net.ssl.TrustManager;
35  import javax.net.ssl.X509TrustManager;
36  
37  import org.apache.commons.cli2.CommandLine;
38  import org.apache.commons.cli2.DisplaySetting;
39  import org.apache.commons.cli2.Group;
40  import org.apache.commons.cli2.OptionException;
41  import org.apache.commons.cli2.builder.ArgumentBuilder;
42  import org.apache.commons.cli2.builder.DefaultOptionBuilder;
43  import org.apache.commons.cli2.builder.GroupBuilder;
44  import org.apache.commons.cli2.commandline.Parser;
45  import org.apache.commons.cli2.option.DefaultOption;
46  import org.apache.commons.cli2.util.HelpFormatter;
47  import org.apache.commons.cli2.validation.NumberValidator;
48  
49  /**
50   * This class represent a simple JNRPE client that can be used to invoke
51   * commands installed inside JNRPE by code. It is the JAVA equivalent of
52   * check_nrpe.
53   * 
54   * @author Massimiliano Ziccardi
55   */
56  public class JNRPEClient {
57  
58  	/**
59  	 * Default timeout in seconds.
60  	 */
61  	private static final int DEFAULT_TIMEOUT = 10;
62  
63  	/**
64  	 * Default server port.
65  	 */
66  	private static final int DEFAULT_PORT = 5666;
67  
68  	/**
69  	 * The IP address (or URL) of the JNRPE Server.
70  	 */
71  	private final String serverIPorURL;
72  
73  	/**
74  	 * The port where the JNRPE server listens to.
75  	 */
76  	private final int serverPort;
77  
78  	/**
79  	 * <code>true</code> if the communication must happens through SSL.
80  	 */
81  	private final boolean useSSL;
82  
83  	/**
84  	 * The communication timeout (in seconds).
85  	 */
86  	private int communicationTimeout = DEFAULT_TIMEOUT;
87  
88  	/**
89  	 * Instantiates a JNRPE client.
90  	 * 
91  	 * @param sJNRPEServerIP
92  	 *            The IP where the JNRPE is installed
93  	 * @param iJNRPEServerPort
94  	 *            The port where the JNRPE server listens
95  	 * @param bSSL
96  	 *            <code>true</code> if SSL must be used
97  	 */
98  	public JNRPEClient(final String sJNRPEServerIP, final int iJNRPEServerPort,
99  			final boolean bSSL) {
100 		serverIPorURL = sJNRPEServerIP;
101 		serverPort = iJNRPEServerPort;
102 		useSSL = bSSL;
103 	}
104 
105 	/**
106 	 * Creates a custom TrustManager that trusts any certificate.
107 	 * 
108 	 * @return The custom trustmanager
109 	 */
110 	private TrustManager getTrustManager() {
111 		// Trust all certificates
112 		return new X509TrustManager() {
113 
114 			public X509Certificate[] getAcceptedIssuers() {
115 				return null;
116 			}
117 
118 			public void checkServerTrusted(final X509Certificate[] chain,
119 					final String authType) throws CertificateException {
120 
121 			}
122 
123 			public void checkClientTrusted(final X509Certificate[] chain,
124 					final String authType) throws CertificateException {
125 			}
126 		};
127 	}
128 
129 	/**
130 	 * Inovoke a command installed in JNRPE.
131 	 * 
132 	 * @param sCommandName
133 	 *            The name of the command to be invoked
134 	 * @param arguments
135 	 *            The arguments to pass to the command (will substitute the
136 	 *            $ARGSx$ parameters)
137 	 * @return The value returned by the server
138 	 * @throws JNRPEClientException
139 	 *             Thrown on any communication error.
140 	 */
141 	public final ReturnValue sendCommand(final String sCommandName,
142 			final String... arguments) throws JNRPEClientException {
143 		SocketFactory socketFactory = null;
144 
145 		Socket s = null;
146 		try {
147 			if (!useSSL) {
148 				socketFactory = SocketFactory.getDefault();
149 			} else {
150 				SSLContext sslContext = SSLContext.getInstance("SSL");
151 				sslContext.init(null, null, new java.security.SecureRandom());
152 
153 				sslContext.init(null, new TrustManager[] { getTrustManager() },
154 						new SecureRandom());
155 
156 				socketFactory = sslContext.getSocketFactory();
157 			}
158 
159 			s = socketFactory.createSocket();
160 			s.setSoTimeout(communicationTimeout * 1000);
161 			s.connect(new InetSocketAddress(serverIPorURL, serverPort));
162 			JNRPERequest req = new JNRPERequest(sCommandName, arguments);
163 
164 			s.getOutputStream().write(req.toByteArray());
165 
166 			InputStream in = s.getInputStream();
167 			JNRPEResponse res = new JNRPEResponse(in);
168 
169 			return new ReturnValue(Status.fromIntValue(res.getResultCode()),
170 					res.getMessage());
171 		} catch (Exception e) {
172 			// e.printStackTrace();
173 			throw new JNRPEClientException(e);
174 		} finally {
175 			if (s != null) {
176 				try {
177 					s.close();
178 				} catch (IOException e) {
179 					// Ignore
180 				}
181 			}
182 		}
183 	}
184 
185 	/**
186 	 * Sets the connection timeout in seconds.
187 	 * 
188 	 * @param iTimeout
189 	 *            The new connection timeout. Default : 10
190 	 */
191 	public final void setTimeout(final int iTimeout) {
192 		communicationTimeout = iTimeout;
193 	}
194 
195 	/**
196 	 * Returns the currently configured connection timeout in seconds.
197 	 * 
198 	 * @return The connection timeout
199 	 */
200 	public final int getTimeout() {
201 		return communicationTimeout;
202 	}
203 
204 	/**
205 	 * Configures the command line parser.
206 	 * 
207 	 * @return The command line parser configuration
208 	 */
209 	private static Group configureCommandLine() {
210 		DefaultOptionBuilder oBuilder = new DefaultOptionBuilder();
211 		ArgumentBuilder aBuilder = new ArgumentBuilder();
212 		GroupBuilder gBuilder = new GroupBuilder();
213 
214 		DefaultOption nosslOption = oBuilder.withLongName("nossl")
215 				.withShortName("n").withDescription("Do no use SSL").create();
216 
217 		DefaultOption unknownOption = oBuilder
218 				.withLongName("unknown")
219 				.withShortName("u")
220 				.withDescription(
221 						"Make socket timeouts return an UNKNOWN "
222 								+ "state instead of CRITICAL").create();
223 
224 		DefaultOption hostOption = oBuilder
225 				.withLongName("host")
226 				.withShortName("H")
227 				.withDescription(
228 						"The address of the host running "
229 								+ "the JNRPE/NRPE daemon")
230 				.withArgument(
231 						aBuilder.withName("host").withMinimum(1).withMaximum(1)
232 								.create()).create();
233 
234 		NumberValidator positiveInt = NumberValidator.getIntegerInstance();
235 		positiveInt.setMinimum(0);
236 		DefaultOption portOption = oBuilder
237 				.withLongName("port")
238 				.withShortName("p")
239 				.withDescription(
240 						"The port on which the daemon "
241 								+ "is running (default=5666)")
242 				.withArgument(
243 						aBuilder.withName("port").withMinimum(1).withMaximum(1)
244 								.withDefault(new Long(DEFAULT_PORT))
245 								.withValidator(positiveInt).create()).create();
246 
247 		DefaultOption timeoutOption = oBuilder
248 				.withLongName("timeout")
249 				.withShortName("t")
250 				.withDescription(
251 						"Number of seconds before connection "
252 								+ "times out (default=10)")
253 				.withArgument(
254 						aBuilder.withName("timeout").withMinimum(1)
255 								.withMaximum(1)
256 								.withDefault(new Long(DEFAULT_TIMEOUT))
257 								.withValidator(positiveInt).create()).create();
258 
259 		DefaultOption commandOption = oBuilder
260 				.withLongName("command")
261 				.withShortName("c")
262 				.withDescription(
263 						"The name of the command that "
264 								+ "the remote daemon should run")
265 				.withArgument(
266 						aBuilder.withName("command").withMinimum(1)
267 								.withMaximum(1).create()).create();
268 
269 		DefaultOption argsOption = oBuilder
270 				.withLongName("arglist")
271 				.withShortName("a")
272 				.withDescription(
273 						"Optional arguments that should be "
274 								+ "passed to the command.  Multiple "
275 								+ "arguments should be separated by "
276 								+ "a space (' '). If provided, "
277 								+ "this must be the last option "
278 								+ "supplied on the command line.")
279 				.withArgument(
280 						aBuilder.withName("arglist").withMinimum(1).create())
281 				.create();
282 
283 		DefaultOption helpOption = oBuilder.withLongName("help")
284 				.withShortName("h").withDescription("Shows this help").create();
285 
286 		Group executionOption = gBuilder.withOption(nosslOption)
287 				.withOption(unknownOption).withOption(hostOption)
288 				.withOption(portOption).withOption(timeoutOption)
289 				.withOption(commandOption).withOption(argsOption).create();
290 
291 		Group mainGroup = gBuilder.withOption(executionOption)
292 				.withOption(helpOption).withMinimum(1).withMaximum(1).create();
293 
294 		return mainGroup;
295 	}
296 
297 	/**
298 	 * Prints the application version.
299 	 */
300 	private static void printVersion() {
301 
302 		System.out.println("jcheck_nrpe version "
303 				+ JNRPEClient.class.getPackage().getImplementationVersion());
304 		System.out.println("Copyright (c) 2013 Massimiliano Ziccardi");
305 		System.out.println("Licensed under the Apache License, Version 2.0");
306 		System.out.println();
307 	}
308 
309 	/**
310 	 * Prints usage instrunctions and, eventually, an error message about the
311 	 * latest execution.
312 	 * 
313 	 * @param e
314 	 *            The exception error
315 	 */
316 	@SuppressWarnings("unchecked")
317 	private static void printUsage(final Exception e) {
318 		printVersion();
319 
320 		StringBuffer sbDivider = new StringBuffer("=");
321 
322 		if (e != null) {
323 			System.out.println(e.getMessage() + "\n");
324 		}
325 
326 		HelpFormatter hf = new HelpFormatter();
327 		while (sbDivider.length() < hf.getPageWidth()) {
328 			sbDivider.append("=");
329 		}
330 
331 		// DISPLAY SETTING
332 		hf.getDisplaySettings().clear();
333 		hf.getDisplaySettings().add(DisplaySetting.DISPLAY_GROUP_EXPANDED);
334 		hf.getDisplaySettings().add(DisplaySetting.DISPLAY_PARENT_CHILDREN);
335 
336 		// USAGE SETTING
337 
338 		hf.getFullUsageSettings().clear();
339 		hf.getFullUsageSettings().add(DisplaySetting.DISPLAY_PARENT_ARGUMENT);
340 		hf.getFullUsageSettings()
341 				.add(DisplaySetting.DISPLAY_ARGUMENT_BRACKETED);
342 		hf.getFullUsageSettings().add(DisplaySetting.DISPLAY_PARENT_CHILDREN);
343 		hf.getFullUsageSettings().add(DisplaySetting.DISPLAY_GROUP_EXPANDED);
344 
345 		hf.setDivider(sbDivider.toString());
346 
347 		hf.setGroup(configureCommandLine());
348 		hf.print();
349 		System.exit(0);
350 	}
351 
352 	/**
353 	 * 
354 	 * @param args
355 	 *            command line arguments
356 	 * @throws Exception
357 	 *             -
358 	 */
359 	public static void main(final String[] args) throws Exception {
360 
361 		Parser parser = new Parser();
362 		parser.setGroup(configureCommandLine());
363 
364 		boolean timeoutAsUnknown = false;
365 
366 		try {
367 			CommandLine cli = parser.parse(args);
368 
369 			if (cli.hasOption("--help")) {
370 				printUsage(null);
371 			}
372 
373 			timeoutAsUnknown = cli.hasOption("--unknown");
374 
375 			String sHost = (String) cli.getValue("--host");
376 			Long port = (Long) cli.getValue("--port", new Long(DEFAULT_PORT));
377 			String sCommand = (String) cli.getValue("--command");
378 
379 			JNRPEClient client = new JNRPEClient(sHost, port.intValue(),
380 					!cli.hasOption("--nossl"));
381 			client.setTimeout(((Long) cli.getValue("--timeout", new Long(
382 					DEFAULT_TIMEOUT))).intValue());
383 
384 			@SuppressWarnings("unchecked")
385 			ReturnValue ret = client.sendCommand(sCommand, (String[]) cli
386 					.getValues("--arglist").toArray(new String[0]));
387 
388 			System.out.println(ret.getMessage());
389 			System.exit(ret.getStatus().intValue());
390 		} catch (JNRPEClientException exc) {
391 			Status returnStatus = null;
392 
393 			if (timeoutAsUnknown && exc.getCause() != null
394 					&& exc.getCause() instanceof SocketTimeoutException) {
395 				returnStatus = Status.UNKNOWN;
396 			} else {
397 				returnStatus = Status.CRITICAL;
398 			}
399 
400 			System.out.println(exc.getMessage());
401 			System.exit(returnStatus.intValue());
402 		} catch (OptionException oe) {
403 			printUsage(oe);
404 		}
405 	}
406 }