File size: 5,604 Bytes
d46f4a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package net.minecraft.server.rcon.thread;

import com.mojang.logging.LogUtils;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import net.minecraft.server.ServerInterface;
import net.minecraft.server.rcon.PktUtils;
import org.slf4j.Logger;

public class RconClient extends GenericThread {
    private static final Logger LOGGER = LogUtils.getLogger();
    private static final int SERVERDATA_AUTH = 3;
    private static final int SERVERDATA_EXECCOMMAND = 2;
    private static final int SERVERDATA_RESPONSE_VALUE = 0;
    private static final int SERVERDATA_AUTH_RESPONSE = 2;
    private static final int SERVERDATA_AUTH_FAILURE = -1;
    private boolean authed;
    private final Socket client;
    private final byte[] buf = new byte[1460];
    private final String rconPassword;
    private final ServerInterface serverInterface;

    RconClient(ServerInterface p_11587_, String p_11588_, Socket p_11589_) {
        super("RCON Client " + p_11589_.getInetAddress());
        this.serverInterface = p_11587_;
        this.client = p_11589_;

        try {
            this.client.setSoTimeout(0);
        } catch (Exception exception) {
            this.running = false;
        }

        this.rconPassword = p_11588_;
    }

    @Override
    public void run() {
        try {
            try {
                while (this.running) {
                    BufferedInputStream bufferedinputstream = new BufferedInputStream(this.client.getInputStream());
                    int i = bufferedinputstream.read(this.buf, 0, 1460);
                    if (10 > i) {
                        return;
                    }

                    int j = 0;
                    int k = PktUtils.intFromByteArray(this.buf, 0, i);
                    if (k != i - 4) {
                        return;
                    }

                    j += 4;
                    int l = PktUtils.intFromByteArray(this.buf, j, i);
                    j += 4;
                    int i1 = PktUtils.intFromByteArray(this.buf, j);
                    j += 4;
                    switch (i1) {
                        case 2:
                            if (this.authed) {
                                String s1 = PktUtils.stringFromByteArray(this.buf, j, i);

                                try {
                                    this.sendCmdResponse(l, this.serverInterface.runCommand(s1));
                                } catch (Exception exception) {
                                    this.sendCmdResponse(l, "Error executing: " + s1 + " (" + exception.getMessage() + ")");
                                }
                                break;
                            }

                            this.sendAuthFailure();
                            break;
                        case 3:
                            String s = PktUtils.stringFromByteArray(this.buf, j, i);
                            j += s.length();
                            if (!s.isEmpty() && s.equals(this.rconPassword)) {
                                this.authed = true;
                                this.send(l, 2, "");
                                break;
                            }

                            this.authed = false;
                            this.sendAuthFailure();
                            break;
                        default:
                            this.sendCmdResponse(l, String.format(Locale.ROOT, "Unknown request %s", Integer.toHexString(i1)));
                    }
                }

                return;
            } catch (IOException ioexception) {
            } catch (Exception exception1) {
                LOGGER.error("Exception whilst parsing RCON input", (Throwable)exception1);
            }
        } finally {
            this.closeSocket();
            LOGGER.info("Thread {} shutting down", this.name);
            this.running = false;
        }
    }

    private void send(int p_11591_, int p_11592_, String p_11593_) throws IOException {
        ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream(1248);
        DataOutputStream dataoutputstream = new DataOutputStream(bytearrayoutputstream);
        byte[] abyte = p_11593_.getBytes(StandardCharsets.UTF_8);
        dataoutputstream.writeInt(Integer.reverseBytes(abyte.length + 10));
        dataoutputstream.writeInt(Integer.reverseBytes(p_11591_));
        dataoutputstream.writeInt(Integer.reverseBytes(p_11592_));
        dataoutputstream.write(abyte);
        dataoutputstream.write(0);
        dataoutputstream.write(0);
        this.client.getOutputStream().write(bytearrayoutputstream.toByteArray());
    }

    private void sendAuthFailure() throws IOException {
        this.send(-1, 2, "");
    }

    private void sendCmdResponse(int p_11595_, String p_11596_) throws IOException {
        int i = p_11596_.length();

        do {
            int j = 4096 <= i ? 4096 : i;
            this.send(p_11595_, 0, p_11596_.substring(0, j));
            p_11596_ = p_11596_.substring(j);
            i = p_11596_.length();
        } while (0 != i);
    }

    @Override
    public void stop() {
        this.running = false;
        this.closeSocket();
        super.stop();
    }

    private void closeSocket() {
        try {
            this.client.close();
        } catch (IOException ioexception) {
            LOGGER.warn("Failed to close socket", (Throwable)ioexception);
        }
    }
}