1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/** 2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * $RCSfile$ 3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * $Revision$ 4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * $Date$ 5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * 6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * you may not use this file except in compliance with the License. 88a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com * You may obtain a copy of the License at 98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com * 108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com * http://www.apache.org/licenses/LICENSE-2.0 114991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com * 12c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com * Unless required by applicable law or agreed to in writing, software 138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com * distributed under the License is distributed on an "AS IS" BASIS, 144991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 154991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com * See the License for the specific language governing permissions and 164991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com * limitations under the License. 178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com */ 188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.compackage org.jivesoftware.smack.proxy; 19038aff623d9fd47946cd31685f74cf473f7c84f0senorblanco@chromium.org 20a728e35edcffd99216e3965a4b908ad0df7f69c2vandebo@chromium.orgimport java.io.IOException; 214e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.comimport java.io.InputStream; 22038aff623d9fd47946cd31685f74cf473f7c84f0senorblanco@chromium.orgimport java.io.OutputStream; 234e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.comimport java.net.InetAddress; 2482065d667f64e232bcde2ad849756a6096fcbe6freed@google.comimport java.net.Socket; 2582065d667f64e232bcde2ad849756a6096fcbe6freed@google.comimport java.net.UnknownHostException; 26038aff623d9fd47946cd31685f74cf473f7c84f0senorblanco@chromium.orgimport javax.net.SocketFactory; 27038aff623d9fd47946cd31685f74cf473f7c84f0senorblanco@chromium.org 284868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org/** 29fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com * Socket factory for Socks5 proxy 304868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org * 314868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org * @author Atul Aggarwal 328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com */ 33fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.compublic class Socks5ProxySocketFactory 34038aff623d9fd47946cd31685f74cf473f7c84f0senorblanco@chromium.org extends SocketFactory 354e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com{ 368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com private ProxyInfo proxy; 374868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org 384868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org public Socks5ProxySocketFactory(ProxyInfo proxy) 394e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com { 40a6398911174d5445456ecb2f5f4f0565db2f100bjunov@google.com this.proxy = proxy; 41a6398911174d5445456ecb2f5f4f0565db2f100bjunov@google.com } 42a6398911174d5445456ecb2f5f4f0565db2f100bjunov@google.com 434868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org public Socket createSocket(String host, int port) 444e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com throws IOException, UnknownHostException 454e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com { 464e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com return socks5ProxifiedSocket(host,port); 474868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org } 484868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org 498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com public Socket createSocket(String host ,int port, InetAddress localHost, 508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int localPort) 516bac947cd5bc460dd9166ada6310d678fd2e39f8reed@google.com throws IOException, UnknownHostException 526bac947cd5bc460dd9166ada6310d678fd2e39f8reed@google.com { 536bac947cd5bc460dd9166ada6310d678fd2e39f8reed@google.com 548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return socks5ProxifiedSocket(host,port); 558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 56c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com } 57c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com 58c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com public Socket createSocket(InetAddress host, int port) 59c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com throws IOException 608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com { 618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 624e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com return socks5ProxifiedSocket(host.getHostAddress(),port); 6382065d667f64e232bcde2ad849756a6096fcbe6freed@google.com 644868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org } 658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com public Socket createSocket( InetAddress address, int port, 6754924243c1b65b3ee6d8fa064b50a9b1bb2a19a5djsollen@google.com InetAddress localAddress, int localPort) 6854924243c1b65b3ee6d8fa064b50a9b1bb2a19a5djsollen@google.com throws IOException 698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com { 708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 71c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com return socks5ProxifiedSocket(address.getHostAddress(),port); 728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 734868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org } 74c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com 758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com private Socket socks5ProxifiedSocket(String host, int port) 768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com throws IOException 774e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com { 784e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com Socket socket = null; 798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com InputStream in = null; 808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com OutputStream out = null; 814e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com String proxy_host = proxy.getProxyAddress(); 828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int proxy_port = proxy.getProxyPort(); 834e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com String user = proxy.getProxyUsername(); 844e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com String passwd = proxy.getProxyPassword(); 854e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com 864e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com try 874e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com { 884e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com socket=new Socket(proxy_host, proxy_port); 8956c69773aea56c6c6bd47bc7e7970dd081205184djsollen@google.com in=socket.getInputStream(); 90f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com out=socket.getOutputStream(); 91f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com 92f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com socket.setTcpNoDelay(true); 93f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com 94f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com byte[] buf=new byte[1024]; 95f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com int index=0; 96f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com 974e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com/* 98f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com +----+----------+----------+ 994e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com |VER | NMETHODS | METHODS | 1004e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com +----+----------+----------+ 1014e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com | 1 | 1 | 1 to 255 | 1024e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com +----+----------+----------+ 1034e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com 1044e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com The VER field is set to X'05' for this version of the protocol. The 1054e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com NMETHODS field contains the number of method identifier octets that 1064e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com appear in the METHODS field. 1074e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com 1084e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com The values currently defined for METHOD are: 1094e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com 1104e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com o X'00' NO AUTHENTICATION REQUIRED 1114e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com o X'01' GSSAPI 1124e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com o X'02' USERNAME/PASSWORD 1134e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com o X'03' to X'7F' IANA ASSIGNED 1144e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com o X'80' to X'FE' RESERVED FOR PRIVATE METHODS 1154e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com o X'FF' NO ACCEPTABLE METHODS 1164e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com*/ 1174e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com 1188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com buf[index++]=5; 1198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1204991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com buf[index++]=2; 1214991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com buf[index++]=0; // NO AUTHENTICATION REQUIRED 1224991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com buf[index++]=2; // USERNAME/PASSWORD 1234991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com 1244991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com out.write(buf, 0, index); 1254991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com 1264991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com/* 1274991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com The server selects from one of the methods given in METHODS, and 1284991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com sends a METHOD selection message: 1294991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com 1304991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com +----+--------+ 1314991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com |VER | METHOD | 1324991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com +----+--------+ 1334991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com | 1 | 1 | 1344991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com +----+--------+ 1354991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com*/ 1364991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com //in.read(buf, 0, 2); 1374991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com fill(in, buf, 2); 1384991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com 1394991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com boolean check=false; 1404991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com switch((buf[1])&0xff) 1414991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com { 1424991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com case 0: // NO AUTHENTICATION REQUIRED 1434991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com check=true; 1444991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com break; 1454991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com case 2: // USERNAME/PASSWORD 1464991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com if(user==null || passwd==null) 1474991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com { 1484991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com break; 1494991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com } 1504991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com 1514991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com/* 152 Once the SOCKS V5 server has started, and the client has selected the 153 Username/Password Authentication protocol, the Username/Password 154 subnegotiation begins. This begins with the client producing a 155 Username/Password request: 156 157 +----+------+----------+------+----------+ 158 |VER | ULEN | UNAME | PLEN | PASSWD | 159 +----+------+----------+------+----------+ 160 | 1 | 1 | 1 to 255 | 1 | 1 to 255 | 161 +----+------+----------+------+----------+ 162 163 The VER field contains the current version of the subnegotiation, 164 which is X'01'. The ULEN field contains the length of the UNAME field 165 that follows. The UNAME field contains the username as known to the 166 source operating system. The PLEN field contains the length of the 167 PASSWD field that follows. The PASSWD field contains the password 168 association with the given UNAME. 169*/ 170 index=0; 171 buf[index++]=1; 172 buf[index++]=(byte)(user.length()); 173 System.arraycopy(user.getBytes(), 0, buf, index, 174 user.length()); 175 index+=user.length(); 176 buf[index++]=(byte)(passwd.length()); 177 System.arraycopy(passwd.getBytes(), 0, buf, index, 178 passwd.length()); 179 index+=passwd.length(); 180 181 out.write(buf, 0, index); 182 183/* 184 The server verifies the supplied UNAME and PASSWD, and sends the 185 following response: 186 187 +----+--------+ 188 |VER | STATUS | 189 +----+--------+ 190 | 1 | 1 | 191 +----+--------+ 192 193 A STATUS field of X'00' indicates success. If the server returns a 194 `failure' (STATUS value other than X'00') status, it MUST close the 195 connection. 196*/ 197 //in.read(buf, 0, 2); 198 fill(in, buf, 2); 199 if(buf[1]==0) 200 { 201 check=true; 202 } 203 break; 204 default: 205 } 206 207 if(!check) 208 { 209 try 210 { 211 socket.close(); 212 } 213 catch(Exception eee) 214 { 215 } 216 throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, 217 "fail in SOCKS5 proxy"); 218 } 219 220/* 221 The SOCKS request is formed as follows: 222 223 +----+-----+-------+------+----------+----------+ 224 |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | 225 +----+-----+-------+------+----------+----------+ 226 | 1 | 1 | X'00' | 1 | Variable | 2 | 227 +----+-----+-------+------+----------+----------+ 228 229 Where: 230 231 o VER protocol version: X'05' 232 o CMD 233 o CONNECT X'01' 234 o BIND X'02' 235 o UDP ASSOCIATE X'03' 236 o RSV RESERVED 237 o ATYP address type of following address 238 o IP V4 address: X'01' 239 o DOMAINNAME: X'03' 240 o IP V6 address: X'04' 241 o DST.ADDR desired destination address 242 o DST.PORT desired destination port in network octet 243 order 244*/ 245 246 index=0; 247 buf[index++]=5; 248 buf[index++]=1; // CONNECT 249 buf[index++]=0; 250 251 byte[] hostb=host.getBytes(); 252 int len=hostb.length; 253 buf[index++]=3; // DOMAINNAME 254 buf[index++]=(byte)(len); 255 System.arraycopy(hostb, 0, buf, index, len); 256 index+=len; 257 buf[index++]=(byte)(port>>>8); 258 buf[index++]=(byte)(port&0xff); 259 260 out.write(buf, 0, index); 261 262/* 263 The SOCKS request information is sent by the client as soon as it has 264 established a connection to the SOCKS server, and completed the 265 authentication negotiations. The server evaluates the request, and 266 returns a reply formed as follows: 267 268 +----+-----+-------+------+----------+----------+ 269 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | 270 +----+-----+-------+------+----------+----------+ 271 | 1 | 1 | X'00' | 1 | Variable | 2 | 272 +----+-----+-------+------+----------+----------+ 273 274 Where: 275 276 o VER protocol version: X'05' 277 o REP Reply field: 278 o X'00' succeeded 279 o X'01' general SOCKS server failure 280 o X'02' connection not allowed by ruleset 281 o X'03' Network unreachable 282 o X'04' Host unreachable 283 o X'05' Connection refused 284 o X'06' TTL expired 285 o X'07' Command not supported 286 o X'08' Address type not supported 287 o X'09' to X'FF' unassigned 288 o RSV RESERVED 289 o ATYP address type of following address 290 o IP V4 address: X'01' 291 o DOMAINNAME: X'03' 292 o IP V6 address: X'04' 293 o BND.ADDR server bound address 294 o BND.PORT server bound port in network octet order 295*/ 296 297 //in.read(buf, 0, 4); 298 fill(in, buf, 4); 299 300 if(buf[1]!=0) 301 { 302 try 303 { 304 socket.close(); 305 } 306 catch(Exception eee) 307 { 308 } 309 throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, 310 "server returns "+buf[1]); 311 } 312 313 switch(buf[3]&0xff) 314 { 315 case 1: 316 //in.read(buf, 0, 6); 317 fill(in, buf, 6); 318 break; 319 case 3: 320 //in.read(buf, 0, 1); 321 fill(in, buf, 1); 322 //in.read(buf, 0, buf[0]+2); 323 fill(in, buf, (buf[0]&0xff)+2); 324 break; 325 case 4: 326 //in.read(buf, 0, 18); 327 fill(in, buf, 18); 328 break; 329 default: 330 } 331 return socket; 332 333 } 334 catch(RuntimeException e) 335 { 336 throw e; 337 } 338 catch(Exception e) 339 { 340 try 341 { 342 if(socket!=null) 343 { 344 socket.close(); 345 } 346 } 347 catch(Exception eee) 348 { 349 } 350 String message="ProxySOCKS5: "+e.toString(); 351 if(e instanceof Throwable) 352 { 353 throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,message, 354 (Throwable)e); 355 } 356 throw new IOException(message); 357 } 358 } 359 360 private void fill(InputStream in, byte[] buf, int len) 361 throws IOException 362 { 363 int s=0; 364 while(s<len) 365 { 366 int i=in.read(buf, s, len-s); 367 if(i<=0) 368 { 369 throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, "stream " + 370 "is closed"); 371 } 372 s+=i; 373 } 374 } 375} 376