summaryrefslogtreecommitdiffstats
path: root/src/main/java/de/bwlehrpool/bwlp_guac/VncConnection.java
blob: d1ac1ad548e28dcafdc97b7193d200b888b7dd25 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package de.bwlehrpool.bwlp_guac;

import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class VncConnection implements Closeable {

	private static final Logger LOGGER = LoggerFactory.getLogger(VncConnection.class);

	private final Socket sock;
	private final DataOutputStream out;
	private final DataInputStream in;

	public VncConnection(String host, int port) throws IOException {
		sock = new Socket();
		sock.connect(new InetSocketAddress(host, port), 1200);
		sock.setSoTimeout(1000);
		out = new DataOutputStream(sock.getOutputStream());
		in = new DataInputStream(sock.getInputStream());
	}

	/**
	 * @return Version string on success, null if not RFB
	 * @throws IOException
	 */
	public String handshake() throws IOException {
		byte[] buffer = new byte[12];
		in.readFully(buffer);
		if (buffer[0] != 'R' || buffer[1] != 'F' || buffer[2] != 'B')
			return null;
		out.write("RFB 003.008\n".getBytes());
		out.flush();
		return new String(buffer).substring(4, 11);
	}

	public boolean tryLogin(String passwd) throws IOException {
		if (passwd == null)
			return false; // Paswordless not supported yet (although simpler..)
		int numTypes = in.read();
		if (numTypes == 0) {
			LOGGER.info("VNC Server @ " + sock.getRemoteSocketAddress() + " does not support any auth methods");
			printError();
			return false;
		}
		boolean ok = false;
		for (int i = 0; i < numTypes; ++i) {
			if (in.read() == 2) {
				ok = true;
				break; // Found "VNC Authentication"
			}
		}
		if (!ok) {
			LOGGER.info("VNC Server @ " + sock.getRemoteSocketAddress() + " does not support VNC Authentication");
			return false;
		}
		out.write(2); // Pick passwd auth
		// Get challenge data
		byte[] challenge = new byte[16];
		int ret = in.read(challenge);
		if (ret != 16) {
			LOGGER.info("Didn't receive challenge from VNC server @ " + sock.getRemoteSocketAddress());
			return false;
		}
		// pad pw to 8 bytes
		byte[] pw_bytes = passwd.getBytes();
		pw_bytes = Arrays.copyOf(pw_bytes, 8);
		// Encrypt
		out.write(WeakCrypto.vncEncrypt(pw_bytes, challenge));
		// check reply
		int securityReply = in.readInt();
		if (securityReply != 0) {
			LOGGER.info("Security reply = " + securityReply + " for VNC server @ " + sock.getRemoteSocketAddress());
			return false;
		}
		return true;
	}

	private void printError() {
		try {
			int len = in.readInt();
			byte[] msg = new byte[len];
			in.readFully(msg);
			LOGGER.info(new String(msg, StandardCharsets.ISO_8859_1));
		} catch (IOException e) {
			// Nothing, we're already kinda handling an error, so if we can't fetch the
			// message, ignore
		}
	}

	@Override
	public void close() throws IOException {
		try {
			in.close();
		} catch (Exception e) {
		}
		try {
			out.close();
		} catch (Exception e) {
		}
		try {
			sock.close();
		} catch (Exception e) {
		}
	}

}