/*******************************************************************************
* Copyright (c) 2007, 2014 Massimiliano Ziccardi
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 it.jnrpe.client;
import it.jnrpe.ReturnValue;
import it.jnrpe.Status;
import it.jnrpe.net.JNRPERequest;
import it.jnrpe.net.JNRPEResponse;
import it.jnrpe.utils.TimeUnit;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Set;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.cli2.CommandLine;
import org.apache.commons.cli2.DisplaySetting;
import org.apache.commons.cli2.Group;
import org.apache.commons.cli2.OptionException;
import org.apache.commons.cli2.builder.ArgumentBuilder;
import org.apache.commons.cli2.builder.DefaultOptionBuilder;
import org.apache.commons.cli2.builder.GroupBuilder;
import org.apache.commons.cli2.commandline.Parser;
import org.apache.commons.cli2.option.DefaultOption;
import org.apache.commons.cli2.util.HelpFormatter;
import org.apache.commons.cli2.validation.NumberValidator;
/**
* This class represent a simple JNRPE client that can be used to invoke
* commands installed inside JNRPE by code. It is the JAVA equivalent of
* check_nrpe.
*
* @author Massimiliano Ziccardi
*/
public class JNRPEClient {
/**
* Default timeout in seconds.
*/
private static final int DEFAULT_TIMEOUT = 10;
/**
* Default server port.
*/
private static final int DEFAULT_PORT = 5666;
/**
* The IP address (or URL) of the JNRPE Server.
*/
private final String serverIPorURL;
/**
* The port where the JNRPE server listens to.
*/
private final int serverPort;
/**
* <code>true</code> if the communication must happens through SSL.
*/
private final boolean useSSL;
/**
* <code>true</code> to enable weak cipher suites. Useful if you need to be
* able to communicate with NSClient++
*/
private boolean weakCipherSuitesEnabled = false;
/**
* The communication timeout (in seconds).
*/
private int communicationTimeout = DEFAULT_TIMEOUT;
/**
* The trust manager.
*/
private static final TrustAllManager TRUSTALL_MGR = new TrustAllManager();
/**
* Trust manager implementation. This trust manager allows connection on any
* host.
*
* @author Massimiliano Ziccardi
*/
private static final class TrustAllManager implements X509TrustManager {
/**
* Empty list of accepted issuers.
*/
private static final X509Certificate[] ISSUERS = new X509Certificate[0];
/**
* Simply returns null.
*
* @return null
*/
public X509Certificate[] getAcceptedIssuers() {
return ISSUERS;
}
/**
* No checks performed.
*
* @param chain
* certificate chain
* @param authType
* authentication type
*/
public void checkServerTrusted(final X509Certificate[] chain, final String authType) {
}
/**
* No checks performed.
*
* @param chain
* certificate chain
* @param authType
* authentication type
*/
public void checkClientTrusted(final X509Certificate[] chain, final String authType) {
}
}
/**
* Instantiates a JNRPE client.
*
* @param sJNRPEServerIP
* The IP where the JNRPE is installed
* @param iJNRPEServerPort
* The port where the JNRPE server listens
* @param bSSL
* <code>true</code> if SSL must be used
*/
public JNRPEClient(final String sJNRPEServerIP, final int iJNRPEServerPort, final boolean bSSL) {
serverIPorURL = sJNRPEServerIP;
serverPort = iJNRPEServerPort;
useSSL = bSSL;
}
/**
* Creates a custom TrustManager that trusts any certificate.
*
* @return The custom trustmanager
*/
private TrustManager getTrustManager() {
return TRUSTALL_MGR;
}
/**
* Inovoke a command installed in JNRPE.
*
* @param sCommandName
* The name of the command to be invoked
* @param arguments
* The arguments to pass to the command (will substitute the
* $ARGSx$ parameters)
* @return The value returned by the server
* @throws JNRPEClientException
* Thrown on any communication error.
*/
public final ReturnValue sendCommand(final String sCommandName, final String... arguments) throws JNRPEClientException {
SocketFactory socketFactory;
Socket s = null;
try {
if (!useSSL) {
socketFactory = SocketFactory.getDefault();
} else {
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, new TrustManager[] { getTrustManager() }, new SecureRandom());
socketFactory = sslContext.getSocketFactory();
}
s = socketFactory.createSocket();
if (weakCipherSuitesEnabled) {
SSLSocket ssl = (SSLSocket) s;
ssl.setEnabledCipherSuites(ssl.getSupportedCipherSuites());
}
s.setSoTimeout((int) TimeUnit.SECOND.convert(communicationTimeout));
s.connect(new InetSocketAddress(serverIPorURL, serverPort));
JNRPERequest req = new JNRPERequest(sCommandName, arguments);
s.getOutputStream().write(req.toByteArray());
InputStream in = s.getInputStream();
JNRPEResponse res = new JNRPEResponse(in);
return new ReturnValue(Status.fromIntValue(res.getResultCode()), res.getMessage());
} catch (RuntimeException re) {
throw re;
} catch (Exception e) {
throw new JNRPEClientException(e);
} finally {
if (s != null) {
try {
s.close();
} catch (IOException e) {
// Ignore
}
}
}
}
/**
* Sets the connection timeout in seconds.
*
* @param iTimeout
* The new connection timeout. Default : 10
*/
public final void setTimeout(final int iTimeout) {
communicationTimeout = iTimeout;
}
/**
* Returns the currently configured connection timeout in seconds.
*
* @return The connection timeout
*/
public final int getTimeout() {
return communicationTimeout;
}
/**
* Configures the command line parser.
*
* @return The command line parser configuration
*/
private static Group configureCommandLine() {
DefaultOptionBuilder oBuilder = new DefaultOptionBuilder();
ArgumentBuilder aBuilder = new ArgumentBuilder();
GroupBuilder gBuilder = new GroupBuilder();
DefaultOption nosslOption = oBuilder.withLongName("nossl").withShortName("n").withDescription("Do no use SSL").create();
DefaultOption weakSslOption = oBuilder.withLongName("weakCiherSuites").withShortName("w").withDescription("Enable weak cipher suites").create();
DefaultOption unknownOption = oBuilder.withLongName("unknown").withShortName("u")
.withDescription("Make socket timeouts return an UNKNOWN state instead of CRITICAL").create();
DefaultOption hostOption = oBuilder.withLongName("host").withShortName("H")
.withDescription("The address of the host running the JNRPE/NRPE daemon")
.withArgument(aBuilder.withName("host").withMinimum(1).withMaximum(1).create()).create();
NumberValidator positiveInt = NumberValidator.getIntegerInstance();
positiveInt.setMinimum(0);
DefaultOption portOption = oBuilder
.withLongName("port")
.withShortName("p")
.withDescription("The port on which the daemon is running (default=5666)")
.withArgument(
aBuilder.withName("port").withMinimum(1).withMaximum(1).withDefault(Long.valueOf(DEFAULT_PORT)).withValidator(positiveInt)
.create()).create();
DefaultOption timeoutOption = oBuilder
.withLongName("timeout")
.withShortName("t")
.withDescription("Number of seconds before connection times out (default=10)")
.withArgument(
aBuilder.withName("timeout").withMinimum(1).withMaximum(1).withDefault(Long.valueOf(DEFAULT_TIMEOUT))
.withValidator(positiveInt).create()).create();
DefaultOption commandOption = oBuilder
.withLongName("command")
.withShortName("c")
.withDescription("The name of the command that the remote daemon should run")
.withArgument(
aBuilder
.withName("command")
.withMinimum(1)
.withMaximum(1).create()
)
.create();
DefaultOption argsOption = oBuilder
.withLongName("arglist")
.withShortName("a")
.withDescription(
"Optional arguments that should be passed to the command. Multiple arguments should be separated by "
+ "a space (' '). If provided, this must be the last option supplied on the command line.")
.withArgument(aBuilder.withName("arglist").withMinimum(1).create()).create();
DefaultOption helpOption = oBuilder.withLongName("help").withShortName("h").withDescription("Shows this help").create();
Group executionOption = gBuilder
.withOption(nosslOption)
.withOption(weakSslOption)
.withOption(unknownOption)
.withOption(hostOption)
.withOption(portOption)
.withOption(timeoutOption)
.withOption(commandOption)
.withOption(argsOption).create();
return gBuilder.withOption(executionOption).withOption(helpOption).withMinimum(1).withMaximum(1).create();
}
/**
* Prints the application version.
*/
private static void printVersion() {
System.out.println("jcheck_nrpe version " + JNRPEClient.class.getPackage().getImplementationVersion());
System.out.println("Copyright (c) 2013 Massimiliano Ziccardi");
System.out.println("Licensed under the Apache License, Version 2.0");
System.out.println();
}
/**
* Prints usage instrunctions and, eventually, an error message about the
* latest execution.
*
* @param e
* The exception error
*/
@SuppressWarnings("unchecked")
private static void printUsage(final Exception e) {
printVersion();
StringBuilder sbDivider = new StringBuilder("=");
if (e != null) {
System.out.println(e.getMessage() + "\n");
}
HelpFormatter hf = new HelpFormatter();
while (sbDivider.length() < hf.getPageWidth()) {
sbDivider.append('=');
}
// DISPLAY SETTING
Set displaySettings = hf.getDisplaySettings();
displaySettings.clear();
displaySettings.add(DisplaySetting.DISPLAY_GROUP_EXPANDED);
displaySettings.add(DisplaySetting.DISPLAY_PARENT_CHILDREN);
// USAGE SETTING
Set usageSettings = hf.getFullUsageSettings();
usageSettings.clear();
usageSettings.add(DisplaySetting.DISPLAY_PARENT_ARGUMENT);
usageSettings.add(DisplaySetting.DISPLAY_ARGUMENT_BRACKETED);
usageSettings.add(DisplaySetting.DISPLAY_PARENT_CHILDREN);
usageSettings.add(DisplaySetting.DISPLAY_GROUP_EXPANDED);
hf.setDivider(sbDivider.toString());
hf.setGroup(configureCommandLine());
hf.print();
}
/**
* Enables weak cipher suites.
*/
public final void enableWeakCipherSuites() {
weakCipherSuitesEnabled = true;
}
/**
*
* @param args
* command line arguments
* -
*/
public static void main(final String[] args) {
Parser parser = new Parser();
parser.setGroup(configureCommandLine());
CommandLine cli = null;
try {
cli = parser.parse(args);
if (cli.hasOption("--help")) {
printUsage(null);
}
//timeoutAsUnknown = cli.hasOption("--unknown");
String sHost = (String) cli.getValue("--host");
final Long port = (Long) cli.getValue("--port", Long.valueOf(DEFAULT_PORT));
String sCommand = (String) cli.getValue("--command", "_NRPE_CHECK");
JNRPEClient client = new JNRPEClient(sHost, port.intValue(), !cli.hasOption("--nossl"));
client.setTimeout(((Long) cli.getValue("--timeout", Long.valueOf(DEFAULT_TIMEOUT))).intValue());
if (cli.hasOption("--weakCiherSuites")) {
client.enableWeakCipherSuites();
}
@SuppressWarnings("unchecked")
List<String> argList = cli.getValues("--arglist");
ReturnValue ret = client.sendCommand(sCommand, argList.toArray(new String[argList.size()]));
System.out.println(ret.getMessage());
System.exit(ret.getStatus().intValue());
} catch (JNRPEClientException exc) {
Status returnStatus;
Throwable cause = exc.getCause();
if (cli.hasOption("--unknown") && cause instanceof SocketTimeoutException) {
returnStatus = Status.UNKNOWN;
} else {
returnStatus = Status.CRITICAL;
}
System.out.println(exc.getMessage());
System.exit(returnStatus.intValue());
} catch (OptionException oe) {
printUsage(oe);
}
}
}