KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > proxy > ClientSocketFactory


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.proxy;
21
22 import javax.net.SocketFactory;
23 import java.io.*;
24 import java.net.*;
25 import java.util.regex.*;
26 import java.nio.channels.SocketChannel JavaDoc;
27
28 /**
29  * This class is responsible for establishing socket connection to a given IP address. Whether the
30  * connection is direct or routes via some type of proxy depends on current ConnectivitySettings that must
31  * be provided by the caller. Proxy protocols and authentication methods are handled internally and transparently
32  * to the caller.
33  *
34  * @author Maros Sandor
35  */

36 public class ClientSocketFactory extends SocketFactory {
37     private static final int CONNECT_TIMEOUT = 1000 * 20; /// 20 seconds timeout
38

39     private static final String JavaDoc AUTH_NONE = "<none>";
40     private static final String JavaDoc AUTH_BASIC = "Basic";
41     private static final Pattern sConnectionEstablishedPattern = Pattern.compile("HTTP\\/\\d+\\.\\d+\\s+200\\s+");
42     private static final Pattern sProxyAuthRequiredPattern = Pattern.compile("HTTP\\/\\d+\\.\\d+\\s+407\\s+");
43
44     private ConnectivitySettings mSettings;
45
46     /**
47      * Creates a new socket IP connection factory.
48      *
49      * @param settings the connectivity settings to use when connecting to a remote IP address
50      */

51     public ClientSocketFactory(ConnectivitySettings settings) {
52         mSettings = settings;
53     }
54
55     /**
56      * Creates probe socket that supports only
57      * connect(SocketAddressm, int timeout).
58      */

59     public Socket createSocket() throws IOException {
60         return new Socket() {
61             public void connect(SocketAddress endpoint, int timeout) throws IOException {
62                 Socket s = createSocket((InetSocketAddress)endpoint, timeout);
63                 s.close();
64             }
65
66             public void bind(SocketAddress bindpoint) {
67                 throw new UnsupportedOperationException JavaDoc();
68             }
69
70             protected Object JavaDoc clone() {
71                 throw new UnsupportedOperationException JavaDoc();
72             }
73
74             public synchronized void close() {
75             }
76
77             public void connect(SocketAddress endpoint) {
78                 throw new UnsupportedOperationException JavaDoc();
79             }
80
81             public SocketChannel JavaDoc getChannel() {
82                 throw new UnsupportedOperationException JavaDoc();
83             }
84
85             public InetAddress getInetAddress() {
86                 throw new UnsupportedOperationException JavaDoc();
87             }
88
89             public InputStream getInputStream() {
90                 throw new UnsupportedOperationException JavaDoc();
91             }
92
93             public boolean getKeepAlive() {
94                 throw new UnsupportedOperationException JavaDoc();
95             }
96
97             public InetAddress getLocalAddress() {
98                 throw new UnsupportedOperationException JavaDoc();
99             }
100
101             public int getLocalPort() {
102                 throw new UnsupportedOperationException JavaDoc();
103             }
104
105             public SocketAddress getLocalSocketAddress() {
106                 throw new UnsupportedOperationException JavaDoc();
107             }
108
109             public boolean getOOBInline() {
110                 throw new UnsupportedOperationException JavaDoc();
111             }
112
113             public OutputStream getOutputStream() {
114                 throw new UnsupportedOperationException JavaDoc();
115             }
116
117             public int getPort() {
118                 throw new UnsupportedOperationException JavaDoc();
119             }
120
121             public synchronized int getReceiveBufferSize() {
122                 throw new UnsupportedOperationException JavaDoc();
123             }
124
125             public SocketAddress getRemoteSocketAddress() {
126                 throw new UnsupportedOperationException JavaDoc();
127             }
128
129             public boolean getReuseAddress() {
130                 throw new UnsupportedOperationException JavaDoc();
131             }
132
133             public synchronized int getSendBufferSize() {
134                 throw new UnsupportedOperationException JavaDoc();
135             }
136
137             public int getSoLinger() {
138                 throw new UnsupportedOperationException JavaDoc();
139             }
140
141             public synchronized int getSoTimeout() {
142                 throw new UnsupportedOperationException JavaDoc();
143             }
144
145             public boolean getTcpNoDelay() {
146                 throw new UnsupportedOperationException JavaDoc();
147             }
148
149             public int getTrafficClass() {
150                 throw new UnsupportedOperationException JavaDoc();
151             }
152
153             public boolean isBound() {
154                 throw new UnsupportedOperationException JavaDoc();
155             }
156
157             public boolean isClosed() {
158                 throw new UnsupportedOperationException JavaDoc();
159             }
160
161             public boolean isConnected() {
162                 throw new UnsupportedOperationException JavaDoc();
163             }
164
165             public boolean isInputShutdown() {
166                 throw new UnsupportedOperationException JavaDoc();
167             }
168
169             public boolean isOutputShutdown() {
170                 throw new UnsupportedOperationException JavaDoc();
171             }
172
173             public void sendUrgentData(int data) {
174                 throw new UnsupportedOperationException JavaDoc();
175             }
176
177             public void setKeepAlive(boolean on) {
178                 throw new UnsupportedOperationException JavaDoc();
179             }
180
181             public void setOOBInline(boolean on) {
182                 throw new UnsupportedOperationException JavaDoc();
183             }
184
185             public synchronized void setReceiveBufferSize(int size) {
186                 throw new UnsupportedOperationException JavaDoc();
187             }
188
189             public void setReuseAddress(boolean on) {
190                 throw new UnsupportedOperationException JavaDoc();
191             }
192
193             public synchronized void setSendBufferSize(int size) {
194                 throw new UnsupportedOperationException JavaDoc();
195             }
196
197             public void setSoLinger(boolean on, int linger) {
198                 throw new UnsupportedOperationException JavaDoc();
199             }
200
201             public synchronized void setSoTimeout(int timeout) {
202                 throw new UnsupportedOperationException JavaDoc();
203             }
204
205             public void setTcpNoDelay(boolean on) {
206                 throw new UnsupportedOperationException JavaDoc();
207             }
208
209             public void setTrafficClass(int tc) {
210                 throw new UnsupportedOperationException JavaDoc();
211             }
212
213             public void shutdownInput() {
214                 throw new UnsupportedOperationException JavaDoc();
215             }
216
217             public void shutdownOutput() {
218                 throw new UnsupportedOperationException JavaDoc();
219             }
220         };
221     }
222
223     public Socket createSocket(String JavaDoc host, int port) throws IOException, UnknownHostException {
224         return createSocket(new InetSocketAddress(host, port), CONNECT_TIMEOUT);
225     }
226
227     public Socket createSocket(InetAddress inetAddress, int port) throws IOException {
228         return createSocket(new InetSocketAddress(inetAddress, port), CONNECT_TIMEOUT);
229     }
230
231     public Socket createSocket(String JavaDoc s, int i, InetAddress inetAddress, int i1) throws IOException,
232             UnknownHostException {
233         throw new IOException("Unsupported operation");
234     }
235
236     public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException {
237         throw new IOException("Unsupported operation");
238     }
239
240     /**
241      * Connects to the remote machine by establishing a tunnel through a HTTP proxy. It issues a CONNECT request and
242      * eventually authenticates with the HTTP proxy. Supported authentication methods include: Basic.
243      *
244      * @param address remote machine to connect to
245      * @return a TCP/IP socket connected to the remote machine
246      * @throws UnknownHostException if the proxy host name cannot be resolved
247      * @throws IOException if an I/O error occurs during handshake (a network problem)
248      */

249     private Socket getHttpsTunnelSocket(InetSocketAddress address, int timeout) throws UnknownHostException, IOException {
250         Socket proxy = new Socket();
251         proxy.connect(new InetSocketAddress(mSettings.getProxyHost(), mSettings.getProxyPort()), timeout);
252         BufferedReader r = new BufferedReader(new InputStreamReader(new InterruptibleInputStream(proxy.getInputStream())));
253         DataOutputStream dos = new DataOutputStream(proxy.getOutputStream());
254
255         dos.writeBytes("CONNECT ");
256         dos.writeBytes(address.getHostName() + ":" + address.getPort());
257         dos.writeBytes(" HTTP/1.0\r\n");
258         dos.writeBytes("Connection: Keep-Alive\r\n\r\n");
259         dos.flush();
260
261         String JavaDoc line;
262         line = r.readLine();
263
264         if (sConnectionEstablishedPattern.matcher(line).find()) {
265             for (; ;) {
266                 line = r.readLine();
267                 if (line.length() == 0) break;
268             }
269             return proxy;
270         } else if (sProxyAuthRequiredPattern.matcher(line).find()) {
271             boolean authMethodSelected = false;
272             String JavaDoc authMethod = AUTH_NONE;
273             for (; ;) {
274                 line = r.readLine();
275                 if (line.length() == 0) break;
276                 if (line.startsWith("Proxy-Authenticate:") && !authMethodSelected) {
277                     authMethod = line.substring(19).trim();
278                     if (authMethod.equals(AUTH_BASIC)) {
279                         authMethodSelected = true;
280                     }
281                 }
282             }
283             // TODO: need to read full response before closing connection?
284
proxy.close();
285
286             if (authMethod.startsWith(AUTH_BASIC)) {
287                 return authenticateBasic(address);
288             } else {
289                 throw new IOException("Unsupported authentication method: " + authMethod);
290             }
291         } else {
292             proxy.close();
293             throw new IOException("HTTP proxy does not support CONNECT command. Received reply: " + line);
294         }
295     }
296
297     /**
298      * Connects to the remote machine by establishing a tunnel through a HTTP proxy with Basic authentication.
299      * It issues a CONNECT request and authenticates with the HTTP proxy with Basic protocol.
300      *
301      * @param address remote machine to connect to
302      * @return a TCP/IP socket connected to the remote machine
303      * @throws IOException if an I/O error occurs during handshake (a network problem)
304      */

305     private Socket authenticateBasic(InetSocketAddress address) throws IOException {
306         Socket proxy = new Socket(mSettings.getProxyHost(), mSettings.getProxyPort());
307         BufferedReader r = new BufferedReader(new InputStreamReader(new InterruptibleInputStream(proxy.getInputStream())));
308         DataOutputStream dos = new DataOutputStream(proxy.getOutputStream());
309
310         String JavaDoc username = mSettings.getProxyUsername() == null ? "" : mSettings.getProxyUsername();
311         String JavaDoc password = mSettings.getProxyPassword() == null ? "" : String.valueOf(mSettings.getProxyPassword());
312         String JavaDoc credentials = username + ":" + password;
313         String JavaDoc basicCookie = Base64Encoder.encode(credentials.getBytes("US-ASCII"));
314
315         dos.writeBytes("CONNECT ");
316         dos.writeBytes(address.getHostName() + ":" + address.getPort());
317         dos.writeBytes(" HTTP/1.0\r\n");
318         dos.writeBytes("Connection: Keep-Alive\r\n");
319         dos.writeBytes("Proxy-Authorization: Basic " + basicCookie + "\r\n");
320         dos.writeBytes("\r\n");
321         dos.flush();
322
323         String JavaDoc line = r.readLine();
324         if (sConnectionEstablishedPattern.matcher(line).find()) {
325             for (; ;) {
326                 line = r.readLine();
327                 if (line.length() == 0) break;
328             }
329             return proxy;
330         }
331         throw new IOException("Basic authentication failed: " + line);
332     }
333
334     private Socket getSocks4TunnelSocket(InetSocketAddress address, int timeout) throws IOException {
335         boolean success = false;
336         Socket proxy = new Socket();
337         proxy.connect(new InetSocketAddress(mSettings.getProxyHost(), mSettings.getProxyPort()), timeout);
338         try {
339             DataInputStream din = new DataInputStream(new InterruptibleInputStream(proxy.getInputStream()));
340             DataOutputStream dos = new DataOutputStream(proxy.getOutputStream());
341
342             dos.writeByte(4); // protocol
343
dos.writeByte(1); // connect command
344
dos.writeShort(address.getPort());
345             InetAddress addr = address.getAddress();
346             if (addr == null) throw new UnknownHostException(address.getHostName());
347             byte[] byteAddress = addr.getAddress();
348             for (int i = 0; i < byteAddress.length; i++) {
349                 dos.writeByte(byteAddress[i]);
350             }
351             String JavaDoc uname = mSettings.getProxyUsername();
352             if (uname != null) {
353                 byte[] unamebytes = uname.getBytes();
354                 for (int i = 0; i < unamebytes.length; i++) {
355                     dos.writeByte(unamebytes[i]);
356                 }
357             }
358             dos.writeByte(0);
359
360             int replyVersion = din.read();
361             if (replyVersion != 0) throw new IOException("socks4.not.available." + replyVersion);
362             int retCode = din.read();
363             if (retCode != 90) throw new IOException("socks4.error." + retCode);
364             while (din.available() > 0) din.read();
365             success = true;
366             return proxy;
367         } finally {
368             if (!success) proxy.close();
369         }
370     }
371
372     private Socket getSocks5TunnelSocket(InetSocketAddress address, int timeout) throws IOException {
373         boolean success = false;
374         int tmp;
375
376         Socket proxy = new Socket();
377         proxy.connect(new InetSocketAddress(mSettings.getProxyHost(), mSettings.getProxyPort()), timeout);
378         try {
379             DataInputStream din = new DataInputStream(new InterruptibleInputStream(proxy.getInputStream()));
380             DataOutputStream dos = new DataOutputStream(proxy.getOutputStream());
381
382 // protocol, # of supported auth methods, no auth, user/pass auth
383
dos.write(new byte[]{5, 2, 0, 2});
384
385             int serverVersion = din.read();
386             if (serverVersion != 5) throw new IOException("SOCKS5 protocol error: version: " + serverVersion);
387             int authMethod = din.read();
388             if (authMethod == 0xFF) throw new IOException("SOCKS5 authentication failure: no supported method acccepted by server");
389
390             if (authMethod == 2) {
391                 dos.writeByte(1); // negotiation version
392
String JavaDoc uname = mSettings.getProxyUsername();
393                 byte[] unamebytes = (uname == null) ? new byte[]{} : uname.getBytes();
394                 dos.writeByte(unamebytes.length);
395                 for (int i = 0; i < unamebytes.length; i++) {
396                     dos.writeByte(unamebytes[i]);
397                 }
398                 String JavaDoc pwd = null;
399                 if (mSettings.getProxyPassword() != null) {
400                     pwd = new String JavaDoc(mSettings.getProxyPassword());
401                 }
402                 byte[] pwdbytes = (pwd == null) ? new byte[]{} : pwd.getBytes();
403                 dos.writeByte(pwdbytes.length);
404                 for (int i = 0; i < pwdbytes.length; i++) {
405                     dos.writeByte(pwdbytes[i]);
406                 }
407
408                 tmp = din.read();
409                 if (tmp != 1) throw new IOException("socks5.auth.error." + tmp);
410                 tmp = din.read();
411                 if (tmp != 0) throw new IOException("socks5.auth.error." + tmp);
412             }
413
414             String JavaDoc hostName = address.getHostName();
415 // protocol, CONNECT, <reserved>, domain name follows, domain name length
416
dos.write(new byte[]{5, 1, 0, 3, (byte) hostName.length()});
417             dos.writeBytes(hostName);
418             dos.writeShort(address.getPort());
419
420             serverVersion = din.read();
421             if (serverVersion != 5) throw new IOException("SOCKS5 protocol error: version: " + serverVersion);
422             tmp = din.read();
423             if (tmp != 0) throw new IOException("SOCKS5 protocol error: " + tmp);
424             tmp = din.read();
425             if (tmp != 0) throw new IOException("SOCKS5 protocol error: " + tmp);
426             int addrType = din.read();
427             if (addrType == -1) throw new IOException("SOCKS5 protocol error: " + addrType);
428             // address
429
for (int i = 0; i < 4; i++) {
430                 tmp = din.read();
431                 if (tmp == -1) throw new IOException("SOCKS5 error: " + tmp);
432             }
433             // port
434
tmp = din.read();
435             tmp = din.read();
436             success = true;
437             return proxy;
438         } finally {
439             if (!success) proxy.close();
440         }
441     }
442
443     /**
444      * Creates a new Socket connected to the given IP address. The method uses connection settings supplied
445      * in the constructor for connecting the socket.
446      *
447      * @param address the IP address to connect to
448      * @return connected socket
449      * @throws java.net.UnknownHostException if the hostname of the address or the proxy cannot be resolved
450      * @throws java.io.IOException if an I/O error occured while connecting to the remote end or to the proxy
451      */

452     private Socket createSocket(InetSocketAddress address, int timeout) throws UnknownHostException, IOException {
453         String JavaDoc socksProxyHost = System.getProperty("socksProxyHost");
454         System.getProperties().remove("socksProxyHost");
455         try
456         {
457         switch (mSettings.getConnectionType()) {
458         case ConnectivitySettings.CONNECTION_VIA_SOCKS:
459             try {
460                 return getSocks5TunnelSocket(address, timeout);
461             } catch (IOException e) {
462                 return getSocks4TunnelSocket(address, timeout);
463             }
464
465         case ConnectivitySettings.CONNECTION_DIRECT:
466             Socket s = new Socket();
467             s.connect(address, timeout);
468             return s;
469
470         case ConnectivitySettings.CONNECTION_VIA_HTTPS:
471             return getHttpsTunnelSocket(address, timeout);
472
473         default:
474             throw new IOException("Illegal connection type: " + mSettings.getConnectionType());
475         }
476         } finally {
477             if (socksProxyHost != null) {
478                 System.setProperty("socksProxyHost", socksProxyHost);
479     }
480         }
481     }
482
483 }
484
Popular Tags