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 18 package java.net; 19 20 import java.nio.ByteOrder; 21 import java.nio.charset.StandardCharsets; 22 import libcore.io.Memory; 23 24 class 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