// Copyright (C) 2012 jOVAL.org. All rights reserved.
// This software is licensed under the AGPL 3.0 license available at http://www.joval.org/agpl_v3.txt
package jwsmv.winrs;
import java.io.BufferedReader;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.util.ArrayList;
import javax.security.auth.login.FailedLoginException;
import jline.ConsoleReader;
import jline.Terminal;
import jwsmv.Constants;
import jwsmv.Shell;
import jwsmv.wsman.Port;
/**
* A winrs client. To use it, you must first do this on the target machine:
* winrm quickconfig
*
* @author David A. Solin
* @version %I% %G%
*/
public class Client implements Constants {
public static void main(String[] argv) {
String host = null;
String dir = "%USERPROFILE%";
String user = null;
String pass = null;
String debugFile = null;
boolean encrypt = true;
boolean compress = false;
boolean echo = true;
ArrayList<String> env = null;
int index = 0;
for (; index < argv.length; index++) {
if (!argv[index].startsWith("-") && !argv[index].startsWith("/")) {
break;
}
String arg = argv[index].substring(1);
int ptr = arg.indexOf(":");
if (arg.equals("?")) {
usage();
System.exit(0);
} else if (arg.startsWith("r:") || arg.startsWith("remote:")) {
host = arg.substring(ptr+1);
} else if (arg.equals("un") || arg.equals("unencrypted")) {
encrypt = false;
} else if (arg.equals("comp") || arg.equals("unencrypted")) {
compress = true;
} else if (arg.equals("noe") || arg.equals("noecho")) {
echo = false;
} else if (arg.startsWith("u:") || arg.startsWith("username:")) {
user = arg.substring(ptr+1);
} else if (arg.startsWith("p:") || arg.startsWith("password:")) {
pass = arg.substring(ptr+1);
} else if (arg.startsWith("d:") || arg.startsWith("directory:")) {
dir = arg.substring(ptr+1);
} else if (arg.equals("debug") && (index+1) < argv.length) {
debugFile = argv[++index];
} else if (arg.startsWith("env:") || arg.startsWith("environment:")) {
if (env == null) {
env = new ArrayList<String>();
}
env.add(arg.substring(ptr+1));
}
}
StringBuffer sb = new StringBuffer();
for (; index < argv.length; index++) {
if (sb.length() > 0) {
sb.append(" ");
}
sb.append(argv[index]);
}
String command = sb.toString();
if (host == null) {
System.out.println("Target host is missing");
usage();
System.exit(1);
} else if (user == null) {
System.out.println("Username is missing");
usage();
System.exit(1);
} else if (pass == null) {
System.out.println("Password is missing");
usage();
System.exit(1);
} else if (command.length() == 0) {
System.out.println("Command is missing");
usage();
System.exit(1);
} else {
String url = null;
if (host.startsWith("http://") || host.startsWith("https://")) {
url = host;
} else {
sb = new StringBuffer("http://");
sb.append(host);
if (host.indexOf(":") == -1) {
sb.append(":");
sb.append(Integer.toString(HTTP_PORT));
}
sb.append("/");
sb.append(URL_PREFIX);
url = sb.toString();
}
OutputStream debug = null;
try {
Port port = new Port(url, new PasswordAuthentication(user, pass.toCharArray()));
if (debugFile != null) {
debug = new FileOutputStream(debugFile);
port.setDebug(debug);
}
port.setEncryption(encrypt);
String[] environment = null;
if (env != null) {
environment = env.toArray(new String[env.size()]);
}
Shell shell = new Shell(port, compress, false, null, environment, dir);
Process p = shell.exec(command);
InputStream in = p.getInputStream();
StreamCopier errorThread = new StreamCopier(p.getErrorStream(), System.err);
errorThread.start();
ConsoleReader reader = new ConsoleReader();
if (echo) {
reader.getTerminal().enableEcho();
} else {
reader.getTerminal().disableEcho();
}
TerminalInput inputThread = new TerminalInput(reader, p.getOutputStream());
inputThread.start();
int ch;
while ((ch = in.read()) != -1) {
System.out.write((byte)(ch & 0xFF));
System.out.flush();
}
int exitValue = p.exitValue();
errorThread.close();
inputThread.close();
shell.dispose();
System.exit(exitValue);
} catch (FailedLoginException e) {
System.out.println("Authentication failed for user " + user);
System.exit(0x4DC); // ERROR_NOT_AUTHENTICATED
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
} finally {
if(debug != null) {
try {
debug.close();
} catch (IOException e) {
}
}
}
}
}
static void usage() {
System.out.println("Usage: winrs [-/SWITCH[:VALUE]] COMMAND");
System.out.println(" COMMAND - Any string that can be executed as a command in the cmd.exe shell");
System.out.println(" SWITCHES:");
System.out.println(" -r[emote]:ENDPOINT - The target endpoint using a DNS name or URL");
System.out.println(" -comp[ress] - Turn on compression.");
System.out.println(" -un[encrypted] - Specify that messages sent to the target");
System.out.println(" should not be encrypted");
System.out.println(" -u[sername]:USERNAME - The username for connecting to the target");
System.out.println(" -p[assword]:PASSWORD - The password for the user");
System.out.println(" -d[irectory]:PATH - Set the working directory for the command");
System.out.println(" -env[ironment]:STRING=VALUE - Set an environment variable. Use multiple");
System.out.println(" times to set multiple variables.");
System.out.println(" -noe[cho] - Disable echo");
System.out.println(" -debug FILENAME - Write SOAP messages to the specified file.");
System.out.println(" -? - Print this help message");
}
/**
* A simple stream copier that polls the input for available data.
*/
static class StreamCopier implements Runnable {
Thread thread;
InputStream in;
OutputStream out;
boolean open;
StreamCopier(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
thread = new Thread(this);
open = false;
}
void start() {
open = true;
thread.start();
}
void close() throws IOException {
open = false;
}
public void run() {
try {
while(open) {
int len = in.available();
if (len > 0) {
byte[] buff = new byte[len];
in.read(buff);
out.write(buff);
out.flush();
} else {
Thread.sleep(250);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Uses a JLine ConsoleReader.
*/
static class TerminalInput implements Runnable {
Thread thread;
ConsoleReader reader;
OutputStream out;
TerminalInput(ConsoleReader reader, OutputStream out) {
this.reader = reader;
this.out = out;
thread = new Thread(this);
}
void start() {
thread.start();
}
void close() throws IOException {
thread.interrupt();
}
public void run() {
try {
String line = null;
while((line = reader.readLine()) != null) {
out.write(line.getBytes());
out.write("\r\n".getBytes());
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}