Commit 7c094a26 authored by liang.tang's avatar liang.tang
Browse files

arthas-master

parents
Pipeline #220 failed with stages
in 0 seconds
package com.taobao.arthas.boot;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.InputMismatchException;
import com.taobao.arthas.common.AnsiLog;
import com.taobao.arthas.common.ExecutingCommand;
import com.taobao.arthas.common.IOUtils;
import com.taobao.arthas.common.JavaVersionUtils;
import com.taobao.arthas.common.PidUtils;
/**
*
* @author hengyunabc 2018-11-06
*
*/
public class ProcessUtils {
private static String FOUND_JAVA_HOME = null;
//status code from com.taobao.arthas.client.TelnetConsole
/**
* Process success
*/
public static final int STATUS_OK = 0;
/**
* Generic error
*/
public static final int STATUS_ERROR = 1;
/**
* Execute commands timeout
*/
public static final int STATUS_EXEC_TIMEOUT = 100;
/**
* Execute commands error
*/
public static final int STATUS_EXEC_ERROR = 101;
@SuppressWarnings("resource")
public static long select(boolean v, long telnetPortPid, String select) throws InputMismatchException {
Map<Long, String> processMap = listProcessByJps(v);
// Put the port that is already listening at the first
if (telnetPortPid > 0 && processMap.containsKey(telnetPortPid)) {
String telnetPortProcess = processMap.get(telnetPortPid);
processMap.remove(telnetPortPid);
Map<Long, String> newProcessMap = new LinkedHashMap<Long, String>();
newProcessMap.put(telnetPortPid, telnetPortProcess);
newProcessMap.putAll(processMap);
processMap = newProcessMap;
}
if (processMap.isEmpty()) {
AnsiLog.info("Can not find java process. Try to run `jps` command lists the instrumented Java HotSpot VMs on the target system.");
return -1;
}
// select target process by the '--select' option when match only one process
if (select != null && !select.trim().isEmpty()) {
int matchedSelectCount = 0;
Long matchedPid = null;
for (Entry<Long, String> entry : processMap.entrySet()) {
if (entry.getValue().contains(select)) {
matchedSelectCount++;
matchedPid = entry.getKey();
}
}
if (matchedSelectCount == 1) {
return matchedPid;
}
}
AnsiLog.info("Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.");
// print list
int count = 1;
for (String process : processMap.values()) {
if (count == 1) {
System.out.println("* [" + count + "]: " + process);
} else {
System.out.println(" [" + count + "]: " + process);
}
count++;
}
// read choice
String line = new Scanner(System.in).nextLine();
if (line.trim().isEmpty()) {
// get the first process id
return processMap.keySet().iterator().next();
}
int choice = new Scanner(line).nextInt();
if (choice <= 0 || choice > processMap.size()) {
return -1;
}
Iterator<Long> idIter = processMap.keySet().iterator();
for (int i = 1; i <= choice; ++i) {
if (i == choice) {
return idIter.next();
}
idIter.next();
}
return -1;
}
private static Map<Long, String> listProcessByJps(boolean v) {
Map<Long, String> result = new LinkedHashMap<Long, String>();
String jps = "jps";
File jpsFile = findJps();
if (jpsFile != null) {
jps = jpsFile.getAbsolutePath();
}
AnsiLog.debug("Try use jps to lis java process, jps: " + jps);
String[] command = null;
if (v) {
command = new String[] { jps, "-v", "-l" };
} else {
command = new String[] { jps, "-l" };
}
List<String> lines = ExecutingCommand.runNative(command);
AnsiLog.debug("jps result: " + lines);
long currentPid = Long.parseLong(PidUtils.currentPid());
for (String line : lines) {
String[] strings = line.trim().split("\\s+");
if (strings.length < 1) {
continue;
}
try {
long pid = Long.parseLong(strings[0]);
if (pid == currentPid) {
continue;
}
if (strings.length >= 2 && isJpsProcess(strings[1])) { // skip jps
continue;
}
result.put(pid, line);
} catch (Throwable e) {
// https://github.com/alibaba/arthas/issues/970
// ignore
}
}
return result;
}
/**
* <pre>
* 1. Try to find java home from System Property java.home
* 2. If jdk > 8, FOUND_JAVA_HOME set to java.home
* 3. If jdk <= 8, try to find tools.jar under java.home
* 4. If tools.jar do not exists under java.home, try to find System env JAVA_HOME
* 5. If jdk <= 8 and tools.jar do not exists under JAVA_HOME, throw IllegalArgumentException
* </pre>
*
* @return
*/
public static String findJavaHome() {
if (FOUND_JAVA_HOME != null) {
return FOUND_JAVA_HOME;
}
String javaHome = System.getProperty("java.home");
if (JavaVersionUtils.isLessThanJava9()) {
File toolsJar = new File(javaHome, "lib/tools.jar");
if (!toolsJar.exists()) {
toolsJar = new File(javaHome, "../lib/tools.jar");
}
if (!toolsJar.exists()) {
// maybe jre
toolsJar = new File(javaHome, "../../lib/tools.jar");
}
if (toolsJar.exists()) {
FOUND_JAVA_HOME = javaHome;
return FOUND_JAVA_HOME;
}
if (!toolsJar.exists()) {
AnsiLog.debug("Can not find tools.jar under java.home: " + javaHome);
String javaHomeEnv = System.getenv("JAVA_HOME");
if (javaHomeEnv != null && !javaHomeEnv.isEmpty()) {
AnsiLog.debug("Try to find tools.jar in System Env JAVA_HOME: " + javaHomeEnv);
// $JAVA_HOME/lib/tools.jar
toolsJar = new File(javaHomeEnv, "lib/tools.jar");
if (!toolsJar.exists()) {
// maybe jre
toolsJar = new File(javaHomeEnv, "../lib/tools.jar");
}
}
if (toolsJar.exists()) {
AnsiLog.info("Found java home from System Env JAVA_HOME: " + javaHomeEnv);
FOUND_JAVA_HOME = javaHomeEnv;
return FOUND_JAVA_HOME;
}
throw new IllegalArgumentException("Can not find tools.jar under java home: " + javaHome
+ ", please try to start arthas-boot with full path java. Such as /opt/jdk/bin/java -jar arthas-boot.jar");
}
} else {
FOUND_JAVA_HOME = javaHome;
}
return FOUND_JAVA_HOME;
}
public static void startArthasCore(long targetPid, List<String> attachArgs) {
// find java/java.exe, then try to find tools.jar
String javaHome = findJavaHome();
// find java/java.exe
File javaPath = findJava(javaHome);
if (javaPath == null) {
throw new IllegalArgumentException(
"Can not find java/java.exe executable file under java home: " + javaHome);
}
File toolsJar = findToolsJar(javaHome);
if (JavaVersionUtils.isLessThanJava9()) {
if (toolsJar == null || !toolsJar.exists()) {
throw new IllegalArgumentException("Can not find tools.jar under java home: " + javaHome);
}
}
List<String> command = new ArrayList<String>();
command.add(javaPath.getAbsolutePath());
if (toolsJar != null && toolsJar.exists()) {
command.add("-Xbootclasspath/a:" + toolsJar.getAbsolutePath());
}
command.addAll(attachArgs);
// "${JAVA_HOME}"/bin/java \
// ${opts} \
// -jar "${arthas_lib_dir}/arthas-core.jar" \
// -pid ${TARGET_PID} \
// -target-ip ${TARGET_IP} \
// -telnet-port ${TELNET_PORT} \
// -http-port ${HTTP_PORT} \
// -core "${arthas_lib_dir}/arthas-core.jar" \
// -agent "${arthas_lib_dir}/arthas-agent.jar"
ProcessBuilder pb = new ProcessBuilder(command);
try {
final Process proc = pb.start();
Thread redirectStdout = new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = proc.getInputStream();
try {
IOUtils.copy(inputStream, System.out);
} catch (IOException e) {
IOUtils.close(inputStream);
}
}
});
Thread redirectStderr = new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = proc.getErrorStream();
try {
IOUtils.copy(inputStream, System.err);
} catch (IOException e) {
IOUtils.close(inputStream);
}
}
});
redirectStdout.start();
redirectStderr.start();
redirectStdout.join();
redirectStderr.join();
int exitValue = proc.exitValue();
if (exitValue != 0) {
AnsiLog.error("attach fail, targetPid: " + targetPid);
System.exit(1);
}
} catch (Throwable e) {
// ignore
}
}
public static int startArthasClient(String arthasHomeDir, List<String> telnetArgs, OutputStream out) throws Throwable {
// start java telnet client
// find arthas-client.jar
URLClassLoader classLoader = new URLClassLoader(
new URL[]{new File(arthasHomeDir, "arthas-client.jar").toURI().toURL()});
Class<?> telnetConsoleClass = classLoader.loadClass("com.taobao.arthas.client.TelnetConsole");
Method processMethod = telnetConsoleClass.getMethod("process", String[].class);
//redirect System.out/System.err
PrintStream originSysOut = System.out;
PrintStream originSysErr = System.err;
PrintStream newOut = new PrintStream(out);
PrintStream newErr = new PrintStream(out);
// call TelnetConsole.process()
// fix https://github.com/alibaba/arthas/issues/833
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
System.setOut(newOut);
System.setErr(newErr);
Thread.currentThread().setContextClassLoader(classLoader);
return (Integer) processMethod.invoke(null, new Object[]{telnetArgs.toArray(new String[0])});
} catch (Throwable e) {
//java.lang.reflect.InvocationTargetException : java.net.ConnectException
e = e.getCause();
if (e instanceof IOException || e instanceof InterruptedException) {
// ignore connection error and interrupted error
return STATUS_ERROR;
} else {
// process error
AnsiLog.error("process error: {}", e.toString());
AnsiLog.error(e);
return STATUS_EXEC_ERROR;
}
} finally {
Thread.currentThread().setContextClassLoader(tccl);
//reset System.out/System.err
System.setOut(originSysOut);
System.setErr(originSysErr);
//flush output
newOut.flush();
newErr.flush();
}
}
private static File findJava(String javaHome) {
String[] paths = { "bin/java", "bin/java.exe", "../bin/java", "../bin/java.exe" };
List<File> javaList = new ArrayList<File>();
for (String path : paths) {
File javaFile = new File(javaHome, path);
if (javaFile.exists()) {
AnsiLog.debug("Found java: " + javaFile.getAbsolutePath());
javaList.add(javaFile);
}
}
if (javaList.isEmpty()) {
AnsiLog.debug("Can not find java/java.exe under current java home: " + javaHome);
return null;
}
// find the shortest path, jre path longer than jdk path
if (javaList.size() > 1) {
Collections.sort(javaList, new Comparator<File>() {
@Override
public int compare(File file1, File file2) {
try {
return file1.getCanonicalPath().length() - file2.getCanonicalPath().length();
} catch (IOException e) {
// ignore
}
return -1;
}
});
}
return javaList.get(0);
}
private static File findToolsJar(String javaHome) {
if (JavaVersionUtils.isGreaterThanJava8()) {
return null;
}
File toolsJar = new File(javaHome, "lib/tools.jar");
if (!toolsJar.exists()) {
toolsJar = new File(javaHome, "../lib/tools.jar");
}
if (!toolsJar.exists()) {
// maybe jre
toolsJar = new File(javaHome, "../../lib/tools.jar");
}
if (!toolsJar.exists()) {
throw new IllegalArgumentException("Can not find tools.jar under java home: " + javaHome);
}
AnsiLog.debug("Found tools.jar: " + toolsJar.getAbsolutePath());
return toolsJar;
}
private static File findJps() {
// Try to find jps under java.home and System env JAVA_HOME
String javaHome = System.getProperty("java.home");
String[] paths = { "bin/jps", "bin/jps.exe", "../bin/jps", "../bin/jps.exe" };
List<File> jpsList = new ArrayList<File>();
for (String path : paths) {
File jpsFile = new File(javaHome, path);
if (jpsFile.exists()) {
AnsiLog.debug("Found jps: " + jpsFile.getAbsolutePath());
jpsList.add(jpsFile);
}
}
if (jpsList.isEmpty()) {
AnsiLog.debug("Can not find jps under :" + javaHome);
String javaHomeEnv = System.getenv("JAVA_HOME");
AnsiLog.debug("Try to find jps under env JAVA_HOME :" + javaHomeEnv);
for (String path : paths) {
File jpsFile = new File(javaHomeEnv, path);
if (jpsFile.exists()) {
AnsiLog.debug("Found jps: " + jpsFile.getAbsolutePath());
jpsList.add(jpsFile);
}
}
}
if (jpsList.isEmpty()) {
AnsiLog.debug("Can not find jps under current java home: " + javaHome);
return null;
}
// find the shortest path, jre path longer than jdk path
if (jpsList.size() > 1) {
Collections.sort(jpsList, new Comparator<File>() {
@Override
public int compare(File file1, File file2) {
try {
return file1.getCanonicalPath().length() - file2.getCanonicalPath().length();
} catch (IOException e) {
// ignore
}
return -1;
}
});
}
return jpsList.get(0);
}
private static boolean isJpsProcess(String mainClassName) {
return "sun.tools.jps.Jps".equals(mainClassName) || "jdk.jcmd/sun.tools.jps.Jps".equals(mainClassName);
}
}
package com.taobao.arthas.boot;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class DownloadUtilsTest {
@Rule
public TemporaryFolder rootFolder = new TemporaryFolder();
@Test
public void testReadReleaseVersion() {
String releaseVersion = DownloadUtils.readLatestReleaseVersion();
Assert.assertNotNull(releaseVersion);
Assert.assertNotEquals("releaseVersion is empty", "", releaseVersion.trim());
System.err.println(releaseVersion);
}
@Test
public void testReadAllVersions() {
List<String> versions = DownloadUtils.readRemoteVersions();
Assert.assertEquals("", true, versions.contains("3.1.7"));
}
@Test
public void testAliyunDownload() throws IOException {
// fix travis-ci failed problem
if (TimeUnit.MILLISECONDS.toHours(TimeZone.getDefault().getOffset(System.currentTimeMillis())) == 8) {
String version = "3.3.7";
File folder = rootFolder.newFolder();
System.err.println(folder.getAbsolutePath());
DownloadUtils.downArthasPackaging("aliyun", false, version, folder.getAbsolutePath());
File as = new File(folder, version + File.separator + "arthas" + File.separator + "as.sh");
Assert.assertTrue(as.exists());
}
}
@Test
public void testCenterDownload() throws IOException {
String version = "3.1.7";
File folder = rootFolder.newFolder();
System.err.println(folder.getAbsolutePath());
DownloadUtils.downArthasPackaging("center", false, version, folder.getAbsolutePath());
File as = new File(folder, version + File.separator + "arthas" + File.separator + "as.sh");
Assert.assertTrue(as.exists());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>arthas-all</artifactId>
<groupId>com.taobao.arthas</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>arthas-client</artifactId>
<name>arthas-client</name>
<build>
<finalName>arthas-client</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.taobao.arthas.client.TelnetConsole</mainClass>
</manifest>
<manifestEntries>
<Created-By>core engine team, middleware group, alibaba inc.</Created-By>
<Specification-Title>${project.name}</Specification-Title>
<Specification-Version>${project.version}</Specification-Version>
<Implementation-Title>${project.name}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.middleware</groupId>
<artifactId>cli</artifactId>
</dependency>
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
</dependency>
</dependencies>
</project>
package com.taobao.arthas.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Writer;
/***
* This is a utility class providing a reader/writer capability required by the
* weatherTelnet, rexec, rshell, and rlogin example programs. The only point of
* the class is to hold the static method readWrite which spawns a reader thread
* and a writer thread. The reader thread reads from a local input source
* (presumably stdin) and writes the data to a remote output destination. The
* writer thread reads from a remote input source and writes to a local output
* destination. The threads terminate when the remote input source closes.
***/
public final class IOUtil {
public static final void readWrite(final InputStream remoteInput, final OutputStream remoteOutput,
final InputStream localInput, final Writer localOutput) {
Thread reader, writer;
reader = new Thread() {
@Override
public void run() {
int ch;
try {
while (!interrupted() && (ch = localInput.read()) != -1) {
remoteOutput.write(ch);
remoteOutput.flush();
}
} catch (IOException e) {
// e.printStackTrace();
}
}
};
writer = new Thread() {
@Override
public void run() {
try {
InputStreamReader reader = new InputStreamReader(remoteInput);
while (true) {
int singleChar = reader.read();
if (singleChar == -1) {
break;
}
localOutput.write(singleChar);
localOutput.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
writer.setPriority(Thread.currentThread().getPriority() + 1);
writer.start();
reader.setDaemon(true);
reader.start();
try {
writer.join();
reader.interrupt();
} catch (InterruptedException e) {
// Ignored
}
}
}
\ No newline at end of file
package com.taobao.arthas.client;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.apache.commons.net.telnet.InvalidTelnetOptionException;
import org.apache.commons.net.telnet.TelnetClient;
import org.apache.commons.net.telnet.TelnetOptionHandler;
import org.apache.commons.net.telnet.WindowSizeOptionHandler;
import com.taobao.arthas.common.OSUtils;
import com.taobao.arthas.common.UsageRender;
import com.taobao.middleware.cli.CLI;
import com.taobao.middleware.cli.CommandLine;
import com.taobao.middleware.cli.UsageMessageFormatter;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.CLIConfigurator;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import jline.Terminal;
import jline.TerminalSupport;
import jline.UnixTerminal;
import jline.console.ConsoleReader;
import jline.console.KeyMap;
/**
* @author ralf0131 2016-12-29 11:55.
* @author hengyunabc 2018-11-01
*/
@Name("arthas-client")
@Summary("Arthas Telnet Client")
@Description("EXAMPLES:\n" + " java -jar arthas-client.jar 127.0.0.1 3658\n"
+ " java -jar arthas-client.jar -c 'dashboard -n 1' \n"
+ " java -jar arthas-client.jar -f batch.as 127.0.0.1\n")
public class TelnetConsole {
private static final String PROMPT = "[arthas@"; // [arthas@49603]$
private static final int DEFAULT_CONNECTION_TIMEOUT = 5000; // 5000 ms
private static final byte CTRL_C = 0x03;
// ------- Status codes ------- //
/**
* Process success
*/
public static final int STATUS_OK = 0;
/**
* Generic error
*/
public static final int STATUS_ERROR = 1;
/**
* Execute commands timeout
*/
public static final int STATUS_EXEC_TIMEOUT = 100;
/**
* Execute commands error
*/
public static final int STATUS_EXEC_ERROR = 101;
private boolean help = false;
private String targetIp = "127.0.0.1";
private int port = 3658;
private String command;
private String batchFile;
private int executionTimeout = -1;
private Integer width = null;
private Integer height = null;
@Argument(argName = "target-ip", index = 0, required = false)
@Description("Target ip")
public void setTargetIp(String targetIp) {
this.targetIp = targetIp;
}
@Argument(argName = "port", index = 1, required = false)
@Description("The remote server port")
public void setPort(int port) {
this.port = port;
}
@Option(longName = "help", flag = true)
@Description("Print usage")
public void setHelp(boolean help) {
this.help = help;
}
@Option(shortName = "c", longName = "command")
@Description("Command to execute, multiple commands separated by ;")
public void setCommand(String command) {
this.command = command;
}
@Option(shortName = "f", longName = "batch-file")
@Description("The batch file to execute")
public void setBatchFile(String batchFile) {
this.batchFile = batchFile;
}
@Option(shortName = "t", longName = "execution-timeout")
@Description("The timeout (ms) of execute commands or batch file ")
public void setExecutionTimeout(int executionTimeout) {
this.executionTimeout = executionTimeout;
}
@Option(shortName = "w", longName = "width")
@Description("The terminal width")
public void setWidth(int width) {
this.width = width;
}
@Option(shortName = "h", longName = "height")
@Description("The terminal height")
public void setheight(int height) {
this.height = height;
}
public TelnetConsole() {
}
private static List<String> readLines(File batchFile) {
List<String> list = new ArrayList<String>();
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(batchFile));
String line = br.readLine();
while (line != null) {
list.add(line);
line = br.readLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
// ignore
}
}
}
return list;
}
public static void main(String[] args) throws Exception {
try {
int status = process(args, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(STATUS_OK);
}
});
System.exit(status);
} catch (Throwable e) {
e.printStackTrace();
CLI cli = CLIConfigurator.define(TelnetConsole.class);
System.out.println(usage(cli));
System.exit(STATUS_ERROR);
}
}
/**
* 提供给arthas-boot使用的主处理函数
*
* @param args
* @return status code
* @throws IOException
* @throws InterruptedException
*/
public static int process(String[] args) throws IOException, InterruptedException {
return process(args, null);
}
/**
* arthas client 主函数
* 注意: process()函数提供给arthas-boot使用,内部不能调用System.exit()结束进程的方法
*
* @param args
* @param eotEventCallback Ctrl+D signals an End of Transmission (EOT) event
* @return status code
* @throws IOException
*/
public static int process(String[] args, ActionListener eotEventCallback) throws IOException {
// support mingw/cygw jline color
if (OSUtils.isCygwinOrMinGW()) {
System.setProperty("jline.terminal", System.getProperty("jline.terminal", "jline.UnixTerminal"));
}
TelnetConsole telnetConsole = new TelnetConsole();
CLI cli = CLIConfigurator.define(TelnetConsole.class);
CommandLine commandLine = cli.parse(Arrays.asList(args));
CLIConfigurator.inject(commandLine, telnetConsole);
if (telnetConsole.isHelp()) {
System.out.println(usage(cli));
return STATUS_ERROR;
}
// Try to read cmds
List<String> cmds = new ArrayList<String>();
if (telnetConsole.getCommand() != null) {
for (String c : telnetConsole.getCommand().split(";")) {
cmds.add(c.trim());
}
} else if (telnetConsole.getBatchFile() != null) {
File file = new File(telnetConsole.getBatchFile());
if (!file.exists()) {
throw new IllegalArgumentException("batch file do not exist: " + telnetConsole.getBatchFile());
} else {
cmds.addAll(readLines(file));
}
}
final ConsoleReader consoleReader = new ConsoleReader(System.in, System.out);
consoleReader.setHandleUserInterrupt(true);
Terminal terminal = consoleReader.getTerminal();
// support catch ctrl+c event
terminal.disableInterruptCharacter();
if (terminal instanceof UnixTerminal) {
((UnixTerminal) terminal).disableLitteralNextCharacter();
}
try {
int width = TerminalSupport.DEFAULT_WIDTH;
int height = TerminalSupport.DEFAULT_HEIGHT;
if (!cmds.isEmpty()) {
// batch mode
if (telnetConsole.getWidth() != null) {
width = telnetConsole.getWidth();
}
if (telnetConsole.getheight() != null) {
height = telnetConsole.getheight();
}
} else {
// normal telnet client, get current terminal size
if (telnetConsole.getWidth() != null) {
width = telnetConsole.getWidth();
} else {
width = terminal.getWidth();
// hack for windows dos
if (OSUtils.isWindows()) {
width--;
}
}
if (telnetConsole.getheight() != null) {
height = telnetConsole.getheight();
} else {
height = terminal.getHeight();
}
}
final TelnetClient telnet = new TelnetClient();
telnet.setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT);
// send init terminal size
TelnetOptionHandler sizeOpt = new WindowSizeOptionHandler(width, height, true, true, false, false);
try {
telnet.addOptionHandler(sizeOpt);
} catch (InvalidTelnetOptionException e) {
// ignore
}
// ctrl + c event callback
consoleReader.getKeys().bind(Character.toString((char) CTRL_C), new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
consoleReader.getCursorBuffer().clear(); // clear current line
telnet.getOutputStream().write(CTRL_C);
telnet.getOutputStream().flush();
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
// ctrl + d event call back
consoleReader.getKeys().bind(Character.toString(KeyMap.CTRL_D), eotEventCallback);
try {
telnet.connect(telnetConsole.getTargetIp(), telnetConsole.getPort());
} catch (IOException e) {
System.out.println("Connect to telnet server error: " + telnetConsole.getTargetIp() + " "
+ telnetConsole.getPort());
throw e;
}
if (cmds.isEmpty()) {
IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), consoleReader.getInput(),
consoleReader.getOutput());
} else {
try {
return batchModeRun(telnet, cmds, telnetConsole.getExecutionTimeout());
} catch (Throwable e) {
System.out.println("Execute commands error: " + e.getMessage());
e.printStackTrace();
return STATUS_EXEC_ERROR;
} finally {
try {
telnet.disconnect();
} catch (IOException e) {
//ignore ex
}
}
}
return STATUS_OK;
} finally {
//reset terminal setting, fix https://github.com/alibaba/arthas/issues/1412
try {
terminal.restore();
} catch (Throwable e) {
System.out.println("Restore terminal settings failure: "+e.getMessage());
e.printStackTrace();
}
}
}
private static int batchModeRun(TelnetClient telnet, List<String> commands, final int executionTimeout)
throws IOException, InterruptedException {
if (commands.size() == 0) {
return STATUS_OK;
}
long startTime = System.currentTimeMillis();
final InputStream inputStream = telnet.getInputStream();
final OutputStream outputStream = telnet.getOutputStream();
final BlockingQueue<String> receviedPromptQueue = new LinkedBlockingQueue<String>(1);
Thread printResultThread = new Thread(new Runnable() {
@Override
public void run() {
try {
StringBuilder line = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
int b = -1;
while (true) {
b = in.read();
if (b == -1) {
break;
}
line.appendCodePoint(b);
// 检查到有 [arthas@ 时,意味着可以执行下一个命令了
int index = line.indexOf(PROMPT);
if (index > 0) {
line.delete(0, index + PROMPT.length());
receviedPromptQueue.put("");
}
System.out.print(Character.toChars(b));
}
} catch (Exception e) {
// ignore
}
}
});
printResultThread.start();
// send commands to arthas server
for (String command : commands) {
if (command.trim().isEmpty()) {
continue;
}
// try poll prompt and check timeout
while (receviedPromptQueue.poll(100, TimeUnit.MILLISECONDS) == null) {
if (executionTimeout > 0) {
long now = System.currentTimeMillis();
if (now - startTime > executionTimeout) {
return STATUS_EXEC_TIMEOUT;
}
}
}
// send command to server
outputStream.write((command + " | plaintext\n").getBytes());
outputStream.flush();
}
// 读到最后一个命令执行后的 prompt ,可以直接发 quit命令了。
receviedPromptQueue.take();
outputStream.write("quit\n".getBytes());
outputStream.flush();
System.out.println();
return STATUS_OK;
}
private static String usage(CLI cli) {
StringBuilder usageStringBuilder = new StringBuilder();
UsageMessageFormatter usageMessageFormatter = new UsageMessageFormatter();
usageMessageFormatter.setOptionComparator(null);
cli.usage(usageStringBuilder, usageMessageFormatter);
return UsageRender.render(usageStringBuilder.toString());
}
public String getTargetIp() {
return targetIp;
}
public int getPort() {
return port;
}
public String getCommand() {
return command;
}
public String getBatchFile() {
return batchFile;
}
public int getExecutionTimeout() {
return executionTimeout;
}
public Integer getWidth() {
return width;
}
public Integer getheight() {
return height;
}
public boolean isHelp() {
return help;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.Charset;
/***
* The DatagramSocketClient provides the basic operations that are required
* of client objects accessing datagram sockets. It is meant to be
* subclassed to avoid having to rewrite the same code over and over again
* to open a socket, close a socket, set timeouts, etc. Of special note
* is the {@link #setDatagramSocketFactory setDatagramSocketFactory }
* method, which allows you to control the type of DatagramSocket the
* DatagramSocketClient creates for network communications. This is
* especially useful for adding things like proxy support as well as better
* support for applets. For
* example, you could create a
* {@link org.apache.commons.net.DatagramSocketFactory}
* that
* requests browser security capabilities before creating a socket.
* All classes derived from DatagramSocketClient should use the
* {@link #_socketFactory_ _socketFactory_ } member variable to
* create DatagramSocket instances rather than instantiating
* them by directly invoking a constructor. By honoring this contract
* you guarantee that a user will always be able to provide his own
* Socket implementations by substituting his own SocketFactory.
*
*
* @see DatagramSocketFactory
***/
public abstract class DatagramSocketClient
{
/***
* The default DatagramSocketFactory shared by all DatagramSocketClient
* instances.
***/
private static final DatagramSocketFactory __DEFAULT_SOCKET_FACTORY =
new DefaultDatagramSocketFactory();
/**
* Charset to use for byte IO.
*/
private Charset charset = Charset.defaultCharset();
/*** The timeout to use after opening a socket. ***/
protected int _timeout_;
/*** The datagram socket used for the connection. ***/
protected DatagramSocket _socket_;
/***
* A status variable indicating if the client's socket is currently open.
***/
protected boolean _isOpen_;
/*** The datagram socket's DatagramSocketFactory. ***/
protected DatagramSocketFactory _socketFactory_;
/***
* Default constructor for DatagramSocketClient. Initializes
* _socket_ to null, _timeout_ to 0, and _isOpen_ to false.
***/
public DatagramSocketClient()
{
_socket_ = null;
_timeout_ = 0;
_isOpen_ = false;
_socketFactory_ = __DEFAULT_SOCKET_FACTORY;
}
/***
* Opens a DatagramSocket on the local host at the first available port.
* Also sets the timeout on the socket to the default timeout set
* by {@link #setDefaultTimeout setDefaultTimeout() }.
* <p>
* _isOpen_ is set to true after calling this method and _socket_
* is set to the newly opened socket.
*
* @exception SocketException If the socket could not be opened or the
* timeout could not be set.
***/
public void open() throws SocketException
{
_socket_ = _socketFactory_.createDatagramSocket();
_socket_.setSoTimeout(_timeout_);
_isOpen_ = true;
}
/***
* Opens a DatagramSocket on the local host at a specified port.
* Also sets the timeout on the socket to the default timeout set
* by {@link #setDefaultTimeout setDefaultTimeout() }.
* <p>
* _isOpen_ is set to true after calling this method and _socket_
* is set to the newly opened socket.
*
* @param port The port to use for the socket.
* @exception SocketException If the socket could not be opened or the
* timeout could not be set.
***/
public void open(int port) throws SocketException
{
_socket_ = _socketFactory_.createDatagramSocket(port);
_socket_.setSoTimeout(_timeout_);
_isOpen_ = true;
}
/***
* Opens a DatagramSocket at the specified address on the local host
* at a specified port.
* Also sets the timeout on the socket to the default timeout set
* by {@link #setDefaultTimeout setDefaultTimeout() }.
* <p>
* _isOpen_ is set to true after calling this method and _socket_
* is set to the newly opened socket.
*
* @param port The port to use for the socket.
* @param laddr The local address to use.
* @exception SocketException If the socket could not be opened or the
* timeout could not be set.
***/
public void open(int port, InetAddress laddr) throws SocketException
{
_socket_ = _socketFactory_.createDatagramSocket(port, laddr);
_socket_.setSoTimeout(_timeout_);
_isOpen_ = true;
}
/***
* Closes the DatagramSocket used for the connection.
* You should call this method after you've finished using the class
* instance and also before you call {@link #open open() }
* again. _isOpen_ is set to false and _socket_ is set to null.
* If you call this method when the client socket is not open,
* a NullPointerException is thrown.
***/
public void close()
{
if (_socket_ != null) {
_socket_.close();
}
_socket_ = null;
_isOpen_ = false;
}
/***
* Returns true if the client has a currently open socket.
*
* @return True if the client has a curerntly open socket, false otherwise.
***/
public boolean isOpen()
{
return _isOpen_;
}
/***
* Set the default timeout in milliseconds to use when opening a socket.
* After a call to open, the timeout for the socket is set using this value.
* This method should be used prior to a call to {@link #open open()}
* and should not be confused with {@link #setSoTimeout setSoTimeout()}
* which operates on the currently open socket. _timeout_ contains
* the new timeout value.
*
* @param timeout The timeout in milliseconds to use for the datagram socket
* connection.
***/
public void setDefaultTimeout(int timeout)
{
_timeout_ = timeout;
}
/***
* Returns the default timeout in milliseconds that is used when
* opening a socket.
*
* @return The default timeout in milliseconds that is used when
* opening a socket.
***/
public int getDefaultTimeout()
{
return _timeout_;
}
/***
* Set the timeout in milliseconds of a currently open connection.
* Only call this method after a connection has been opened
* by {@link #open open()}.
*
* @param timeout The timeout in milliseconds to use for the currently
* open datagram socket connection.
* @throws SocketException if an error setting the timeout
***/
public void setSoTimeout(int timeout) throws SocketException
{
_socket_.setSoTimeout(timeout);
}
/***
* Returns the timeout in milliseconds of the currently opened socket.
* If you call this method when the client socket is not open,
* a NullPointerException is thrown.
*
* @return The timeout in milliseconds of the currently opened socket.
* @throws SocketException if an error getting the timeout
***/
public int getSoTimeout() throws SocketException
{
return _socket_.getSoTimeout();
}
/***
* Returns the port number of the open socket on the local host used
* for the connection. If you call this method when the client socket
* is not open, a NullPointerException is thrown.
*
* @return The port number of the open socket on the local host used
* for the connection.
***/
public int getLocalPort()
{
return _socket_.getLocalPort();
}
/***
* Returns the local address to which the client's socket is bound.
* If you call this method when the client socket is not open, a
* NullPointerException is thrown.
*
* @return The local address to which the client's socket is bound.
***/
public InetAddress getLocalAddress()
{
return _socket_.getLocalAddress();
}
/***
* Sets the DatagramSocketFactory used by the DatagramSocketClient
* to open DatagramSockets. If the factory value is null, then a default
* factory is used (only do this to reset the factory after having
* previously altered it).
*
* @param factory The new DatagramSocketFactory the DatagramSocketClient
* should use.
***/
public void setDatagramSocketFactory(DatagramSocketFactory factory)
{
if (factory == null) {
_socketFactory_ = __DEFAULT_SOCKET_FACTORY;
} else {
_socketFactory_ = factory;
}
}
/**
* Gets the charset name.
*
* @return the charset name.
* @since 3.3
* TODO Will be deprecated once the code requires Java 1.6 as a mininmum
*/
public String getCharsetName() {
return charset.name();
}
/**
* Gets the charset.
*
* @return the charset.
* @since 3.3
*/
public Charset getCharset() {
return charset;
}
/**
* Sets the charset.
*
* @param charset the charset.
* @since 3.3
*/
public void setCharset(Charset charset) {
this.charset = charset;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/***
* The DatagramSocketFactory interface provides a means for the
* programmer to control the creation of datagram sockets and
* provide his own DatagramSocket implementations for use by all
* classes derived from
* {@link org.apache.commons.net.DatagramSocketClient}
* .
* This allows you to provide your own DatagramSocket implementations and
* to perform security checks or browser capability requests before
* creating a DatagramSocket.
*
*
***/
public interface DatagramSocketFactory
{
/***
* Creates a DatagramSocket on the local host at the first available port.
* @return the socket
*
* @exception SocketException If the socket could not be created.
***/
public DatagramSocket createDatagramSocket() throws SocketException;
/***
* Creates a DatagramSocket on the local host at a specified port.
*
* @param port The port to use for the socket.
* @return the socket
* @exception SocketException If the socket could not be created.
***/
public DatagramSocket createDatagramSocket(int port) throws SocketException;
/***
* Creates a DatagramSocket at the specified address on the local host
* at a specified port.
*
* @param port The port to use for the socket.
* @param laddr The local address to use.
* @return the socket
* @exception SocketException If the socket could not be created.
***/
public DatagramSocket createDatagramSocket(int port, InetAddress laddr)
throws SocketException;
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/***
* DefaultDatagramSocketFactory implements the DatagramSocketFactory
* interface by simply wrapping the java.net.DatagramSocket
* constructors. It is the default DatagramSocketFactory used by
* {@link org.apache.commons.net.DatagramSocketClient}
* implementations.
*
*
* @see DatagramSocketFactory
* @see DatagramSocketClient
* @see DatagramSocketClient#setDatagramSocketFactory
***/
public class DefaultDatagramSocketFactory implements DatagramSocketFactory
{
/***
* Creates a DatagramSocket on the local host at the first available port.
* @return a new DatagramSocket
* @exception SocketException If the socket could not be created.
***/
@Override
public DatagramSocket createDatagramSocket() throws SocketException
{
return new DatagramSocket();
}
/***
* Creates a DatagramSocket on the local host at a specified port.
*
* @param port The port to use for the socket.
* @return a new DatagramSocket
* @exception SocketException If the socket could not be created.
***/
@Override
public DatagramSocket createDatagramSocket(int port) throws SocketException
{
return new DatagramSocket(port);
}
/***
* Creates a DatagramSocket at the specified address on the local host
* at a specified port.
*
* @param port The port to use for the socket.
* @param laddr The local address to use.
* @return a new DatagramSocket
* @exception SocketException If the socket could not be created.
***/
@Override
public DatagramSocket createDatagramSocket(int port, InetAddress laddr)
throws SocketException
{
return new DatagramSocket(port, laddr);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.SocketFactory;
/***
* DefaultSocketFactory implements the SocketFactory interface by
* simply wrapping the java.net.Socket and java.net.ServerSocket
* constructors. It is the default SocketFactory used by
* {@link org.apache.commons.net.SocketClient}
* implementations.
*
*
* @see SocketFactory
* @see SocketClient
* @see SocketClient#setSocketFactory
***/
public class DefaultSocketFactory extends SocketFactory
{
/** The proxy to use when creating new sockets. */
private final Proxy connProxy;
/**
* The default constructor.
*/
public DefaultSocketFactory()
{
this(null);
}
/**
* A constructor for sockets with proxy support.
*
* @param proxy The Proxy to use when creating new Sockets.
* @since 3.2
*/
public DefaultSocketFactory(Proxy proxy)
{
connProxy = proxy;
}
/**
* Creates an unconnected Socket.
*
* @return A new unconnected Socket.
* @exception IOException If an I/O error occurs while creating the Socket.
* @since 3.2
*/
@Override
public Socket createSocket() throws IOException
{
if (connProxy != null)
{
return new Socket(connProxy);
}
return new Socket();
}
/***
* Creates a Socket connected to the given host and port.
*
* @param host The hostname to connect to.
* @param port The port to connect to.
* @return A Socket connected to the given host and port.
* @exception UnknownHostException If the hostname cannot be resolved.
* @exception IOException If an I/O error occurs while creating the Socket.
***/
@Override
public Socket createSocket(String host, int port)
throws UnknownHostException, IOException
{
if (connProxy != null)
{
Socket s = new Socket(connProxy);
s.connect(new InetSocketAddress(host, port));
return s;
}
return new Socket(host, port);
}
/***
* Creates a Socket connected to the given host and port.
*
* @param address The address of the host to connect to.
* @param port The port to connect to.
* @return A Socket connected to the given host and port.
* @exception IOException If an I/O error occurs while creating the Socket.
***/
@Override
public Socket createSocket(InetAddress address, int port)
throws IOException
{
if (connProxy != null)
{
Socket s = new Socket(connProxy);
s.connect(new InetSocketAddress(address, port));
return s;
}
return new Socket(address, port);
}
/***
* Creates a Socket connected to the given host and port and
* originating from the specified local address and port.
*
* @param host The hostname to connect to.
* @param port The port to connect to.
* @param localAddr The local address to use.
* @param localPort The local port to use.
* @return A Socket connected to the given host and port.
* @exception UnknownHostException If the hostname cannot be resolved.
* @exception IOException If an I/O error occurs while creating the Socket.
***/
@Override
public Socket createSocket(String host, int port,
InetAddress localAddr, int localPort)
throws UnknownHostException, IOException
{
if (connProxy != null)
{
Socket s = new Socket(connProxy);
s.bind(new InetSocketAddress(localAddr, localPort));
s.connect(new InetSocketAddress(host, port));
return s;
}
return new Socket(host, port, localAddr, localPort);
}
/***
* Creates a Socket connected to the given host and port and
* originating from the specified local address and port.
*
* @param address The address of the host to connect to.
* @param port The port to connect to.
* @param localAddr The local address to use.
* @param localPort The local port to use.
* @return A Socket connected to the given host and port.
* @exception IOException If an I/O error occurs while creating the Socket.
***/
@Override
public Socket createSocket(InetAddress address, int port,
InetAddress localAddr, int localPort)
throws IOException
{
if (connProxy != null)
{
Socket s = new Socket(connProxy);
s.bind(new InetSocketAddress(localAddr, localPort));
s.connect(new InetSocketAddress(address, port));
return s;
}
return new Socket(address, port, localAddr, localPort);
}
/***
* Creates a ServerSocket bound to a specified port. A port
* of 0 will create the ServerSocket on a system-determined free port.
*
* @param port The port on which to listen, or 0 to use any free port.
* @return A ServerSocket that will listen on a specified port.
* @exception IOException If an I/O error occurs while creating
* the ServerSocket.
***/
public ServerSocket createServerSocket(int port) throws IOException
{
return new ServerSocket(port);
}
/***
* Creates a ServerSocket bound to a specified port with a given
* maximum queue length for incoming connections. A port of 0 will
* create the ServerSocket on a system-determined free port.
*
* @param port The port on which to listen, or 0 to use any free port.
* @param backlog The maximum length of the queue for incoming connections.
* @return A ServerSocket that will listen on a specified port.
* @exception IOException If an I/O error occurs while creating
* the ServerSocket.
***/
public ServerSocket createServerSocket(int port, int backlog)
throws IOException
{
return new ServerSocket(port, backlog);
}
/***
* Creates a ServerSocket bound to a specified port on a given local
* address with a given maximum queue length for incoming connections.
* A port of 0 will
* create the ServerSocket on a system-determined free port.
*
* @param port The port on which to listen, or 0 to use any free port.
* @param backlog The maximum length of the queue for incoming connections.
* @param bindAddr The local address to which the ServerSocket should bind.
* @return A ServerSocket that will listen on a specified port.
* @exception IOException If an I/O error occurs while creating
* the ServerSocket.
***/
public ServerSocket createServerSocket(int port, int backlog,
InetAddress bindAddr)
throws IOException
{
return new ServerSocket(port, backlog, bindAddr);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.io.IOException;
/***
* This exception is used to indicate that the reply from a server
* could not be interpreted. Most of the NetComponents classes attempt
* to be as lenient as possible when receiving server replies. Many
* server implementations deviate from IETF protocol specifications, making
* it necessary to be as flexible as possible. However, there will be
* certain situations where it is not possible to continue an operation
* because the server reply could not be interpreted in a meaningful manner.
* In these cases, a MalformedServerReplyException should be thrown.
*
*
***/
public class MalformedServerReplyException extends IOException
{
private static final long serialVersionUID = 6006765264250543945L;
/*** Constructs a MalformedServerReplyException with no message ***/
public MalformedServerReplyException()
{
super();
}
/***
* Constructs a MalformedServerReplyException with a specified message.
*
* @param message The message explaining the reason for the exception.
***/
public MalformedServerReplyException(String message)
{
super(message);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.io.PrintStream;
import java.io.PrintWriter;
/***
* This is a support class for some of the example programs. It is
* a sample implementation of the ProtocolCommandListener interface
* which just prints out to a specified stream all command/reply traffic.
*
* @since 2.0
***/
public class PrintCommandListener implements ProtocolCommandListener
{
private final PrintWriter __writer;
private final boolean __nologin;
private final char __eolMarker;
private final boolean __directionMarker;
/**
* Create the default instance which prints everything.
*
* @param stream where to write the commands and responses
* e.g. System.out
* @since 3.0
*/
public PrintCommandListener(PrintStream stream)
{
this(new PrintWriter(stream));
}
/**
* Create an instance which optionally suppresses login command text
* and indicates where the EOL starts with the specified character.
*
* @param stream where to write the commands and responses
* @param suppressLogin if {@code true}, only print command name for login
*
* @since 3.0
*/
public PrintCommandListener(PrintStream stream, boolean suppressLogin) {
this(new PrintWriter(stream), suppressLogin);
}
/**
* Create an instance which optionally suppresses login command text
* and indicates where the EOL starts with the specified character.
*
* @param stream where to write the commands and responses
* @param suppressLogin if {@code true}, only print command name for login
* @param eolMarker if non-zero, add a marker just before the EOL.
*
* @since 3.0
*/
public PrintCommandListener(PrintStream stream, boolean suppressLogin, char eolMarker) {
this(new PrintWriter(stream), suppressLogin, eolMarker);
}
/**
* Create an instance which optionally suppresses login command text
* and indicates where the EOL starts with the specified character.
*
* @param stream where to write the commands and responses
* @param suppressLogin if {@code true}, only print command name for login
* @param eolMarker if non-zero, add a marker just before the EOL.
* @param showDirection if {@code true}, add {@code "> "} or {@code "< "} as appropriate to the output
*
* @since 3.0
*/
public PrintCommandListener(PrintStream stream, boolean suppressLogin, char eolMarker, boolean showDirection) {
this(new PrintWriter(stream), suppressLogin, eolMarker, showDirection);
}
/**
* Create the default instance which prints everything.
*
* @param writer where to write the commands and responses
*/
public PrintCommandListener(PrintWriter writer)
{
this(writer, false);
}
/**
* Create an instance which optionally suppresses login command text.
*
* @param writer where to write the commands and responses
* @param suppressLogin if {@code true}, only print command name for login
*
* @since 3.0
*/
public PrintCommandListener(PrintWriter writer, boolean suppressLogin)
{
this(writer, suppressLogin, (char) 0);
}
/**
* Create an instance which optionally suppresses login command text
* and indicates where the EOL starts with the specified character.
*
* @param writer where to write the commands and responses
* @param suppressLogin if {@code true}, only print command name for login
* @param eolMarker if non-zero, add a marker just before the EOL.
*
* @since 3.0
*/
public PrintCommandListener(PrintWriter writer, boolean suppressLogin, char eolMarker)
{
this(writer, suppressLogin, eolMarker, false);
}
/**
* Create an instance which optionally suppresses login command text
* and indicates where the EOL starts with the specified character.
*
* @param writer where to write the commands and responses
* @param suppressLogin if {@code true}, only print command name for login
* @param eolMarker if non-zero, add a marker just before the EOL.
* @param showDirection if {@code true}, add {@code ">} " or {@code "< "} as appropriate to the output
*
* @since 3.0
*/
public PrintCommandListener(PrintWriter writer, boolean suppressLogin, char eolMarker, boolean showDirection)
{
__writer = writer;
__nologin = suppressLogin;
__eolMarker = eolMarker;
__directionMarker = showDirection;
}
@Override
public void protocolCommandSent(ProtocolCommandEvent event)
{
if (__directionMarker) {
__writer.print("> ");
}
if (__nologin) {
String cmd = event.getCommand();
if ("PASS".equalsIgnoreCase(cmd) || "USER".equalsIgnoreCase(cmd)) {
__writer.print(cmd);
__writer.println(" *******"); // Don't bother with EOL marker for this!
} else {
final String IMAP_LOGIN = "LOGIN";
if (IMAP_LOGIN.equalsIgnoreCase(cmd)) { // IMAP
String msg = event.getMessage();
msg=msg.substring(0, msg.indexOf(IMAP_LOGIN)+IMAP_LOGIN.length());
__writer.print(msg);
__writer.println(" *******"); // Don't bother with EOL marker for this!
} else {
__writer.print(getPrintableString(event.getMessage()));
}
}
} else {
__writer.print(getPrintableString(event.getMessage()));
}
__writer.flush();
}
private String getPrintableString(String msg){
if (__eolMarker == 0) {
return msg;
}
int pos = msg.indexOf(SocketClient.NETASCII_EOL);
if (pos > 0) {
return msg.substring(0, pos) +
__eolMarker +
msg.substring(pos);
}
return msg;
}
@Override
public void protocolReplyReceived(ProtocolCommandEvent event)
{
if (__directionMarker) {
__writer.print("< ");
}
__writer.print(event.getMessage());
__writer.flush();
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.util.EventObject;
/***
* There exists a large class of IETF protocols that work by sending an
* ASCII text command and arguments to a server, and then receiving an
* ASCII text reply. For debugging and other purposes, it is extremely
* useful to log or keep track of the contents of the protocol messages.
* The ProtocolCommandEvent class coupled with the
* {@link org.apache.commons.net.ProtocolCommandListener}
* interface facilitate this process.
*
*
* @see ProtocolCommandListener
* @see ProtocolCommandSupport
***/
public class ProtocolCommandEvent extends EventObject
{
private static final long serialVersionUID = 403743538418947240L;
private final int __replyCode;
private final boolean __isCommand;
private final String __message, __command;
/***
* Creates a ProtocolCommandEvent signalling a command was sent to
* the server. ProtocolCommandEvents created with this constructor
* should only be sent after a command has been sent, but before the
* reply has been received.
*
* @param source The source of the event.
* @param command The string representation of the command type sent, not
* including the arguments (e.g., "STAT" or "GET").
* @param message The entire command string verbatim as sent to the server,
* including all arguments.
***/
public ProtocolCommandEvent(Object source, String command, String message)
{
super(source);
__replyCode = 0;
__message = message;
__isCommand = true;
__command = command;
}
/***
* Creates a ProtocolCommandEvent signalling a reply to a command was
* received. ProtocolCommandEvents created with this constructor
* should only be sent after a complete command reply has been received
* fromt a server.
*
* @param source The source of the event.
* @param replyCode The integer code indicating the natureof the reply.
* This will be the protocol integer value for protocols
* that use integer reply codes, or the reply class constant
* corresponding to the reply for protocols like POP3 that use
* strings like OK rather than integer codes (i.e., POP3Repy.OK).
* @param message The entire reply as received from the server.
***/
public ProtocolCommandEvent(Object source, int replyCode, String message)
{
super(source);
__replyCode = replyCode;
__message = message;
__isCommand = false;
__command = null;
}
/***
* Returns the string representation of the command type sent (e.g., "STAT"
* or "GET"). If the ProtocolCommandEvent is a reply event, then null
* is returned.
*
* @return The string representation of the command type sent, or null
* if this is a reply event.
***/
public String getCommand()
{
return __command;
}
/***
* Returns the reply code of the received server reply. Undefined if
* this is not a reply event.
*
* @return The reply code of the received server reply. Undefined if
* not a reply event.
***/
public int getReplyCode()
{
return __replyCode;
}
/***
* Returns true if the ProtocolCommandEvent was generated as a result
* of sending a command.
*
* @return true If the ProtocolCommandEvent was generated as a result
* of sending a command. False otherwise.
***/
public boolean isCommand()
{
return __isCommand;
}
/***
* Returns true if the ProtocolCommandEvent was generated as a result
* of receiving a reply.
*
* @return true If the ProtocolCommandEvent was generated as a result
* of receiving a reply. False otherwise.
***/
public boolean isReply()
{
return !isCommand();
}
/***
* Returns the entire message sent to or received from the server.
* Includes the line terminator.
*
* @return The entire message sent to or received from the server.
***/
public String getMessage()
{
return __message;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.util.EventListener;
/***
* There exists a large class of IETF protocols that work by sending an
* ASCII text command and arguments to a server, and then receiving an
* ASCII text reply. For debugging and other purposes, it is extremely
* useful to log or keep track of the contents of the protocol messages.
* The ProtocolCommandListener interface coupled with the
* {@link ProtocolCommandEvent} class facilitate this process.
* <p>
* To receive ProtocolCommandEvents, you merely implement the
* ProtocolCommandListener interface and register the class as a listener
* with a ProtocolCommandEvent source such as
* {@link org.apache.commons.net.ftp.FTPClient}.
*
*
* @see ProtocolCommandEvent
* @see ProtocolCommandSupport
***/
public interface ProtocolCommandListener extends EventListener
{
/***
* This method is invoked by a ProtocolCommandEvent source after
* sending a protocol command to a server.
*
* @param event The ProtocolCommandEvent fired.
***/
public void protocolCommandSent(ProtocolCommandEvent event);
/***
* This method is invoked by a ProtocolCommandEvent source after
* receiving a reply from a server.
*
* @param event The ProtocolCommandEvent fired.
***/
public void protocolReplyReceived(ProtocolCommandEvent event);
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.io.Serializable;
import java.util.EventListener;
import org.apache.commons.net.util.ListenerList;
/***
* ProtocolCommandSupport is a convenience class for managing a list of
* ProtocolCommandListeners and firing ProtocolCommandEvents. You can
* simply delegate ProtocolCommandEvent firing and listener
* registering/unregistering tasks to this class.
*
*
* @see ProtocolCommandEvent
* @see ProtocolCommandListener
***/
public class ProtocolCommandSupport implements Serializable
{
private static final long serialVersionUID = -8017692739988399978L;
private final Object __source;
private final ListenerList __listeners;
/***
* Creates a ProtocolCommandSupport instance using the indicated source
* as the source of ProtocolCommandEvents.
*
* @param source The source to use for all generated ProtocolCommandEvents.
***/
public ProtocolCommandSupport(Object source)
{
__listeners = new ListenerList();
__source = source;
}
/***
* Fires a ProtocolCommandEvent signalling the sending of a command to all
* registered listeners, invoking their
* {@link org.apache.commons.net.ProtocolCommandListener#protocolCommandSent protocolCommandSent() }
* methods.
*
* @param command The string representation of the command type sent, not
* including the arguments (e.g., "STAT" or "GET").
* @param message The entire command string verbatim as sent to the server,
* including all arguments.
***/
public void fireCommandSent(String command, String message)
{
ProtocolCommandEvent event;
event = new ProtocolCommandEvent(__source, command, message);
for (EventListener listener : __listeners)
{
((ProtocolCommandListener)listener).protocolCommandSent(event);
}
}
/***
* Fires a ProtocolCommandEvent signalling the reception of a command reply
* to all registered listeners, invoking their
* {@link org.apache.commons.net.ProtocolCommandListener#protocolReplyReceived protocolReplyReceived() }
* methods.
*
* @param replyCode The integer code indicating the natureof the reply.
* This will be the protocol integer value for protocols
* that use integer reply codes, or the reply class constant
* corresponding to the reply for protocols like POP3 that use
* strings like OK rather than integer codes (i.e., POP3Repy.OK).
* @param message The entire reply as received from the server.
***/
public void fireReplyReceived(int replyCode, String message)
{
ProtocolCommandEvent event;
event = new ProtocolCommandEvent(__source, replyCode, message);
for (EventListener listener : __listeners)
{
((ProtocolCommandListener)listener).protocolReplyReceived(event);
}
}
/***
* Adds a ProtocolCommandListener.
*
* @param listener The ProtocolCommandListener to add.
***/
public void addProtocolCommandListener(ProtocolCommandListener listener)
{
__listeners.addListener(listener);
}
/***
* Removes a ProtocolCommandListener.
*
* @param listener The ProtocolCommandListener to remove.
***/
public void removeProtocolCommandListener(ProtocolCommandListener listener)
{
__listeners.removeListener(listener);
}
/***
* Returns the number of ProtocolCommandListeners currently registered.
*
* @return The number of ProtocolCommandListeners currently registered.
***/
public int getListenerCount()
{
return __listeners.getListenerCount();
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.Charset;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
/**
* The SocketClient provides the basic operations that are required of
* client objects accessing sockets. It is meant to be
* subclassed to avoid having to rewrite the same code over and over again
* to open a socket, close a socket, set timeouts, etc. Of special note
* is the {@link #setSocketFactory setSocketFactory }
* method, which allows you to control the type of Socket the SocketClient
* creates for initiating network connections. This is especially useful
* for adding SSL or proxy support as well as better support for applets. For
* example, you could create a
* {@link javax.net.SocketFactory} that
* requests browser security capabilities before creating a socket.
* All classes derived from SocketClient should use the
* {@link #_socketFactory_ _socketFactory_ } member variable to
* create Socket and ServerSocket instances rather than instantiating
* them by directly invoking a constructor. By honoring this contract
* you guarantee that a user will always be able to provide his own
* Socket implementations by substituting his own SocketFactory.
* @see SocketFactory
*/
public abstract class SocketClient
{
/**
* The end of line character sequence used by most IETF protocols. That
* is a carriage return followed by a newline: "\r\n"
*/
public static final String NETASCII_EOL = "\r\n";
/** The default SocketFactory shared by all SocketClient instances. */
private static final SocketFactory __DEFAULT_SOCKET_FACTORY =
SocketFactory.getDefault();
/** The default {@link ServerSocketFactory} */
private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY =
ServerSocketFactory.getDefault();
/**
* A ProtocolCommandSupport object used to manage the registering of
* ProtocolCommandListeners and the firing of ProtocolCommandEvents.
*/
private ProtocolCommandSupport __commandSupport;
/** The timeout to use after opening a socket. */
protected int _timeout_;
/** The socket used for the connection. */
protected Socket _socket_;
/** The hostname used for the connection (null = no hostname supplied). */
protected String _hostname_;
/** The default port the client should connect to. */
protected int _defaultPort_;
/** The socket's InputStream. */
protected InputStream _input_;
/** The socket's OutputStream. */
protected OutputStream _output_;
/** The socket's SocketFactory. */
protected SocketFactory _socketFactory_;
/** The socket's ServerSocket Factory. */
protected ServerSocketFactory _serverSocketFactory_;
/** The socket's connect timeout (0 = infinite timeout) */
private static final int DEFAULT_CONNECT_TIMEOUT = 0;
protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
/** Hint for SO_RCVBUF size */
private int receiveBufferSize = -1;
/** Hint for SO_SNDBUF size */
private int sendBufferSize = -1;
/** The proxy to use when connecting. */
private Proxy connProxy;
/**
* Charset to use for byte IO.
*/
private Charset charset = Charset.defaultCharset();
/**
* Default constructor for SocketClient. Initializes
* _socket_ to null, _timeout_ to 0, _defaultPort to 0,
* _isConnected_ to false, charset to {@code Charset.defaultCharset()}
* and _socketFactory_ to a shared instance of
* {@link org.apache.commons.net.DefaultSocketFactory}.
*/
public SocketClient()
{
_socket_ = null;
_hostname_ = null;
_input_ = null;
_output_ = null;
_timeout_ = 0;
_defaultPort_ = 0;
_socketFactory_ = __DEFAULT_SOCKET_FACTORY;
_serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
}
/**
* Because there are so many connect() methods, the _connectAction_()
* method is provided as a means of performing some action immediately
* after establishing a connection, rather than reimplementing all
* of the connect() methods. The last action performed by every
* connect() method after opening a socket is to call this method.
* <p>
* This method sets the timeout on the just opened socket to the default
* timeout set by {@link #setDefaultTimeout setDefaultTimeout() },
* sets _input_ and _output_ to the socket's InputStream and OutputStream
* respectively, and sets _isConnected_ to true.
* <p>
* Subclasses overriding this method should start by calling
* <code> super._connectAction_() </code> first to ensure the
* initialization of the aforementioned protected variables.
* @throws IOException (SocketException) if a problem occurs with the socket
*/
protected void _connectAction_() throws IOException
{
_socket_.setSoTimeout(_timeout_);
_input_ = _socket_.getInputStream();
_output_ = _socket_.getOutputStream();
}
/**
* Opens a Socket connected to a remote host at the specified port and
* originating from the current host at a system assigned port.
* Before returning, {@link #_connectAction_ _connectAction_() }
* is called to perform connection initialization actions.
* <p>
* @param host The remote host.
* @param port The port to connect to on the remote host.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
*/
public void connect(InetAddress host, int port)
throws SocketException, IOException
{
_hostname_ = null;
_socket_ = _socketFactory_.createSocket();
if (receiveBufferSize != -1) {
_socket_.setReceiveBufferSize(receiveBufferSize);
}
if (sendBufferSize != -1) {
_socket_.setSendBufferSize(sendBufferSize);
}
_socket_.connect(new InetSocketAddress(host, port), connectTimeout);
_connectAction_();
}
/**
* Opens a Socket connected to a remote host at the specified port and
* originating from the current host at a system assigned port.
* Before returning, {@link #_connectAction_ _connectAction_() }
* is called to perform connection initialization actions.
* <p>
* @param hostname The name of the remote host.
* @param port The port to connect to on the remote host.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
* @exception java.net.UnknownHostException If the hostname cannot be resolved.
*/
public void connect(String hostname, int port)
throws SocketException, IOException
{
connect(InetAddress.getByName(hostname), port);
_hostname_ = hostname;
}
/**
* Opens a Socket connected to a remote host at the specified port and
* originating from the specified local address and port.
* Before returning, {@link #_connectAction_ _connectAction_() }
* is called to perform connection initialization actions.
* <p>
* @param host The remote host.
* @param port The port to connect to on the remote host.
* @param localAddr The local address to use.
* @param localPort The local port to use.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
*/
public void connect(InetAddress host, int port,
InetAddress localAddr, int localPort)
throws SocketException, IOException
{
_hostname_ = null;
_socket_ = _socketFactory_.createSocket();
if (receiveBufferSize != -1) {
_socket_.setReceiveBufferSize(receiveBufferSize);
}
if (sendBufferSize != -1) {
_socket_.setSendBufferSize(sendBufferSize);
}
_socket_.bind(new InetSocketAddress(localAddr, localPort));
_socket_.connect(new InetSocketAddress(host, port), connectTimeout);
_connectAction_();
}
/**
* Opens a Socket connected to a remote host at the specified port and
* originating from the specified local address and port.
* Before returning, {@link #_connectAction_ _connectAction_() }
* is called to perform connection initialization actions.
* <p>
* @param hostname The name of the remote host.
* @param port The port to connect to on the remote host.
* @param localAddr The local address to use.
* @param localPort The local port to use.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
* @exception java.net.UnknownHostException If the hostname cannot be resolved.
*/
public void connect(String hostname, int port,
InetAddress localAddr, int localPort)
throws SocketException, IOException
{
connect(InetAddress.getByName(hostname), port, localAddr, localPort);
_hostname_ = hostname;
}
/**
* Opens a Socket connected to a remote host at the current default port
* and originating from the current host at a system assigned port.
* Before returning, {@link #_connectAction_ _connectAction_() }
* is called to perform connection initialization actions.
* <p>
* @param host The remote host.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
*/
public void connect(InetAddress host) throws SocketException, IOException
{
_hostname_ = null;
connect(host, _defaultPort_);
}
/**
* Opens a Socket connected to a remote host at the current default
* port and originating from the current host at a system assigned port.
* Before returning, {@link #_connectAction_ _connectAction_() }
* is called to perform connection initialization actions.
* <p>
* @param hostname The name of the remote host.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
* @exception java.net.UnknownHostException If the hostname cannot be resolved.
*/
public void connect(String hostname) throws SocketException, IOException
{
connect(hostname, _defaultPort_);
_hostname_ = hostname;
}
/**
* Disconnects the socket connection.
* You should call this method after you've finished using the class
* instance and also before you call
* {@link #connect connect() }
* again. _isConnected_ is set to false, _socket_ is set to null,
* _input_ is set to null, and _output_ is set to null.
* <p>
* @exception IOException If there is an error closing the socket.
*/
public void disconnect() throws IOException
{
closeQuietly(_socket_);
closeQuietly(_input_);
closeQuietly(_output_);
_socket_ = null;
_hostname_ = null;
_input_ = null;
_output_ = null;
}
private void closeQuietly(Socket socket) {
if (socket != null){
try {
socket.close();
} catch (IOException e) {
// Ignored
}
}
}
private void closeQuietly(Closeable close){
if (close != null){
try {
close.close();
} catch (IOException e) {
// Ignored
}
}
}
/**
* Returns true if the client is currently connected to a server.
* <p>
* Delegates to {@link Socket#isConnected()}
* @return True if the client is currently connected to a server,
* false otherwise.
*/
public boolean isConnected()
{
if (_socket_ == null) {
return false;
}
return _socket_.isConnected();
}
/**
* Make various checks on the socket to test if it is available for use.
* Note that the only sure test is to use it, but these checks may help
* in some cases.
* @see <a href="https://issues.apache.org/jira/browse/NET-350">NET-350</a>
* @return {@code true} if the socket appears to be available for use
* @since 3.0
*/
public boolean isAvailable(){
if (isConnected()) {
try
{
if (_socket_.getInetAddress() == null) {
return false;
}
if (_socket_.getPort() == 0) {
return false;
}
if (_socket_.getRemoteSocketAddress() == null) {
return false;
}
if (_socket_.isClosed()) {
return false;
}
/* these aren't exact checks (a Socket can be half-open),
but since we usually require two-way data transfer,
we check these here too: */
if (_socket_.isInputShutdown()) {
return false;
}
if (_socket_.isOutputShutdown()) {
return false;
}
/* ignore the result, catch exceptions: */
_socket_.getInputStream();
_socket_.getOutputStream();
}
catch (IOException ioex)
{
return false;
}
return true;
} else {
return false;
}
}
/**
* Sets the default port the SocketClient should connect to when a port
* is not specified. The {@link #_defaultPort_ _defaultPort_ }
* variable stores this value. If never set, the default port is equal
* to zero.
* <p>
* @param port The default port to set.
*/
public void setDefaultPort(int port)
{
_defaultPort_ = port;
}
/**
* Returns the current value of the default port (stored in
* {@link #_defaultPort_ _defaultPort_ }).
* <p>
* @return The current value of the default port.
*/
public int getDefaultPort()
{
return _defaultPort_;
}
/**
* Set the default timeout in milliseconds to use when opening a socket.
* This value is only used previous to a call to
* {@link #connect connect()}
* and should not be confused with {@link #setSoTimeout setSoTimeout()}
* which operates on an the currently opened socket. _timeout_ contains
* the new timeout value.
* <p>
* @param timeout The timeout in milliseconds to use for the socket
* connection.
*/
public void setDefaultTimeout(int timeout)
{
_timeout_ = timeout;
}
/**
* Returns the default timeout in milliseconds that is used when
* opening a socket.
* <p>
* @return The default timeout in milliseconds that is used when
* opening a socket.
*/
public int getDefaultTimeout()
{
return _timeout_;
}
/**
* Set the timeout in milliseconds of a currently open connection.
* Only call this method after a connection has been opened
* by {@link #connect connect()}.
* <p>
* To set the initial timeout, use {@link #setDefaultTimeout(int)} instead.
*
* @param timeout The timeout in milliseconds to use for the currently
* open socket connection.
* @exception SocketException If the operation fails.
* @throws NullPointerException if the socket is not currently open
*/
public void setSoTimeout(int timeout) throws SocketException
{
_socket_.setSoTimeout(timeout);
}
/**
* Set the underlying socket send buffer size.
* <p>
* @param size The size of the buffer in bytes.
* @throws SocketException never thrown, but subclasses might want to do so
* @since 2.0
*/
public void setSendBufferSize(int size) throws SocketException {
sendBufferSize = size;
}
/**
* Get the current sendBuffer size
* @return the size, or -1 if not initialised
* @since 3.0
*/
protected int getSendBufferSize(){
return sendBufferSize;
}
/**
* Sets the underlying socket receive buffer size.
* <p>
* @param size The size of the buffer in bytes.
* @throws SocketException never (but subclasses may wish to do so)
* @since 2.0
*/
public void setReceiveBufferSize(int size) throws SocketException {
receiveBufferSize = size;
}
/**
* Get the current receivedBuffer size
* @return the size, or -1 if not initialised
* @since 3.0
*/
protected int getReceiveBufferSize(){
return receiveBufferSize;
}
/**
* Returns the timeout in milliseconds of the currently opened socket.
* <p>
* @return The timeout in milliseconds of the currently opened socket.
* @exception SocketException If the operation fails.
* @throws NullPointerException if the socket is not currently open
*/
public int getSoTimeout() throws SocketException
{
return _socket_.getSoTimeout();
}
/**
* Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
* currently opened socket.
* <p>
* @param on True if Nagle's algorithm is to be enabled, false if not.
* @exception SocketException If the operation fails.
* @throws NullPointerException if the socket is not currently open
*/
public void setTcpNoDelay(boolean on) throws SocketException
{
_socket_.setTcpNoDelay(on);
}
/**
* Returns true if Nagle's algorithm is enabled on the currently opened
* socket.
* <p>
* @return True if Nagle's algorithm is enabled on the currently opened
* socket, false otherwise.
* @exception SocketException If the operation fails.
* @throws NullPointerException if the socket is not currently open
*/
public boolean getTcpNoDelay() throws SocketException
{
return _socket_.getTcpNoDelay();
}
/**
* Sets the SO_KEEPALIVE flag on the currently opened socket.
*
* From the Javadocs, the default keepalive time is 2 hours (although this is
* implementation dependent). It looks as though the Windows WSA sockets implementation
* allows a specific keepalive value to be set, although this seems not to be the case on
* other systems.
* @param keepAlive If true, keepAlive is turned on
* @throws SocketException if there is a problem with the socket
* @throws NullPointerException if the socket is not currently open
* @since 2.2
*/
public void setKeepAlive(boolean keepAlive) throws SocketException {
_socket_.setKeepAlive(keepAlive);
}
/**
* Returns the current value of the SO_KEEPALIVE flag on the currently opened socket.
* Delegates to {@link Socket#getKeepAlive()}
* @return True if SO_KEEPALIVE is enabled.
* @throws SocketException if there is a problem with the socket
* @throws NullPointerException if the socket is not currently open
* @since 2.2
*/
public boolean getKeepAlive() throws SocketException {
return _socket_.getKeepAlive();
}
/**
* Sets the SO_LINGER timeout on the currently opened socket.
* <p>
* @param on True if linger is to be enabled, false if not.
* @param val The linger timeout (in hundredths of a second?)
* @exception SocketException If the operation fails.
* @throws NullPointerException if the socket is not currently open
*/
public void setSoLinger(boolean on, int val) throws SocketException
{
_socket_.setSoLinger(on, val);
}
/**
* Returns the current SO_LINGER timeout of the currently opened socket.
* <p>
* @return The current SO_LINGER timeout. If SO_LINGER is disabled returns
* -1.
* @exception SocketException If the operation fails.
* @throws NullPointerException if the socket is not currently open
*/
public int getSoLinger() throws SocketException
{
return _socket_.getSoLinger();
}
/**
* Returns the port number of the open socket on the local host used
* for the connection.
* Delegates to {@link Socket#getLocalPort()}
* <p>
* @return The port number of the open socket on the local host used
* for the connection.
* @throws NullPointerException if the socket is not currently open
*/
public int getLocalPort()
{
return _socket_.getLocalPort();
}
/**
* Returns the local address to which the client's socket is bound.
* Delegates to {@link Socket#getLocalAddress()}
* <p>
* @return The local address to which the client's socket is bound.
* @throws NullPointerException if the socket is not currently open
*/
public InetAddress getLocalAddress()
{
return _socket_.getLocalAddress();
}
/**
* Returns the port number of the remote host to which the client is
* connected.
* Delegates to {@link Socket#getPort()}
* <p>
* @return The port number of the remote host to which the client is
* connected.
* @throws NullPointerException if the socket is not currently open
*/
public int getRemotePort()
{
return _socket_.getPort();
}
/**
* @return The remote address to which the client is connected.
* Delegates to {@link Socket#getInetAddress()}
* @throws NullPointerException if the socket is not currently open
*/
public InetAddress getRemoteAddress()
{
return _socket_.getInetAddress();
}
/**
* Verifies that the remote end of the given socket is connected to the
* the same host that the SocketClient is currently connected to. This
* is useful for doing a quick security check when a client needs to
* accept a connection from a server, such as an FTP data connection or
* a BSD R command standard error stream.
* <p>
* @param socket the item to check against
* @return True if the remote hosts are the same, false if not.
*/
public boolean verifyRemote(Socket socket)
{
InetAddress host1, host2;
host1 = socket.getInetAddress();
host2 = getRemoteAddress();
return host1.equals(host2);
}
/**
* Sets the SocketFactory used by the SocketClient to open socket
* connections. If the factory value is null, then a default
* factory is used (only do this to reset the factory after having
* previously altered it).
* Any proxy setting is discarded.
* <p>
* @param factory The new SocketFactory the SocketClient should use.
*/
public void setSocketFactory(SocketFactory factory)
{
if (factory == null) {
_socketFactory_ = __DEFAULT_SOCKET_FACTORY;
} else {
_socketFactory_ = factory;
}
// re-setting the socket factory makes the proxy setting useless,
// so set the field to null so that getProxy() doesn't return a
// Proxy that we're actually not using.
connProxy = null;
}
/**
* Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
* connections. If the factory value is null, then a default
* factory is used (only do this to reset the factory after having
* previously altered it).
* <p>
* @param factory The new ServerSocketFactory the SocketClient should use.
* @since 2.0
*/
public void setServerSocketFactory(ServerSocketFactory factory) {
if (factory == null) {
_serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
} else {
_serverSocketFactory_ = factory;
}
}
/**
* Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's
* connect() method.
* @param connectTimeout The connection timeout to use (in ms)
* @since 2.0
*/
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
/**
* Get the underlying socket connection timeout.
* @return timeout (in ms)
* @since 2.0
*/
public int getConnectTimeout() {
return connectTimeout;
}
/**
* Get the underlying {@link ServerSocketFactory}
* @return The server socket factory
* @since 2.2
*/
public ServerSocketFactory getServerSocketFactory() {
return _serverSocketFactory_;
}
/**
* Adds a ProtocolCommandListener.
*
* @param listener The ProtocolCommandListener to add.
* @since 3.0
*/
public void addProtocolCommandListener(ProtocolCommandListener listener) {
getCommandSupport().addProtocolCommandListener(listener);
}
/**
* Removes a ProtocolCommandListener.
*
* @param listener The ProtocolCommandListener to remove.
* @since 3.0
*/
public void removeProtocolCommandListener(ProtocolCommandListener listener) {
getCommandSupport().removeProtocolCommandListener(listener);
}
/**
* If there are any listeners, send them the reply details.
*
* @param replyCode the code extracted from the reply
* @param reply the full reply text
* @since 3.0
*/
protected void fireReplyReceived(int replyCode, String reply) {
if (getCommandSupport().getListenerCount() > 0) {
getCommandSupport().fireReplyReceived(replyCode, reply);
}
}
/**
* If there are any listeners, send them the command details.
*
* @param command the command name
* @param message the complete message, including command name
* @since 3.0
*/
protected void fireCommandSent(String command, String message) {
if (getCommandSupport().getListenerCount() > 0) {
getCommandSupport().fireCommandSent(command, message);
}
}
/**
* Create the CommandSupport instance if required
*/
protected void createCommandSupport(){
__commandSupport = new ProtocolCommandSupport(this);
}
/**
* Subclasses can override this if they need to provide their own
* instance field for backwards compatibilty.
*
* @return the CommandSupport instance, may be {@code null}
* @since 3.0
*/
protected ProtocolCommandSupport getCommandSupport() {
return __commandSupport;
}
/**
* Sets the proxy for use with all the connections.
* The proxy is used for connections established after the
* call to this method.
*
* @param proxy the new proxy for connections.
* @since 3.2
*/
public void setProxy(Proxy proxy) {
setSocketFactory(new DefaultSocketFactory(proxy));
connProxy = proxy;
}
/**
* Gets the proxy for use with all the connections.
* @return the current proxy for connections.
*/
public Proxy getProxy() {
return connProxy;
}
/**
* Gets the charset name.
*
* @return the charset.
* @since 3.3
* @deprecated Since the code now requires Java 1.6 as a mininmum
*/
@Deprecated
public String getCharsetName() {
return charset.name();
}
/**
* Gets the charset.
*
* @return the charset.
* @since 3.3
*/
public Charset getCharset() {
return charset;
}
/**
* Sets the charset.
*
* @param charset the charset.
* @since 3.3
*/
public void setCharset(Charset charset) {
this.charset = charset;
}
/*
* N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility,
* so the abstract method is needed to pass the instance to the methods which were moved here.
*/
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net.telnet;
/***
* Implements the telnet echo option RFC 857.
***/
public class EchoOptionHandler extends TelnetOptionHandler
{
/***
* Constructor for the EchoOptionHandler. Allows defining desired
* initial setting for local/remote activation of this option and
* behaviour in case a local/remote activation request for this
* option is received.
* <p>
* @param initlocal - if set to true, a WILL is sent upon connection.
* @param initremote - if set to true, a DO is sent upon connection.
* @param acceptlocal - if set to true, any DO request is accepted.
* @param acceptremote - if set to true, any WILL request is accepted.
***/
public EchoOptionHandler(boolean initlocal, boolean initremote,
boolean acceptlocal, boolean acceptremote)
{
super(TelnetOption.ECHO, initlocal, initremote,
acceptlocal, acceptremote);
}
/***
* Constructor for the EchoOptionHandler. Initial and accept
* behaviour flags are set to false
***/
public EchoOptionHandler()
{
super(TelnetOption.ECHO, false, false, false, false);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net.telnet;
/***
* The InvalidTelnetOptionException is the exception that is
* thrown whenever a TelnetOptionHandler with an invlaid
* option code is registered in TelnetClient with addOptionHandler.
***/
public class InvalidTelnetOptionException extends Exception
{
private static final long serialVersionUID = -2516777155928793597L;
/***
* Option code
***/
private final int optionCode;
/***
* Error message
***/
private final String msg;
/***
* Constructor for the exception.
* <p>
* @param message - Error message.
* @param optcode - Option code.
***/
public InvalidTelnetOptionException(String message, int optcode)
{
optionCode = optcode;
msg = message;
}
/***
* Gets the error message of ths exception.
* <p>
* @return the error message.
***/
@Override
public String getMessage()
{
return (msg + ": " + optionCode);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net.telnet;
/***
* Simple option handler that can be used for options
* that don't require subnegotiation.
***/
public class SimpleOptionHandler extends TelnetOptionHandler
{
/***
* Constructor for the SimpleOptionHandler. Allows defining desired
* initial setting for local/remote activation of this option and
* behaviour in case a local/remote activation request for this
* option is received.
* <p>
* @param optcode - option code.
* @param initlocal - if set to true, a WILL is sent upon connection.
* @param initremote - if set to true, a DO is sent upon connection.
* @param acceptlocal - if set to true, any DO request is accepted.
* @param acceptremote - if set to true, any WILL request is accepted.
***/
public SimpleOptionHandler(int optcode,
boolean initlocal,
boolean initremote,
boolean acceptlocal,
boolean acceptremote)
{
super(optcode, initlocal, initremote,
acceptlocal, acceptremote);
}
/***
* Constructor for the SimpleOptionHandler. Initial and accept
* behaviour flags are set to false
* <p>
* @param optcode - option code.
***/
public SimpleOptionHandler(int optcode)
{
super(optcode, false, false, false, false);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net.telnet;
/***
* Implements the telnet suppress go ahead option RFC 858.
***/
public class SuppressGAOptionHandler extends TelnetOptionHandler
{
/***
* Constructor for the SuppressGAOptionHandler. Allows defining desired
* initial setting for local/remote activation of this option and
* behaviour in case a local/remote activation request for this
* option is received.
* <p>
* @param initlocal - if set to true, a WILL is sent upon connection.
* @param initremote - if set to true, a DO is sent upon connection.
* @param acceptlocal - if set to true, any DO request is accepted.
* @param acceptremote - if set to true, any WILL request is accepted.
***/
public SuppressGAOptionHandler(boolean initlocal, boolean initremote,
boolean acceptlocal, boolean acceptremote)
{
super(TelnetOption.SUPPRESS_GO_AHEAD, initlocal, initremote,
acceptlocal, acceptremote);
}
/***
* Constructor for the SuppressGAOptionHandler. Initial and accept
* behaviour flags are set to false
***/
public SuppressGAOptionHandler()
{
super(TelnetOption.SUPPRESS_GO_AHEAD, false, false, false, false);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net.telnet;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.util.Arrays;
import org.apache.commons.net.SocketClient;
class Telnet extends SocketClient
{
static final boolean debug = /*true;*/ false;
static final boolean debugoptions = /*true;*/ false;
static final byte[] _COMMAND_DO = {
(byte)TelnetCommand.IAC, (byte)TelnetCommand.DO
};
static final byte[] _COMMAND_DONT = {
(byte)TelnetCommand.IAC, (byte)TelnetCommand.DONT
};
static final byte[] _COMMAND_WILL = {
(byte)TelnetCommand.IAC, (byte)TelnetCommand.WILL
};
static final byte[] _COMMAND_WONT = {
(byte)TelnetCommand.IAC, (byte)TelnetCommand.WONT
};
static final byte[] _COMMAND_SB = {
(byte)TelnetCommand.IAC, (byte)TelnetCommand.SB
};
static final byte[] _COMMAND_SE = {
(byte)TelnetCommand.IAC, (byte)TelnetCommand.SE
};
static final int _WILL_MASK = 0x01, _DO_MASK = 0x02,
_REQUESTED_WILL_MASK = 0x04, _REQUESTED_DO_MASK = 0x08;
/* public */
static final int DEFAULT_PORT = 23;
int[] _doResponse, _willResponse, _options;
/* TERMINAL-TYPE option (start)*/
/***
* Terminal type option
***/
protected static final int TERMINAL_TYPE = 24;
/***
* Send (for subnegotiation)
***/
protected static final int TERMINAL_TYPE_SEND = 1;
/***
* Is (for subnegotiation)
***/
protected static final int TERMINAL_TYPE_IS = 0;
/***
* Is sequence (for subnegotiation)
***/
static final byte[] _COMMAND_IS = {
(byte) TERMINAL_TYPE, (byte) TERMINAL_TYPE_IS
};
/***
* Terminal type
***/
private String terminalType = null;
/* TERMINAL-TYPE option (end)*/
/* open TelnetOptionHandler functionality (start)*/
/***
* Array of option handlers
***/
private final TelnetOptionHandler optionHandlers[];
/* open TelnetOptionHandler functionality (end)*/
/* Code Section added for supporting AYT (start)*/
/***
* AYT sequence
***/
static final byte[] _COMMAND_AYT = {
(byte) TelnetCommand.IAC, (byte) TelnetCommand.AYT
};
/***
* monitor to wait for AYT
***/
private final Object aytMonitor = new Object();
/***
* flag for AYT
***/
private volatile boolean aytFlag = true;
/* Code Section added for supporting AYT (end)*/
/***
* The stream on which to spy
***/
private volatile OutputStream spyStream = null;
/***
* The notification handler
***/
private TelnetNotificationHandler __notifhand = null;
/***
* Empty Constructor
***/
Telnet()
{
setDefaultPort(DEFAULT_PORT);
_doResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
_willResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
_options = new int[TelnetOption.MAX_OPTION_VALUE + 1];
optionHandlers =
new TelnetOptionHandler[TelnetOption.MAX_OPTION_VALUE + 1];
}
/* TERMINAL-TYPE option (start)*/
/***
* This constructor lets you specify the terminal type.
*
* @param termtype - terminal type to be negotiated (ej. VT100)
***/
Telnet(String termtype)
{
setDefaultPort(DEFAULT_PORT);
_doResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
_willResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
_options = new int[TelnetOption.MAX_OPTION_VALUE + 1];
terminalType = termtype;
optionHandlers =
new TelnetOptionHandler[TelnetOption.MAX_OPTION_VALUE + 1];
}
/* TERMINAL-TYPE option (end)*/
/***
* Looks for the state of the option.
*
* @return returns true if a will has been acknowledged
*
* @param option - option code to be looked up.
***/
boolean _stateIsWill(int option)
{
return ((_options[option] & _WILL_MASK) != 0);
}
/***
* Looks for the state of the option.
*
* @return returns true if a wont has been acknowledged
*
* @param option - option code to be looked up.
***/
boolean _stateIsWont(int option)
{
return !_stateIsWill(option);
}
/***
* Looks for the state of the option.
*
* @return returns true if a do has been acknowledged
*
* @param option - option code to be looked up.
***/
boolean _stateIsDo(int option)
{
return ((_options[option] & _DO_MASK) != 0);
}
/***
* Looks for the state of the option.
*
* @return returns true if a dont has been acknowledged
*
* @param option - option code to be looked up.
***/
boolean _stateIsDont(int option)
{
return !_stateIsDo(option);
}
/***
* Looks for the state of the option.
*
* @return returns true if a will has been reuqested
*
* @param option - option code to be looked up.
***/
boolean _requestedWill(int option)
{
return ((_options[option] & _REQUESTED_WILL_MASK) != 0);
}
/***
* Looks for the state of the option.
*
* @return returns true if a wont has been reuqested
*
* @param option - option code to be looked up.
***/
boolean _requestedWont(int option)
{
return !_requestedWill(option);
}
/***
* Looks for the state of the option.
*
* @return returns true if a do has been reuqested
*
* @param option - option code to be looked up.
***/
boolean _requestedDo(int option)
{
return ((_options[option] & _REQUESTED_DO_MASK) != 0);
}
/***
* Looks for the state of the option.
*
* @return returns true if a dont has been reuqested
*
* @param option - option code to be looked up.
***/
boolean _requestedDont(int option)
{
return !_requestedDo(option);
}
/***
* Sets the state of the option.
*
* @param option - option code to be set.
* @throws IOException
***/
void _setWill(int option) throws IOException
{
_options[option] |= _WILL_MASK;
/* open TelnetOptionHandler functionality (start)*/
if (_requestedWill(option))
{
if (optionHandlers[option] != null)
{
optionHandlers[option].setWill(true);
int subneg[] =
optionHandlers[option].startSubnegotiationLocal();
if (subneg != null)
{
_sendSubnegotiation(subneg);
}
}
}
/* open TelnetOptionHandler functionality (end)*/
}
/***
* Sets the state of the option.
*
* @param option - option code to be set.
* @throws IOException
***/
void _setDo(int option) throws IOException
{
_options[option] |= _DO_MASK;
/* open TelnetOptionHandler functionality (start)*/
if (_requestedDo(option))
{
if (optionHandlers[option] != null)
{
optionHandlers[option].setDo(true);
int subneg[] =
optionHandlers[option].startSubnegotiationRemote();
if (subneg != null)
{
_sendSubnegotiation(subneg);
}
}
}
/* open TelnetOptionHandler functionality (end)*/
}
/***
* Sets the state of the option.
*
* @param option - option code to be set.
***/
void _setWantWill(int option)
{
_options[option] |= _REQUESTED_WILL_MASK;
}
/***
* Sets the state of the option.
*
* @param option - option code to be set.
***/
void _setWantDo(int option)
{
_options[option] |= _REQUESTED_DO_MASK;
}
/***
* Sets the state of the option.
*
* @param option - option code to be set.
***/
void _setWont(int option)
{
_options[option] &= ~_WILL_MASK;
/* open TelnetOptionHandler functionality (start)*/
if (optionHandlers[option] != null)
{
optionHandlers[option].setWill(false);
}
/* open TelnetOptionHandler functionality (end)*/
}
/***
* Sets the state of the option.
*
* @param option - option code to be set.
***/
void _setDont(int option)
{
_options[option] &= ~_DO_MASK;
/* open TelnetOptionHandler functionality (start)*/
if (optionHandlers[option] != null)
{
optionHandlers[option].setDo(false);
}
/* open TelnetOptionHandler functionality (end)*/
}
/***
* Sets the state of the option.
*
* @param option - option code to be set.
***/
void _setWantWont(int option)
{
_options[option] &= ~_REQUESTED_WILL_MASK;
}
/***
* Sets the state of the option.
*
* @param option - option code to be set.
***/
void _setWantDont(int option)
{
_options[option] &= ~_REQUESTED_DO_MASK;
}
/**
* Processes a COMMAND.
*
* @param command - option code to be set.
**/
void _processCommand(int command)
{
if (debugoptions)
{
System.err.println("RECEIVED COMMAND: " + command);
}
if (__notifhand != null)
{
__notifhand.receivedNegotiation(
TelnetNotificationHandler.RECEIVED_COMMAND, command);
}
}
/**
* Processes a DO request.
*
* @param option - option code to be set.
* @throws IOException - Exception in I/O.
**/
void _processDo(int option) throws IOException
{
if (debugoptions)
{
System.err.println("RECEIVED DO: "
+ TelnetOption.getOption(option));
}
if (__notifhand != null)
{
__notifhand.receivedNegotiation(
TelnetNotificationHandler.RECEIVED_DO,
option);
}
boolean acceptNewState = false;
/* open TelnetOptionHandler functionality (start)*/
if (optionHandlers[option] != null)
{
acceptNewState = optionHandlers[option].getAcceptLocal();
}
else
{
/* open TelnetOptionHandler functionality (end)*/
/* TERMINAL-TYPE option (start)*/
if (option == TERMINAL_TYPE)
{
if ((terminalType != null) && (terminalType.length() > 0))
{
acceptNewState = true;
}
}
/* TERMINAL-TYPE option (end)*/
/* open TelnetOptionHandler functionality (start)*/
}
/* open TelnetOptionHandler functionality (end)*/
if (_willResponse[option] > 0)
{
--_willResponse[option];
if (_willResponse[option] > 0 && _stateIsWill(option))
{
--_willResponse[option];
}
}
if (_willResponse[option] == 0)
{
if (_requestedWont(option))
{
switch (option)
{
default:
break;
}
if (acceptNewState)
{
_setWantWill(option);
_sendWill(option);
}
else
{
++_willResponse[option];
_sendWont(option);
}
}
else
{
// Other end has acknowledged option.
switch (option)
{
default:
break;
}
}
}
_setWill(option);
}
/**
* Processes a DONT request.
*
* @param option - option code to be set.
* @throws IOException - Exception in I/O.
**/
void _processDont(int option) throws IOException
{
if (debugoptions)
{
System.err.println("RECEIVED DONT: "
+ TelnetOption.getOption(option));
}
if (__notifhand != null)
{
__notifhand.receivedNegotiation(
TelnetNotificationHandler.RECEIVED_DONT,
option);
}
if (_willResponse[option] > 0)
{
--_willResponse[option];
if (_willResponse[option] > 0 && _stateIsWont(option))
{
--_willResponse[option];
}
}
if (_willResponse[option] == 0 && _requestedWill(option))
{
switch (option)
{
default:
break;
}
/* FIX for a BUG in the negotiation (start)*/
if ((_stateIsWill(option)) || (_requestedWill(option)))
{
_sendWont(option);
}
_setWantWont(option);
/* FIX for a BUG in the negotiation (end)*/
}
_setWont(option);
}
/**
* Processes a WILL request.
*
* @param option - option code to be set.
* @throws IOException - Exception in I/O.
**/
void _processWill(int option) throws IOException
{
if (debugoptions)
{
System.err.println("RECEIVED WILL: "
+ TelnetOption.getOption(option));
}
if (__notifhand != null)
{
__notifhand.receivedNegotiation(
TelnetNotificationHandler.RECEIVED_WILL,
option);
}
boolean acceptNewState = false;
/* open TelnetOptionHandler functionality (start)*/
if (optionHandlers[option] != null)
{
acceptNewState = optionHandlers[option].getAcceptRemote();
}
/* open TelnetOptionHandler functionality (end)*/
if (_doResponse[option] > 0)
{
--_doResponse[option];
if (_doResponse[option] > 0 && _stateIsDo(option))
{
--_doResponse[option];
}
}
if (_doResponse[option] == 0 && _requestedDont(option))
{
switch (option)
{
default:
break;
}
if (acceptNewState)
{
_setWantDo(option);
_sendDo(option);
}
else
{
++_doResponse[option];
_sendDont(option);
}
}
_setDo(option);
}
/**
* Processes a WONT request.
*
* @param option - option code to be set.
* @throws IOException - Exception in I/O.
**/
void _processWont(int option) throws IOException
{
if (debugoptions)
{
System.err.println("RECEIVED WONT: "
+ TelnetOption.getOption(option));
}
if (__notifhand != null)
{
__notifhand.receivedNegotiation(
TelnetNotificationHandler.RECEIVED_WONT,
option);
}
if (_doResponse[option] > 0)
{
--_doResponse[option];
if (_doResponse[option] > 0 && _stateIsDont(option))
{
--_doResponse[option];
}
}
if (_doResponse[option] == 0 && _requestedDo(option))
{
switch (option)
{
default:
break;
}
/* FIX for a BUG in the negotiation (start)*/
if ((_stateIsDo(option)) || (_requestedDo(option)))
{
_sendDont(option);
}
_setWantDont(option);
/* FIX for a BUG in the negotiation (end)*/
}
_setDont(option);
}
/* TERMINAL-TYPE option (start)*/
/**
* Processes a suboption negotiation.
*
* @param suboption - subnegotiation data received
* @param suboptionLength - length of data received
* @throws IOException - Exception in I/O.
**/
void _processSuboption(int suboption[], int suboptionLength)
throws IOException
{
if (debug)
{
System.err.println("PROCESS SUBOPTION.");
}
/* open TelnetOptionHandler functionality (start)*/
if (suboptionLength > 0)
{
if (optionHandlers[suboption[0]] != null)
{
int responseSuboption[] =
optionHandlers[suboption[0]].answerSubnegotiation(suboption,
suboptionLength);
_sendSubnegotiation(responseSuboption);
}
else
{
if (suboptionLength > 1)
{
if (debug)
{
for (int ii = 0; ii < suboptionLength; ii++)
{
System.err.println("SUB[" + ii + "]: "
+ suboption[ii]);
}
}
if ((suboption[0] == TERMINAL_TYPE)
&& (suboption[1] == TERMINAL_TYPE_SEND))
{
_sendTerminalType();
}
}
}
}
/* open TelnetOptionHandler functionality (end)*/
}
/***
* Sends terminal type information.
*
* @throws IOException - Exception in I/O.
***/
final synchronized void _sendTerminalType()
throws IOException
{
if (debug)
{
System.err.println("SEND TERMINAL-TYPE: " + terminalType);
}
if (terminalType != null)
{
_output_.write(_COMMAND_SB);
_output_.write(_COMMAND_IS);
_output_.write(terminalType.getBytes(getCharset()));
_output_.write(_COMMAND_SE);
_output_.flush();
}
}
/* TERMINAL-TYPE option (end)*/
/* open TelnetOptionHandler functionality (start)*/
/**
* Manages subnegotiation for Terminal Type.
*
* @param subn - subnegotiation data to be sent
* @throws IOException - Exception in I/O.
**/
final synchronized void _sendSubnegotiation(int subn[])
throws IOException
{
if (debug)
{
System.err.println("SEND SUBNEGOTIATION: ");
if (subn != null)
{
System.err.println(Arrays.toString(subn));
}
}
if (subn != null)
{
_output_.write(_COMMAND_SB);
// Note _output_ is buffered, so might as well simplify by writing single bytes
for (int element : subn)
{
byte b = (byte) element;
if (b == (byte) TelnetCommand.IAC) { // cast is necessary because IAC is outside the signed byte range
_output_.write(b); // double any IAC bytes
}
_output_.write(b);
}
_output_.write(_COMMAND_SE);
/* Code Section added for sending the negotiation ASAP (start)*/
_output_.flush();
/* Code Section added for sending the negotiation ASAP (end)*/
}
}
/* open TelnetOptionHandler functionality (end)*/
/**
* Sends a command, automatically adds IAC prefix and flushes the output.
*
* @param cmd - command data to be sent
* @throws IOException - Exception in I/O.
* @since 3.0
*/
final synchronized void _sendCommand(byte cmd) throws IOException
{
_output_.write(TelnetCommand.IAC);
_output_.write(cmd);
_output_.flush();
}
/* Code Section added for supporting AYT (start)*/
/***
* Processes the response of an AYT
***/
final synchronized void _processAYTResponse()
{
if (!aytFlag)
{
synchronized (aytMonitor)
{
aytFlag = true;
aytMonitor.notifyAll();
}
}
}
/* Code Section added for supporting AYT (end)*/
/***
* Called upon connection.
*
* @throws IOException - Exception in I/O.
***/
@Override
protected void _connectAction_() throws IOException
{
/* (start). BUGFIX: clean the option info for each connection*/
for (int ii = 0; ii < TelnetOption.MAX_OPTION_VALUE + 1; ii++)
{
_doResponse[ii] = 0;
_willResponse[ii] = 0;
_options[ii] = 0;
if (optionHandlers[ii] != null)
{
optionHandlers[ii].setDo(false);
optionHandlers[ii].setWill(false);
}
}
/* (end). BUGFIX: clean the option info for each connection*/
super._connectAction_();
_input_ = new BufferedInputStream(_input_);
_output_ = new BufferedOutputStream(_output_);
/* open TelnetOptionHandler functionality (start)*/
for (int ii = 0; ii < TelnetOption.MAX_OPTION_VALUE + 1; ii++)
{
if (optionHandlers[ii] != null)
{
if (optionHandlers[ii].getInitLocal())
{
_requestWill(optionHandlers[ii].getOptionCode());
}
if (optionHandlers[ii].getInitRemote())
{
_requestDo(optionHandlers[ii].getOptionCode());
}
}
}
/* open TelnetOptionHandler functionality (end)*/
}
/**
* Sends a DO.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void _sendDo(int option)
throws IOException
{
if (debug || debugoptions)
{
System.err.println("DO: " + TelnetOption.getOption(option));
}
_output_.write(_COMMAND_DO);
_output_.write(option);
/* Code Section added for sending the negotiation ASAP (start)*/
_output_.flush();
/* Code Section added for sending the negotiation ASAP (end)*/
}
/**
* Requests a DO.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void _requestDo(int option)
throws IOException
{
if ((_doResponse[option] == 0 && _stateIsDo(option))
|| _requestedDo(option))
{
return ;
}
_setWantDo(option);
++_doResponse[option];
_sendDo(option);
}
/**
* Sends a DONT.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void _sendDont(int option)
throws IOException
{
if (debug || debugoptions)
{
System.err.println("DONT: " + TelnetOption.getOption(option));
}
_output_.write(_COMMAND_DONT);
_output_.write(option);
/* Code Section added for sending the negotiation ASAP (start)*/
_output_.flush();
/* Code Section added for sending the negotiation ASAP (end)*/
}
/**
* Requests a DONT.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void _requestDont(int option)
throws IOException
{
if ((_doResponse[option] == 0 && _stateIsDont(option))
|| _requestedDont(option))
{
return ;
}
_setWantDont(option);
++_doResponse[option];
_sendDont(option);
}
/**
* Sends a WILL.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void _sendWill(int option)
throws IOException
{
if (debug || debugoptions)
{
System.err.println("WILL: " + TelnetOption.getOption(option));
}
_output_.write(_COMMAND_WILL);
_output_.write(option);
/* Code Section added for sending the negotiation ASAP (start)*/
_output_.flush();
/* Code Section added for sending the negotiation ASAP (end)*/
}
/**
* Requests a WILL.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void _requestWill(int option)
throws IOException
{
if ((_willResponse[option] == 0 && _stateIsWill(option))
|| _requestedWill(option))
{
return ;
}
_setWantWill(option);
++_doResponse[option];
_sendWill(option);
}
/**
* Sends a WONT.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void _sendWont(int option)
throws IOException
{
if (debug || debugoptions)
{
System.err.println("WONT: " + TelnetOption.getOption(option));
}
_output_.write(_COMMAND_WONT);
_output_.write(option);
/* Code Section added for sending the negotiation ASAP (start)*/
_output_.flush();
/* Code Section added for sending the negotiation ASAP (end)*/
}
/**
* Requests a WONT.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void _requestWont(int option)
throws IOException
{
if ((_willResponse[option] == 0 && _stateIsWont(option))
|| _requestedWont(option))
{
return ;
}
_setWantWont(option);
++_doResponse[option];
_sendWont(option);
}
/**
* Sends a byte.
*
* @param b - byte to send
* @throws IOException - Exception in I/O.
**/
final synchronized void _sendByte(int b)
throws IOException
{
_output_.write(b);
/* Code Section added for supporting spystreams (start)*/
_spyWrite(b);
/* Code Section added for supporting spystreams (end)*/
}
/* Code Section added for supporting AYT (start)*/
/**
* Sends an Are You There sequence and waits for the result.
*
* @param timeout - Time to wait for a response (millis.)
* @throws IOException - Exception in I/O.
* @throws IllegalArgumentException - Illegal argument
* @throws InterruptedException - Interrupted during wait.
* @return true if AYT received a response, false otherwise
**/
final boolean _sendAYT(long timeout)
throws IOException, IllegalArgumentException, InterruptedException
{
boolean retValue = false;
synchronized (aytMonitor)
{
synchronized (this)
{
aytFlag = false;
_output_.write(_COMMAND_AYT);
_output_.flush();
}
aytMonitor.wait(timeout);
if (!aytFlag)
{
retValue = false;
aytFlag = true;
}
else
{
retValue = true;
}
}
return (retValue);
}
/* Code Section added for supporting AYT (end)*/
/* open TelnetOptionHandler functionality (start)*/
/**
* Registers a new TelnetOptionHandler for this telnet to use.
*
* @param opthand - option handler to be registered.
* @throws InvalidTelnetOptionException - The option code is invalid.
* @throws IOException on error
**/
void addOptionHandler(TelnetOptionHandler opthand)
throws InvalidTelnetOptionException, IOException
{
int optcode = opthand.getOptionCode();
if (TelnetOption.isValidOption(optcode))
{
if (optionHandlers[optcode] == null)
{
optionHandlers[optcode] = opthand;
if (isConnected())
{
if (opthand.getInitLocal())
{
_requestWill(optcode);
}
if (opthand.getInitRemote())
{
_requestDo(optcode);
}
}
}
else
{
throw (new InvalidTelnetOptionException(
"Already registered option", optcode));
}
}
else
{
throw (new InvalidTelnetOptionException(
"Invalid Option Code", optcode));
}
}
/**
* Unregisters a TelnetOptionHandler.
*
* @param optcode - Code of the option to be unregistered.
* @throws InvalidTelnetOptionException - The option code is invalid.
* @throws IOException on error
**/
void deleteOptionHandler(int optcode)
throws InvalidTelnetOptionException, IOException
{
if (TelnetOption.isValidOption(optcode))
{
if (optionHandlers[optcode] == null)
{
throw (new InvalidTelnetOptionException(
"Unregistered option", optcode));
}
else
{
TelnetOptionHandler opthand = optionHandlers[optcode];
optionHandlers[optcode] = null;
if (opthand.getWill())
{
_requestWont(optcode);
}
if (opthand.getDo())
{
_requestDont(optcode);
}
}
}
else
{
throw (new InvalidTelnetOptionException(
"Invalid Option Code", optcode));
}
}
/* open TelnetOptionHandler functionality (end)*/
/* Code Section added for supporting spystreams (start)*/
/***
* Registers an OutputStream for spying what's going on in
* the Telnet session.
*
* @param spystream - OutputStream on which session activity
* will be echoed.
***/
void _registerSpyStream(OutputStream spystream)
{
spyStream = spystream;
}
/***
* Stops spying this Telnet.
*
***/
void _stopSpyStream()
{
spyStream = null;
}
/***
* Sends a read char on the spy stream.
*
* @param ch - character read from the session
***/
void _spyRead(int ch)
{
OutputStream spy = spyStream;
if (spy != null)
{
try
{
if (ch != '\r') // never write '\r' on its own
{
if (ch == '\n')
{
spy.write('\r'); // add '\r' before '\n'
}
spy.write(ch); // write original character
spy.flush();
}
}
catch (IOException e)
{
spyStream = null;
}
}
}
/***
* Sends a written char on the spy stream.
*
* @param ch - character written to the session
***/
void _spyWrite(int ch)
{
if (!(_stateIsDo(TelnetOption.ECHO)
&& _requestedDo(TelnetOption.ECHO)))
{
OutputStream spy = spyStream;
if (spy != null)
{
try
{
spy.write(ch);
spy.flush();
}
catch (IOException e)
{
spyStream = null;
}
}
}
}
/* Code Section added for supporting spystreams (end)*/
/***
* Registers a notification handler to which will be sent
* notifications of received telnet option negotiation commands.
*
* @param notifhand - TelnetNotificationHandler to be registered
***/
public void registerNotifHandler(TelnetNotificationHandler notifhand)
{
__notifhand = notifhand;
}
/***
* Unregisters the current notification handler.
*
***/
public void unregisterNotifHandler()
{
__notifhand = null;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment