1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.example.proxy;
21  
22  import java.net.InetSocketAddress;
23  import java.net.URL;
24  import java.security.Security;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.List;
28  
29  import org.apache.mina.core.RuntimeIoException;
30  import org.apache.mina.core.future.ConnectFuture;
31  import org.apache.mina.core.session.IdleStatus;
32  import org.apache.mina.core.session.IoSession;
33  import org.apache.mina.filter.logging.LoggingFilter;
34  import org.apache.mina.proxy.ProxyConnector;
35  import org.apache.mina.proxy.handlers.ProxyRequest;
36  import org.apache.mina.proxy.handlers.http.HttpAuthenticationMethods;
37  import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
38  import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
39  import org.apache.mina.proxy.handlers.socks.SocksProxyConstants;
40  import org.apache.mina.proxy.handlers.socks.SocksProxyRequest;
41  import org.apache.mina.proxy.session.ProxyIoSession;
42  import org.apache.mina.proxy.utils.MD4Provider;
43  import org.apache.mina.transport.socket.nio.NioSocketConnector;
44  
45  /**
46   * ProxyTestClient.java - Base test class for mina proxy
47   * 
48   * @author The Apache MINA Project (dev@mina.apache.org)
49   * @version $Rev$, $Date$
50   * @since MINA 2.0.0-M3
51   */
52  public class ProxyTestClient {
53      
54      /**
55       * The global variables used when creating the HTTP proxy connection.
56       */
57      
58      /**
59       * The user login.
60       */
61      public final static String USER = "TED_KODS";
62  
63      /**
64       * The user password.
65       */
66      public final static String PWD = "EDOUARD";
67  
68      /**
69       * The domain name. (used in NTLM connections)
70       */
71      public final static String DOMAIN = "MYDOMAIN";
72  
73      /**
74       * The workstation name. (used in NTLM connections)
75       */    
76      public final static String WORKSTATION = "MYWORKSTATION";
77  
78      /**
79       * Set this variable to true in order to generate HTTP/1.1 requests.
80       */
81      private final static boolean USE_HTTP_1_1 = false;
82  
83      /**
84       * NTLM proxy authentication needs a JCE provider that handles MD4 hashing.
85       */
86      static {
87          if (Security.getProvider("MINA") == null) {
88              Security.addProvider(new MD4Provider());
89          }
90      }
91  
92      /**
93       * Creates a connection to the endpoint through a proxy server using the specified
94       * authentication method.
95       * 
96       * Command line arguments: 
97       *       ProxyTestClient <proxy-hostname> <proxy-port> <url> <proxy-method> 
98       *       
99       * Note that <proxy-method> is OPTIONNAL a HTTP proxy connection will be used if not 
100      * specified.
101      * 
102      * Examples:
103      *          ProxyTestClient myproxy 8080 http://mina.apache.org SOCKS4
104      *          ProxyTestClient squidsrv 3128 http://mina.apache.org:80
105      *       
106      * @param args parse arguments to get proxy hostaname, proxy port, the url to connect to 
107      * and optionnaly the proxy authentication method 
108      * @throws Exception
109      */
110     public ProxyTestClient(String[] args) throws Exception {
111         if (args.length < 3) {
112             System.out
113                     .println(ProxyTestClient.class.getName()
114                             + " <proxy-hostname> <proxy-port> <url> <proxy-method> (<proxy-method> is OPTIONNAL)");
115             return;
116         }
117 
118         // Create proxy connector.
119         NioSocketConnector socketConnector = new NioSocketConnector(Runtime
120                 .getRuntime().availableProcessors() + 1);
121 
122         ProxyConnector connector = new ProxyConnector(socketConnector);
123 
124         // Set connect timeout.
125         connector.setConnectTimeoutMillis(5000);
126 
127         URL url = new URL(args[2]);
128         int port = url.getPort() == -1 ? url.getDefaultPort() : url.getPort();
129 
130         ProxyRequest req = null;
131 
132         if (args.length == 4) {
133             if ("SOCKS4".equals(args[3])) {
134                 req = new SocksProxyRequest(
135                         SocksProxyConstants.SOCKS_VERSION_4,
136                         SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
137                         new InetSocketAddress(url.getHost(), port), USER);
138             } else if ("SOCKS4a".equals(args[3])) {
139                 req = new SocksProxyRequest(
140                         SocksProxyConstants.ESTABLISH_TCPIP_STREAM, url
141                                 .getHost(), port, USER);
142             } else if ("SOCKS5".equals(args[3])) {
143                 req = new SocksProxyRequest(
144                         SocksProxyConstants.SOCKS_VERSION_5,
145                         SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
146                         new InetSocketAddress(url.getHost(), port), USER);
147                 ((SocksProxyRequest) req).setPassword(PWD);
148                 ((SocksProxyRequest) req)
149                         .setServiceKerberosName(Socks5GSSAPITestServer.SERVICE_NAME);
150             } else {
151                 req = createHttpProxyRequest(args[2]);
152             }
153         } else {
154             req = createHttpProxyRequest(args[2]);
155         }
156 
157         ProxyIoSession proxyIoSession = new ProxyIoSession(
158                 new InetSocketAddress(args[0], Integer.parseInt(args[1])), req);
159 
160         // Tests modifying authentication order preferences. First algorithm in list available on server 
161         // will be used for authentication.
162         List<HttpAuthenticationMethods> l = new ArrayList<HttpAuthenticationMethods>();
163         l.add(HttpAuthenticationMethods.DIGEST);
164         l.add(HttpAuthenticationMethods.BASIC);
165         proxyIoSession.setPreferedOrder(l);
166 
167         connector.setProxyIoSession(proxyIoSession);
168 
169         socketConnector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 5);
170 
171         connector.getFilterChain().addLast("logger", new LoggingFilter());
172 
173         // This command is sent when using a socks proxy to request a page from the web server.
174         String cmd = "GET " + url.toExternalForm() + " HTTP/1.0"
175                 + HttpProxyConstants.CRLF + HttpProxyConstants.CRLF;
176 
177         connector.setHandler(new ClientSessionHandler(cmd));
178 
179         IoSession session;
180         for (;;) {
181             try {
182                 ConnectFuture future = connector.connect();
183                 future.awaitUninterruptibly();
184                 session = future.getSession();
185                 break;
186             } catch (RuntimeIoException e) {
187                 System.err.println("Failed to connect. Retrying in 5 secs ...");
188                 Thread.sleep(5000);
189             }
190         }
191 
192         // Wait until done
193         if (session != null) {
194             session.getCloseFuture().awaitUninterruptibly();
195         }
196         connector.dispose();
197         System.exit(0);
198     }
199 
200     /**
201      * Creates a {@link HttpProxyRequest} from the provided <i>uri</i> parameter.
202      * It uses the global variables defined at the top of the class to fill the 
203      * connection properties of the request. If the global variable <i>useHttp1_1</i> 
204      * is set to true, it will create a HTTP/1.1 request.
205      * 
206      * @param uri the requested uri to connect to through the HTTP proxy
207      * @return the fully initialized {@link HttpProxyRequest} object
208      */
209     private HttpProxyRequest createHttpProxyRequest(String uri) {
210         HttpProxyRequest req = new HttpProxyRequest(uri);
211         HashMap<String, String> props = new HashMap<String, String>();
212         props.put(HttpProxyConstants.USER_PROPERTY, USER);
213         props.put(HttpProxyConstants.PWD_PROPERTY, PWD);
214         props.put(HttpProxyConstants.DOMAIN_PROPERTY, DOMAIN);
215         props.put(HttpProxyConstants.WORKSTATION_PROPERTY, WORKSTATION);
216 
217         req.setProperties(props);
218         if (USE_HTTP_1_1) {
219             req.setHttpVersion(HttpProxyConstants.HTTP_1_1);
220         }
221 
222         return req;
223     }
224 
225     /**
226      * {@inheritDoc}
227      */
228     public static void main(String[] args) throws Exception {
229         new ProxyTestClient(args);
230     }
231 }