/* * Copyright (C) 2005-2012 NAUMEN. All rights reserved. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 2 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * */ package ru.naumen.servacc.telnet; import com.mindbright.ssh2.SSH2SessionChannel; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PushbackInputStream; import java.net.Socket; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ConsoleManager { private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleManager.class); /* * Telnet protocol commands * Refer to RFC 854 [1] * [1]: https://tools.ietf.org/html/rfc854 */ // Request to use option public static final int DO = 253; // Don't use option public static final int DONT = 254; // End Of File public static final int EOF = 236; // Interpret As Command public static final int IAC = 255; // Start sub-negotiation public static final int SB = 250; // Agree to use option public static final int WILL = 251; // Refuse to use option public static final int WONT = 252; /* * Telnet protocol options */ // Echo public static final int O_ECHO = 1; // Suppress Go Ahead public static final int O_SUPPRESS_GA = 3; // Window Size Negotiation public static final int O_WINDOW_SIZE_NEG = 31; class ConsoleManagerInputStream extends PushbackInputStream { private ConsoleManager manager; public ConsoleManagerInputStream(ConsoleManager manager) throws IOException { super(new LFInputStream(manager.client.getInputStream()), 1024); this.manager = manager; } private void processDO() throws IOException { int option = super.read(); switch (option) { case O_ECHO: say(IAC, WILL, O_ECHO); break; case O_SUPPRESS_GA: say(IAC, WILL, O_SUPPRESS_GA); break; default: say(IAC, WONT, option); LOGGER.warn("IAC DO UNKNOWN(" + option + ")"); break; } } private void processIAC() throws IOException { int command = super.read(); switch (command) { case WILL: processWILL(); break; case DO: processDO(); break; case SB: processSB(); break; default: LOGGER.warn("IAC UNKNOWN(" + command + ")"); break; } } private void processSB() throws IOException { int command = super.read(); int val = super.read(); List<Integer> lst = new ArrayList<>(); while (val != IAC) { lst.add(val); val = super.read(); } super.read(); Integer[] arr = lst.toArray(new Integer[lst.size()]); switch (command) { case O_WINDOW_SIZE_NEG: session.sendWindowChange( arr[2] * 256 + arr[3], arr[0] * 256 + arr[1]); break; default: LOGGER.warn("IAC SB UNKNOWN(" + command + ")"); break; } } private void processWILL() throws IOException { int command = super.read(); switch (command) { case O_SUPPRESS_GA: say(IAC, DO, O_SUPPRESS_GA); break; case O_WINDOW_SIZE_NEG: say(IAC, DO, O_WINDOW_SIZE_NEG); break; default: say(IAC, DONT, command); LOGGER.warn("IAC WILL UNKNOWN(" + command + ")"); break; } } public int read() throws IOException { int val = super.read(); while (val == IAC || val == 17) { switch (val) { case IAC: processIAC(); break; case 17: unread((getPassword() + "\n").getBytes()); break; } val = super.read(); } return val; } public int read(byte[] b, int off, int len) throws IOException { if (manager.inSudoLogin) { return 0; } while (true) { int res; if (available() != 0) { res = super.read(b, off, available()); } else { res = super.read(b, off, len); } if (res == -1) { return -1; } int ptr = 0; while (b[off + ptr] != (byte) IAC && b[off + ptr] != 17 && ptr < res) { ptr++; } if (ptr == res) { return res; } if (ptr != 0) { unread(b, off + ptr, res - ptr); return ptr; } unread(b, off, res); int nextb = read(); if (nextb == -1) { return -1; } unread(nextb); } } } class ConsoleManagerOutputStream extends FilterOutputStream { private ConsoleManager manager; public ConsoleManagerOutputStream(ConsoleManager manager) throws IOException { super(manager.client.getOutputStream()); this.manager = manager; } public void write(byte[] b, int off, int len) throws IOException { if ((off | len | (b.length - (len + off)) | (off + len)) < 0) { throw new IndexOutOfBoundsException(); } out.write(b, off, len); //TODO: auto password enter if (manager.inSudoLogin && b[0] != 27 && b[1] != 97) { manager.in.unread("sudo su -\n".getBytes()); manager.inSudoLogin = false; } } public void write(int b) throws IOException { out.write(b); } } private Socket client; private ConsoleManagerInputStream in = null; private ConsoleManagerOutputStream out = null; private SSH2SessionChannel session; private String password; private boolean inSudoLogin; public ConsoleManager(Socket client, SSH2SessionChannel session, String password, boolean sudoLogin) { this.client = client; this.session = session; this.password = password; this.inSudoLogin = sudoLogin; } public InputStream getInputStream() throws IOException { if (in == null) { in = new ConsoleManagerInputStream(this); } return in; } public OutputStream getOutputStream() throws IOException { if (out == null) { out = new ConsoleManagerOutputStream(this); } return out; } private void say(int a, int b, int c) throws IOException { getOutputStream().write(new byte[]{ (byte) a, (byte) b, (byte) c }); } public void negotiateProtocolOptions() throws IOException { say(IAC, DO, O_WINDOW_SIZE_NEG); say(IAC, WILL, O_ECHO); say(IAC, WILL, O_SUPPRESS_GA); } private String getPassword() { return password; } }