Basically, I need to make a kind of chat. I have set up the JSch library and using their examples I'm able to successfully open an SSH (-R) tunnel between the client(Win10-local) and server(Rpi-remote). I can send commands to shell in rpi, pass files, create new files, write to them etc... But I need to be able to "catch" and read the data on server side right from the socket, let's say some string sent from the client side. Then I have to change this string on the server side and resend it back to the client, where it will be printed out.
I tried to create a socket(server) and bind to the port on which the SSH communication goes, but I get an error that socket can't be bound to this port because it is already used. If I first bind the socket, then the SSH tunnel won't pass.
I also tried the daemon example in jsch examples: http://www.jcraft.com/jsch/examples/Daemon.java.html but without any success.
Right now I'm trying to use this code on the server side:
public class ServerSide {
private final static Random random = new Random();
private final static String[] ADVICE_LIST = {
"Take smaller bites",
"Go for the tight jeans. No they do NOT make you look fat.",
"One word: inappropriate",
};
private void go() throws IOException {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress("localhost",12005));
while(!serverSocket.isClosed()) {
Socket socket = serverSocket.accept();
PrintWriter writer = new PrintWriter(socket.getOutputStream());
System.out.println(socket.getOutputStream());
String advice = getAdvice();
System.out.println("Sending advice: " + advice);
writer.write(advice);
writer.close();
System.out.println("Advice sent!");
socket.close();
}
}
private static String getAdvice() {
return ADVICE_LIST[random.nextInt() % ADVICE_LIST.length];
}
public static void main(String[] args) throws IOException {
ServerSide server = new ServerSide();
server.go();
}
}
}
The server listens on port 12005 and if it detects any client it just sends a random message to him. When I tried it yesterday, with server listening on, the tunnel could not be established, but when I tried it yet, the tunnel is established but the server doesn't detect anything at all. The code on client is:
public class PortForwardingR{
public static void main(String[] arg){
int rport;
String lhost;
int lport;
try{
JSch jsch=new JSch();
String host=null;
if(arg.length>0){
host=arg[0];
}
else{
host=JOptionPane.showInputDialog("Enter username@hostname",
System.getProperty("user.name")+
"@localhost");
}
String user=host.substring(0, host.indexOf('@'));
host=host.substring(host.indexOf('@')+1);
Session session=jsch.getSession(user, host, 22);
String foo=JOptionPane.showInputDialog("Enter -R port:host:hostport",
"port:host:hostport");
rport=Integer.parseInt(foo.substring(0, foo.indexOf(':')));
foo=foo.substring(foo.indexOf(':')+1);
lhost=foo.substring(0, foo.indexOf(':'));
lport=Integer.parseInt(foo.substring(foo.indexOf(':')+1));
// username and password will be given via UserInfo interface.
UserInfo ui=new MyUserInfo();
session.setUserInfo(ui);
session.connect();
// Channel channel=session.openChannel("shell");
// channel.connect();
session.setPortForwardingR(rport, lhost, lport);
System.out.println(host+":"+rport+" -> "+lhost+":"+lport);
}
catch(Exception e){
System.out.println(e);
}
}
public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{
public String getPassword(){ return passwd; }
public boolean promptYesNo(String str){
Object[] options={ "yes", "no" };
int foo=JOptionPane.showOptionDialog(null,
str,
"Warning",
JOptionPane.DEFAULT_OPTION,
JOptionPane.WARNING_MESSAGE,
null, options, options[0]);
return foo==0;
}
String passwd;
JTextField passwordField=(JTextField)new JPasswordField(20);
public String getPassphrase(){ return null; }
public boolean promptPassphrase(String message){ return true; }
public boolean promptPassword(String message){
Object[] ob={passwordField};
int result=
JOptionPane.showConfirmDialog(null, ob, message,
JOptionPane.OK_CANCEL_OPTION);
if(result==JOptionPane.OK_OPTION){
passwd=passwordField.getText();
return true;
}
else{ return false; }
}
public void showMessage(String message){
JOptionPane.showMessageDialog(null, message);
}
final GridBagConstraints gbc =
new GridBagConstraints(0,0,1,1,1,1,
GridBagConstraints.NORTHWEST,
GridBagConstraints.NONE,
new Insets(0,0,0,0),0,0);
private Container panel;
public String[] promptKeyboardInteractive(String destination,
String name,
String instruction,
String[] prompt,
boolean[] echo){
panel = new JPanel();
panel.setLayout(new GridBagLayout());
gbc.weightx = 1.0;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.gridx = 0;
panel.add(new JLabel(instruction), gbc);
gbc.gridy++;
gbc.gridwidth = GridBagConstraints.RELATIVE;
JTextField[] texts=new JTextField[prompt.length];
for(int i=0; i<prompt.length; i++){
gbc.fill = GridBagConstraints.NONE;
gbc.gridx = 0;
gbc.weightx = 1;
panel.add(new JLabel(prompt[i]),gbc);
gbc.gridx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weighty = 1;
if(echo[i]){
texts[i]=new JTextField(20);
}
else{
texts[i]=new JPasswordField(20);
}
panel.add(texts[i], gbc);
gbc.gridy++;
}
if(JOptionPane.showConfirmDialog(null, panel,
destination+": "+name,
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE)
==JOptionPane.OK_OPTION){
String[] response=new String[prompt.length];
for(int i=0; i<prompt.length; i++){
response[i]=texts[i].getText();
}
return response;
}
else{
return null; // cancel
}
}
}
}
When I'm prompt to "Enter username@hostname" I enter RPI's_username@It's_publicIP then in "Enter -R port:host:hostport" I'm entering: 12005:localhost:12005. Am I doing it right? The connection successfully establishes so I believe it's ok.
Also, I found this code here (tried it and it worked), which is very close to what I want, but it works with shell, I want to communicate directly with my code on the server side.
public class TestShell {
public static void main(String[] arg) {
try {
JSch jsch = new JSch();
String host = null;
final Session session = jsch.getSession("user", "remotecomputer", 22);
session.setPassword("fluffybunnyslippers");
session.setConfig("StrictHostKeyChecking", "no");
session.connect(30000); // making a connection with timeout.
final Channel channel = session.openChannel("shell");
PipedInputStream pis = new PipedInputStream();
final PipedOutputStream pos = new PipedOutputStream(pis);
channel.setInputStream(pis);
channel.setOutputStream(new OutputStream() {
private int cmdIndx = 0;
private String[] cmds = {
"ls\n",
"cd ..\n",
"ls\n",
"exit\n"
};
private String line = "";
@Override
public void write(int b) throws IOException {
char c = (char) b;
if (c == '\n') {
logout(line);
System.out.print(line);
line = "";
} else {
line += c;
logout(line);
if (line.endsWith("$ ")) {
String cmd = cmds[cmdIndx];
cmdIndx++;
pos.write(cmd.getBytes());
}
}
}
public void logout(String line) {
if (line.startsWith("logout")) {
System.out.println("...logout...");
channel.disconnect();
session.disconnect();
System.exit(0);
}
}
});
channel.connect(3 * 1000);
} catch (Exception e) {
System.out.println(e);
}
}
}
The Rpi(server) is on a router, which has a public IP. The router has set port forwarding on port 12005, which sends the data to the local IP (and to port 12005) where my RPI is. The router also has port 12005 exposed as public, probably not necessary and I will close it if I achieve what I'm trying to. The client is simply on wifi from a mobile hotspot.
Can someone give me a bit of advice on how to use an existing, working, SSH tunnel to read/write communication directly from sockets? I'm a very beginner in communication through the internet.