1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.net;
19
20import java.nio.ByteOrder;
21import java.nio.charset.StandardCharsets;
22import libcore.io.Memory;
23
24class Socks4Message {
25    static final int COMMAND_CONNECT = 1;
26
27    static final int COMMAND_BIND = 2;
28
29    static final int RETURN_SUCCESS = 90;
30
31    static final int RETURN_FAILURE = 91;
32
33    static final int RETURN_CANNOT_CONNECT_TO_IDENTD = 92;
34
35    static final int RETURN_DIFFERENT_USER_IDS = 93;
36
37    static final int REPLY_LENGTH = 8;
38
39    static final int INDEX_VERSION = 0;
40
41    private static final int SOCKS_VERSION = 4;
42
43    private static final int INDEX_COMMAND = 1;
44
45    private static final int INDEX_PORT = 2;
46
47    private static final int INDEX_IP = 4;
48
49    private static final int INDEX_USER_ID = 8;
50
51    private static final int BUFFER_LENGTH = 256;
52
53    private static final int MAX_USER_ID_LENGTH = BUFFER_LENGTH - INDEX_USER_ID;
54
55    protected byte[] buffer;
56
57    public Socks4Message() {
58        buffer = new byte[BUFFER_LENGTH];
59        setVersionNumber(SOCKS_VERSION);
60    }
61
62    /**
63     * Get the request's command or result.
64     */
65    public int getCommandOrResult() {
66        return buffer[INDEX_COMMAND];
67    }
68
69    /**
70     * Set the request's command or result.
71     */
72    public void setCommandOrResult(int command) {
73        buffer[INDEX_COMMAND] = (byte) command;
74    }
75
76    /**
77     * Returns the request's port number.
78     */
79    public int getPort() {
80        return Memory.peekShort(buffer, INDEX_PORT, ByteOrder.BIG_ENDIAN);
81    }
82
83    /**
84     * Set the request's port number.
85     */
86    public void setPort(int port) {
87        Memory.pokeShort(buffer, INDEX_PORT, (short) port, ByteOrder.BIG_ENDIAN);
88    }
89
90    /**
91     * Returns the IP address of the request as an integer.
92     */
93    public int getIP() {
94        return Memory.peekInt(buffer, INDEX_IP, ByteOrder.BIG_ENDIAN);
95    }
96
97    /**
98     * Set the IP address. This expects an array of four bytes in host order.
99     */
100    public void setIP(byte[] ip) {
101        buffer[INDEX_IP] = ip[0];
102        buffer[INDEX_IP + 1] = ip[1];
103        buffer[INDEX_IP + 2] = ip[2];
104        buffer[INDEX_IP + 3] = ip[3];
105    }
106
107    /**
108     * Returns the user id for authentication.
109     */
110    public String getUserId() {
111        return getString(INDEX_USER_ID, MAX_USER_ID_LENGTH);
112    }
113
114    /**
115     * Set the user id for authentication.
116     */
117    public void setUserId(String id) {
118        setString(INDEX_USER_ID, MAX_USER_ID_LENGTH, id);
119    }
120
121    @Override
122    public String toString() {
123        StringBuilder buf = new StringBuilder(50);
124        buf.append("Version: ");
125        buf.append(Integer.toHexString(getVersionNumber()));
126        buf.append(" Command: ");
127        buf.append(Integer.toHexString(getCommandOrResult()));
128        buf.append(" Port: ");
129        buf.append(getPort());
130        buf.append(" IP: ");
131        buf.append(Integer.toHexString(getIP()));
132        buf.append(" User ID: ");
133        buf.append(getUserId());
134        return buf.toString();
135    }
136
137    /**
138     * Returns the total number of bytes used for the request. This method
139     * searches for the end of the user id, then searches for the end of the
140     * password and returns the final index as the requests length.
141     */
142    public int getLength() {
143        int index = 0;
144
145        // Look for the end of the user id.
146        for (index = INDEX_USER_ID; buffer[index] != 0; index++) {
147            /*
148             * Finds the end of the user id by searching for the null
149             * termination of the user id string.
150             */
151        }
152
153        // Increment the index to include the NULL character in the length;
154        index++;
155        return index;
156    }
157
158    /**
159     * Returns an error string corresponding to the given error value.
160     */
161    public String getErrorString(int error) {
162        switch (error) {
163        case RETURN_FAILURE:
164            return "Failure to connect to SOCKS server";
165        case RETURN_CANNOT_CONNECT_TO_IDENTD:
166            return "Unable to connect to identd to verify user";
167        case RETURN_DIFFERENT_USER_IDS:
168            return "Failure - user ids do not match";
169        default:
170            return "Success";
171        }
172    }
173
174    /**
175     * Returns the message's byte buffer.
176     */
177    public byte[] getBytes() {
178        return buffer;
179    }
180
181    /**
182     * Get a String from the buffer at the offset given. The method reads until
183     * it encounters a null value or reaches the maxLength given.
184     */
185    private String getString(int offset, int maxLength) {
186        int index = offset;
187        int lastIndex = index + maxLength;
188        while (index < lastIndex && (buffer[index] != 0)) {
189            index++;
190        }
191        return new String(buffer, offset, index - offset, StandardCharsets.ISO_8859_1);
192    }
193
194    /**
195     * Returns the SOCKS version number. Should always be 4.
196     */
197    private int getVersionNumber() {
198        return buffer[INDEX_VERSION];
199    }
200
201    /**
202     * Put a string into the buffer at the offset given.
203     */
204    private void setString(int offset, int maxLength, String theString) {
205        byte[] stringBytes = theString.getBytes(StandardCharsets.ISO_8859_1);
206        int length = Math.min(stringBytes.length, maxLength);
207        System.arraycopy(stringBytes, 0, buffer, offset, length);
208        buffer[offset + length] = 0;
209    }
210
211    /**
212     * Set the SOCKS version number. This should always be 4.
213     */
214    private void setVersionNumber(int number) {
215        buffer[INDEX_VERSION] = (byte) number;
216    }
217}
218