├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── socks
│ ├── Authentication.java
│ ├── AuthenticationNone.java
│ ├── CProxy.java
│ ├── InetRange.java
│ ├── ProxyMessage.java
│ ├── ProxyServer.java
│ ├── Socks4Message.java
│ ├── Socks4Proxy.java
│ ├── Socks5DatagramSocket.java
│ ├── Socks5Message.java
│ ├── Socks5Proxy.java
│ ├── SocksDialog.java
│ ├── SocksException.java
│ ├── SocksServerSocket.java
│ ├── SocksSocket.java
│ ├── UDPEncapsulation.java
│ ├── UDPRelayServer.java
│ ├── UserPasswordAuthentication.java
│ └── server
│ ├── Ident.java
│ ├── IdentAuthenticator.java
│ ├── ServerAuthenticator.java
│ ├── ServerAuthenticatorNone.java
│ ├── UserPasswordAuthenticator.java
│ └── UserValidation.java
└── test
├── java
├── Echo.java
├── SOCKS.java
├── SocksEcho.java
├── SocksTest.java
├── SocksUDPEcho.java
├── TestClient.java
├── TestServer.java
├── TestService.java
├── UDPEcho.java
└── UPSOCKS.java
└── resources
├── SocksEcho.gif
└── socks.properties
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Java CI with Maven
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v1.2.0
10 | - name: Set up JDK
11 | uses: actions/setup-java@v1.3.0
12 | with:
13 | java-version: 8
14 | server-id: ossrh
15 | server-username: SONATYPE_USER
16 | server-password: SONATYPE_PW
17 |
18 | - name: Build
19 | run: mvn -B package
20 |
21 | - name: Release to Maven Central
22 | if: ${{ github.ref }} == 'refs/heads/master'
23 | env:
24 | SONATYPE_USER: ${{ secrets.SONATYPE_USER }}
25 | SONATYPE_PW: ${{ secrets.SONATYPE_PW }}
26 | run: |
27 | cat <(echo -e "${{ secrets.GPG_KEY }}") | gpg --batch --import
28 | gpg --list-secret-keys --keyid-format LONG
29 | mvn \
30 | --no-transfer-progress \
31 | --batch-mode \
32 | -Dgpg.passphrase="${{ secrets.GPG_PW }}" \
33 | -DperformRelease=true \
34 | deploy
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | .idea/
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://search.maven.org/artifact/jitsi/jsocks)
2 | [](http://javadoc.io/doc/jitsi/jsocks)
3 |
4 |
5 | # JAVA SOCKS Server
6 | It is a SOCKS server written entirely in Java, which supports both SOCKS4 and SOCKS5 protocols. This software is distributed under GNU Lesser General Public License, meaning that both binary and source code are freely available and can be modified an distributed.
7 | I would not bother you with long list of things I'm not responsible for. Let me just say, that you are using this program on your own risk, and it is not me to blame if anything doesn't work as you expected. I tried my best, but it is up to you to check if it works as you wish it to, the source code is there, so you can correct whatever you don't like.
8 |
9 | ## Update Information
10 | Please visit the JSocks Project Page for latest updates. The CVS update log should be most informative.
11 | Latest packaged version is 1.01. Proxy have been rewised and there are some changes to the Socks Library. New features have been added to the proxy server itself, well at least one
12 | Now it is possible to use this proxy from behind another proxy.
13 | Also proxy chaining is introduced, it allows you to configure the proxy to connect through a series of proxies before reaching the desired host: proxy1-> proxy2->...proxyN->desired_host. It is a strange feature but might be useful in some situations.
14 | Some bug fixes have been done, it is now possible to use ICQ with this proxy server.
15 |
16 | ## What this Server is good for?
17 | Well, it is a not a commercial proxy server. It does not have plenty of features like address caching. Authentication scheme is rather simplistic, but can be extended, if you know how to program in Java. It is java application after all, which gives platform independence but at the cost of performance. But it is simple to install and use.
18 | It is assumed that the socks library itself is more useful to most developers, rather than a simple server which uses the library. If you are a developer and want your applications to have support for SOCKSv5 protocol, you might find useful visiting Socks Library page.
19 |
20 | ## SocksEcho
21 | SocksEcho is another application which uses socks package. It is GUI echo client, which is SOCKS aware, you can make and accept connections through the proxy using this client. You can also send datagrams through SOCKS5 proxy. I have used it a lot for testing. This application is also included.
22 |
23 | ## Requirements
24 | Java 1.8
25 |
26 | ## Acknowledgments
27 | I would like to thank people at SourceForge for the great services they provide to open source community.
28 |
29 |
30 | SOCKS Library
31 | -------------
32 | This page contains information that might be interesting for developers, who wish to use SOCKS protocol in applications they are developing, both client and server side.
33 |
34 | ## Why to use it
35 | There are plenty of reasons for which you might want to use SOCKS library in your java application. They are generally divided in two parts
36 | 1. You are developing a client application, which needs to access proxy.
37 | 2. You are developing a custom Proxy server.
38 |
39 | ## Client Application
40 | Current version of java has a support for SOCKSv4 proxy, which is probably enough for most applications, which need to do some networking. Connections are being done through the proxy or directly depending on the user configurations, transparently to the java application. But SOCKSv4 has some limitations:
41 |
42 | 1. Lack of sufficient authentication.
43 | The only authentication SOCKSv4 supports is the authentication based on Ident protocol. Basically client tells the server who he is, and server verifies it with the identd daemon running on the client machine. This technique works fine when proxy is used to let people on local network have access to the Internet. But it is not suitable when proxy is used to let people from the outside in.
44 |
45 | 2. Lack of UDP support.
46 | SOCKSv4 protocol does not have any UDP support. And currently with almost all multimedia applications using UDP this might be a serious disadvantage.
47 | Besides implementation of the SOCKSv4 in Java appears to be not complete (probably I'm wrong, check with sun). It appears to me that there is no way to accept a connection from the outside Firewall using Java1.2 ServerSocket.
48 |
49 | So if you need your application to be able to
50 | 1. Send and receive datagrams through the firewall.
51 | 2. Accept connections from hosts behind the firewall.
52 | 3. Provide non-trivial authentication to the proxy server.
53 | 4. Ensure secure connection to the proxy server.
54 |
55 | you might find that you need SOCKSv5 support, and hence this library may be useful to you. Even though I have forced the advantages of SOCKSv5 protocol, this library supports SOCKSv4 as well.
56 |
57 | Hopefully one day Java will support SOCKSv5 as well, but it's not yet happened.
58 |
59 | ## Server Application
60 | If you are developing proxy server which need to support either SOCKSv4 or SOCKSv5 or both, you'll find using this library very time saving. There are plenty of things to take care about when developing proxy server, protocol handling is just one part of the problem, which have been solved by this library, so that you can concentrate on authentication, authorization and initialization.
61 | There are generally two major reasons for which proxy servers are used: to let people in, or to let them out, or both. Proxy server is like a door in the Firewall. Usually you do not care all that much who gets out, but you should be very careful in deciding who gets in.
62 |
63 | SOCKSv5 protocol provides for extendable authentication schemes, which might be standard or private. And this socks library makes it easy to use your own implementations of authentication methods. Next step after authentication is authorization, for which methods are also provided.
64 |
65 | ## How to use
66 | Now as you are convinced in the fact that you'll need to use socks library. Its time to know how to do so.
67 | How to page will cover these issues.
68 | I have just finished redesign and testing. There have been major changes to the library, mostly due to poor design decisions in the first stage. Few things that have been changed:
69 |
70 | - It is now possible to connect through unlimited number of proxies, rather then just one. That is it is now possible to use proxyA to connect to proxyB to connect to ... proxyZ to connect to desired host.
71 | - I have previously overlooked UDP handling. It was not possible to perform authentication specific transformations to UDP data. Now this problem have been removed.
72 | - Few convenience features have been added.
73 | - Some bugs have been fixed.
74 | - Some restructuring have been done, to yield more convenient package structure.
75 | - How to page is still yet to come. I haven't had time recently. As for now, read the documentation pages, they are now available online.
76 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 | This method should return an array {inputstream,outputstream 14 | [,UDPEncapsulation]}. The reason for that is that SOCKS5 protocol 15 | allows to have method specific encapsulation of data on the socket for 16 | purposes of integrity or security. And this encapsulation should be 17 | performed by those streams returned from the method. It is also possible 18 | to encapsulate datagrams. If authentication method supports such 19 | encapsulation an instance of the UDPEncapsulation interface should be 20 | returned as third element of the array, otherwise either null should be 21 | returned as third element, or array should contain only 2 elements. 22 | 23 | @param methodId Authentication method selected by the server. 24 | @param proxySocket Socket used to conect to the proxy. 25 | @return Two or three element array containing 26 | Input/Output streams which should be used on this connection. 27 | Third argument is optional and should contain an instance 28 | of UDPEncapsulation. It should be provided if the authentication 29 | method used requires any encapsulation to be done on the 30 | datagrams. 31 | */ 32 | Object[] doSocksAuthentication(int methodId,java.net.Socket proxySocket) 33 | throws java.io.IOException; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/socks/AuthenticationNone.java: -------------------------------------------------------------------------------- 1 | package socks; 2 | 3 | /** 4 | SOCKS5 none authentication. Dummy class does almost nothing. 5 | */ 6 | public class AuthenticationNone implements Authentication{ 7 | 8 | public Object[] doSocksAuthentication(int methodId, 9 | java.net.Socket proxySocket) 10 | throws java.io.IOException{ 11 | 12 | if(methodId!=0) return null; 13 | 14 | return new Object[] { proxySocket.getInputStream(), 15 | proxySocket.getOutputStream()}; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/socks/CProxy.java: -------------------------------------------------------------------------------- 1 | package socks; 2 | import java.net.*; 3 | import java.io.*; 4 | import java.util.Hashtable; 5 | import java.util.Enumeration; 6 | 7 | /** 8 | Abstract class CProxy, base for classes Socks4Proxy and Socks5Proxy. 9 | Defines methods for specifying default proxy, to be 10 | used by all classes of this package. 11 | */ 12 | 13 | public abstract class CProxy{ 14 | 15 | //Data members 16 | protected InetRange directHosts = new InetRange(); 17 | 18 | protected InetAddress proxyIP = null; 19 | protected String proxyHost = null; 20 | protected int proxyPort; 21 | protected Socket proxySocket = null; 22 | 23 | protected InputStream in; 24 | protected OutputStream out; 25 | 26 | protected int version; 27 | 28 | protected CProxy chainProxy = null; 29 | 30 | 31 | //Protected static/class variables 32 | protected static CProxy defaultProxy = null; 33 | 34 | //Constructors 35 | //==================== 36 | CProxy(CProxy chainProxy, 37 | String proxyHost,int proxyPort)throws UnknownHostException{ 38 | this.chainProxy = chainProxy; 39 | this.proxyHost = proxyHost; 40 | 41 | if(chainProxy == null) 42 | this.proxyIP = InetAddress.getByName(proxyHost); 43 | 44 | this.proxyPort = proxyPort; 45 | } 46 | 47 | 48 | CProxy(String proxyHost,int proxyPort)throws UnknownHostException{ 49 | this(null,proxyHost,proxyPort); 50 | } 51 | 52 | CProxy(CProxy chainProxy,InetAddress proxyIP,int proxyPort){ 53 | this.chainProxy = chainProxy; 54 | this.proxyIP = proxyIP; 55 | this.proxyPort = proxyPort; 56 | } 57 | 58 | CProxy(InetAddress proxyIP,int proxyPort){ 59 | this(null,proxyIP,proxyPort); 60 | } 61 | 62 | CProxy(CProxy p){ 63 | this.proxyIP = p.proxyIP; 64 | this.proxyPort = p.proxyPort; 65 | this.version = p.version; 66 | this.directHosts = p.directHosts; 67 | } 68 | 69 | //Public instance methods 70 | //======================== 71 | 72 | /** 73 | Get the port on which proxy server is running. 74 | * @return CProxy port. 75 | */ 76 | public int getPort(){ 77 | return proxyPort; 78 | } 79 | /** 80 | Get the ip address of the proxy server host. 81 | * @return CProxy InetAddress. 82 | */ 83 | public InetAddress getInetAddress(){ 84 | return proxyIP; 85 | } 86 | /** 87 | * Adds given ip to the list of direct addresses. 88 | * This machine will be accessed without using proxy. 89 | */ 90 | public void addDirect(InetAddress ip){ 91 | directHosts.add(ip); 92 | } 93 | /** 94 | * Adds host to the list of direct addresses. 95 | * This machine will be accessed without using proxy. 96 | */ 97 | public boolean addDirect(String host){ 98 | return directHosts.add(host); 99 | } 100 | /** 101 | * Adds given range of addresses to the lsit of direct addresses, 102 | * machines within this range will be accessed without using proxy. 103 | */ 104 | public void addDirect(InetAddress from,InetAddress to){ 105 | directHosts.add(from,to); 106 | } 107 | /** 108 | * Sets given InetRange as the list of direct address, previous 109 | * list will be discarded, any changes done previously with 110 | * addDirect(Inetaddress) will be lost. 111 | * The machines in this range will be accessed without using proxy. 112 | * @param ir InetRange which should be used to look up direct addresses. 113 | * @see InetRange 114 | */ 115 | public void setDirect(InetRange ir){ 116 | directHosts = ir; 117 | } 118 | 119 | /** 120 | Get the list of direct hosts. 121 | * @return Current range of direct address as InetRange object. 122 | * @see InetRange 123 | */ 124 | public InetRange getDirect(){ 125 | return directHosts; 126 | } 127 | /** 128 | Check wether the given host is on the list of direct address. 129 | @param host Host name to check. 130 | * @return true if the given host is specified as the direct addresses. 131 | */ 132 | public boolean isDirect(String host){ 133 | return directHosts.contains(host); 134 | } 135 | /** 136 | Check wether the given host is on the list of direct addresses. 137 | @param host Host address to check. 138 | * @return true if the given host is specified as the direct address. 139 | */ 140 | public boolean isDirect(InetAddress host){ 141 | return directHosts.contains(host); 142 | } 143 | /** 144 | Set the proxy which should be used to connect to given proxy. 145 | @param chainProxy CProxy to use to connect to this proxy. 146 | */ 147 | public void setChainProxy(CProxy chainProxy){ 148 | this.chainProxy = chainProxy; 149 | } 150 | 151 | /** 152 | Get proxy which is used to connect to this proxy. 153 | @return CProxy which is used to connect to this proxy, or null 154 | if proxy is to be contacted directly. 155 | */ 156 | public CProxy getChainProxy(){ 157 | return chainProxy; 158 | } 159 | 160 | /** 161 | Get string representation of this proxy. 162 | * @return string in the form:proxyHost:proxyPort \t Version versionNumber 163 | */ 164 | public String toString(){ 165 | return (""+proxyIP.getHostName()+":"+proxyPort+"\tVersion "+version); 166 | } 167 | 168 | 169 | //Public Static(Class) Methods 170 | //============================== 171 | 172 | /** 173 | * Sets SOCKS4 proxy as default. 174 | @param hostName Host name on which SOCKS4 server is running. 175 | @param port Port on which SOCKS4 server is running. 176 | @param user Username to use for communications with proxy. 177 | */ 178 | public static void setDefaultProxy(String hostName,int port,String user) 179 | throws UnknownHostException{ 180 | defaultProxy = new Socks4Proxy(hostName,port,user); 181 | } 182 | 183 | /** 184 | * Sets SOCKS4 proxy as default. 185 | @param ipAddress Host address on which SOCKS4 server is running. 186 | @param port Port on which SOCKS4 server is running. 187 | @param user Username to use for communications with proxy. 188 | */ 189 | public static void setDefaultProxy(InetAddress ipAddress,int port, 190 | String user){ 191 | defaultProxy = new Socks4Proxy(ipAddress,port,user); 192 | } 193 | /** 194 | * Sets SOCKS5 proxy as default. 195 | * Default proxy only supports no-authentication. 196 | @param hostName Host name on which SOCKS5 server is running. 197 | @param port Port on which SOCKS5 server is running. 198 | */ 199 | public static void setDefaultProxy(String hostName,int port) 200 | throws UnknownHostException{ 201 | defaultProxy = new Socks5Proxy(hostName,port); 202 | } 203 | /** 204 | * Sets SOCKS5 proxy as default. 205 | * Default proxy only supports no-authentication. 206 | @param ipAddress Host address on which SOCKS5 server is running. 207 | @param port Port on which SOCKS5 server is running. 208 | */ 209 | public static void setDefaultProxy(InetAddress ipAddress,int port){ 210 | defaultProxy = new Socks5Proxy(ipAddress,port); 211 | } 212 | /** 213 | * Sets default proxy. 214 | @param p CProxy to use as default proxy. 215 | */ 216 | public static void setDefaultProxy(CProxy p){ 217 | defaultProxy = p; 218 | } 219 | 220 | /** 221 | Get current default proxy. 222 | * @return Current default proxy, or null if none is set. 223 | */ 224 | public static CProxy getDefaultProxy(){ 225 | return defaultProxy; 226 | } 227 | 228 | /** 229 | Parses strings in the form: host[:port:user:password], and creates 230 | proxy from information obtained from parsing. 231 |
232 | Defaults: port = 1080.
233 | If user specified but not password, creates Socks4Proxy, if user
234 | not specified creates Socks5Proxy, if both user and password are
235 | speciefied creates Socks5Proxy with user/password authentication.
236 | @param proxy_entry String in the form host[:port:user:password]
237 | @return CProxy created from the string, null if entry was somehow
238 | invalid(host unknown for example, or empty string)
239 | */
240 | public static CProxy parseProxy(String proxy_entry){
241 |
242 | String proxy_host;
243 | int proxy_port = 1080;
244 | String proxy_user = null;
245 | String proxy_password = null;
246 | CProxy proxy;
247 |
248 | java.util.StringTokenizer st = new java.util.StringTokenizer(
249 | proxy_entry,":");
250 | if(st.countTokens() < 1) return null;
251 |
252 | proxy_host = st.nextToken();
253 | if(st.hasMoreTokens())
254 | try{
255 | proxy_port = Integer.parseInt(st.nextToken().trim());
256 | }catch(NumberFormatException nfe){}
257 |
258 | if(st.hasMoreTokens())
259 | proxy_user = st.nextToken();
260 |
261 | if(st.hasMoreTokens())
262 | proxy_password = st.nextToken();
263 |
264 | try{
265 | if(proxy_user == null)
266 | proxy = new Socks5Proxy(proxy_host,proxy_port);
267 | else if(proxy_password == null)
268 | proxy = new Socks4Proxy(proxy_host,proxy_port,proxy_user);
269 | else{
270 | proxy = new Socks5Proxy(proxy_host,proxy_port);
271 | UserPasswordAuthentication upa = new UserPasswordAuthentication(
272 | proxy_user, proxy_password);
273 |
274 | ((Socks5Proxy)proxy).setAuthenticationMethod(upa.METHOD_ID,upa);
275 | }
276 | }catch(UnknownHostException uhe){
277 | return null;
278 | }
279 |
280 | return proxy;
281 | }
282 |
283 |
284 | //Protected Methods
285 | //=================
286 |
287 | protected void startSession()throws SocksException{
288 | try{
289 | if(chainProxy == null)
290 | proxySocket = new Socket(proxyIP,proxyPort);
291 | else if(proxyIP != null)
292 | proxySocket = new SocksSocket(chainProxy,proxyIP,proxyPort);
293 | else
294 | proxySocket = new SocksSocket(chainProxy,proxyHost,proxyPort);
295 |
296 | in = proxySocket.getInputStream();
297 | out = proxySocket.getOutputStream();
298 | }catch(SocksException se){
299 | throw se;
300 | }catch(IOException io_ex){
301 | throw new SocksException(SOCKS_PROXY_IO_ERROR,""+io_ex);
302 | }
303 | }
304 |
305 | protected abstract CProxy copy();
306 | protected abstract ProxyMessage formMessage(int cmd,InetAddress ip,int port);
307 | protected abstract ProxyMessage formMessage(int cmd,String host,int port)
308 | throws UnknownHostException;
309 | protected abstract ProxyMessage formMessage(InputStream in)
310 | throws SocksException,
311 | IOException;
312 |
313 |
314 | protected ProxyMessage connect(InetAddress ip,int port)
315 | throws SocksException{
316 | try{
317 | startSession();
318 | ProxyMessage request = formMessage(SOCKS_CMD_CONNECT,
319 | ip,port);
320 | return exchange(request);
321 | }catch(SocksException se){
322 | endSession();
323 | throw se;
324 | }
325 | }
326 | protected ProxyMessage connect(String host,int port)
327 | throws UnknownHostException,SocksException{
328 | try{
329 | startSession();
330 | ProxyMessage request = formMessage(SOCKS_CMD_CONNECT,
331 | host,port);
332 | return exchange(request);
333 | }catch(SocksException se){
334 | endSession();
335 | throw se;
336 | }
337 | }
338 |
339 | protected ProxyMessage bind(InetAddress ip,int port)
340 | throws SocksException{
341 | try{
342 | startSession();
343 | ProxyMessage request = formMessage(SOCKS_CMD_BIND,
344 | ip,port);
345 | return exchange(request);
346 | }catch(SocksException se){
347 | endSession();
348 | throw se;
349 | }
350 | }
351 | protected ProxyMessage bind(String host,int port)
352 | throws UnknownHostException,SocksException{
353 | try{
354 | startSession();
355 | ProxyMessage request = formMessage(SOCKS_CMD_BIND,
356 | host,port);
357 | return exchange(request);
358 | }catch(SocksException se){
359 | endSession();
360 | throw se;
361 | }
362 | }
363 |
364 | protected ProxyMessage accept()
365 | throws IOException,SocksException{
366 | ProxyMessage msg;
367 | try{
368 | msg = formMessage(in);
369 | }catch(InterruptedIOException iioe){
370 | throw iioe;
371 | }catch(IOException io_ex){
372 | endSession();
373 | throw new SocksException(SOCKS_PROXY_IO_ERROR,"While Trying accept:"
374 | +io_ex);
375 | }
376 | return msg;
377 | }
378 |
379 | protected ProxyMessage udpAssociate(InetAddress ip,int port)
380 | throws SocksException{
381 | try{
382 | startSession();
383 | ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE,
384 | ip,port);
385 | if(request != null)
386 | return exchange(request);
387 | }catch(SocksException se){
388 | endSession();
389 | throw se;
390 | }
391 | //Only get here if request was null
392 | endSession();
393 | throw new SocksException(SOCKS_METHOD_NOTSUPPORTED,
394 | "This version of proxy does not support UDP associate, use version 5");
395 | }
396 | protected ProxyMessage udpAssociate(String host,int port)
397 | throws UnknownHostException,SocksException{
398 | try{
399 | startSession();
400 | ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE,
401 | host,port);
402 | if(request != null) return exchange(request);
403 | }catch(SocksException se){
404 | endSession();
405 | throw se;
406 | }
407 | //Only get here if request was null
408 | endSession();
409 | throw new SocksException(SOCKS_METHOD_NOTSUPPORTED,
410 | "This version of proxy does not support UDP associate, use version 5");
411 | }
412 |
413 |
414 | protected void endSession(){
415 | try{
416 | if(proxySocket!=null) proxySocket.close();
417 | proxySocket = null;
418 | }catch(IOException io_ex){
419 | }
420 | }
421 |
422 | /**
423 | *Sends the request to SOCKS server
424 | */
425 | protected void sendMsg(ProxyMessage msg)throws SocksException,
426 | IOException{
427 | msg.write(out);
428 | }
429 |
430 | /**
431 | * Reads the reply from the SOCKS server
432 | */
433 | protected ProxyMessage readMsg()throws SocksException,
434 | IOException{
435 | return formMessage(in);
436 | }
437 | /**
438 | *Sends the request reads reply and returns it
439 | *throws exception if something wrong with IO
440 | *or the reply code is not zero
441 | */
442 | protected ProxyMessage exchange(ProxyMessage request)
443 | throws SocksException{
444 | ProxyMessage reply;
445 | try{
446 | request.write(out);
447 | reply = formMessage(in);
448 | }catch(SocksException s_ex){
449 | throw s_ex;
450 | }catch(IOException ioe){
451 | throw(new SocksException(SOCKS_PROXY_IO_ERROR,""+ioe));
452 | }
453 | return reply;
454 | }
455 |
456 |
457 | //Private methods
458 | //===============
459 |
460 |
461 | //Constants
462 |
463 | public static final int SOCKS_SUCCESS =0;
464 | public static final int SOCKS_FAILURE =1;
465 | public static final int SOCKS_BADCONNECT =2;
466 | public static final int SOCKS_BADNETWORK =3;
467 | public static final int SOCKS_HOST_UNREACHABLE =4;
468 | public static final int SOCKS_CONNECTION_REFUSED =5;
469 | public static final int SOCKS_TTL_EXPIRE =6;
470 | public static final int SOCKS_CMD_NOT_SUPPORTED =7;
471 | public static final int SOCKS_ADDR_NOT_SUPPORTED =8;
472 |
473 | public static final int SOCKS_NO_PROXY =1<<16;
474 | public static final int SOCKS_PROXY_NO_CONNECT =2<<16;
475 | public static final int SOCKS_PROXY_IO_ERROR =3<<16;
476 | public static final int SOCKS_AUTH_NOT_SUPPORTED =4<<16;
477 | public static final int SOCKS_AUTH_FAILURE =5<<16;
478 | public static final int SOCKS_JUST_ERROR =6<<16;
479 |
480 | public static final int SOCKS_DIRECT_FAILED =7<<16;
481 | public static final int SOCKS_METHOD_NOTSUPPORTED =8<<16;
482 |
483 |
484 | static final int SOCKS_CMD_CONNECT =0x1;
485 | static final int SOCKS_CMD_BIND =0x2;
486 | static final int SOCKS_CMD_UDP_ASSOCIATE =0x3;
487 |
488 | }
489 |
--------------------------------------------------------------------------------
/src/main/java/socks/InetRange.java:
--------------------------------------------------------------------------------
1 | package socks;
2 | import java.net.InetAddress;
3 | import java.net.UnknownHostException;
4 | import java.util.*;
5 |
6 | /**
7 | * Class InetRange provides the means of defining the range of inetaddresses.
8 | * It's used by Proxy class to store and look up addresses of machines, that
9 | * should be contacted directly rather then through the proxy.
10 | *
11 | * InetRange provides several methods to add either standalone addresses, or 12 | * ranges (e.g. 100.200.300.0:100.200.300.255, which covers all addresses 13 | * on on someones local network). It also provides methods for checking wether 14 | * given address is in this range. Any number of ranges and standalone 15 | * addresses can be added to the range. 16 | */ 17 | public class InetRange implements Cloneable{ 18 | 19 | Hashtable host_names; 20 | Vector all; 21 | Vector end_names; 22 | 23 | boolean useSeparateThread = true; 24 | 25 | /** 26 | * Creates the empty range. 27 | */ 28 | public InetRange(){ 29 | all = new Vector(); 30 | host_names = new Hashtable(); 31 | end_names = new Vector(); 32 | } 33 | 34 | /** 35 | * Adds another host or range to this range. 36 | The String can be one of those: 37 |
146 | * Algorithm:
147 | *
11 | SOCKS5 protocol allows to send host address as either: 12 |
22 | NOTE:
23 | Unlike other SOCKS Sockets, it does not support proxy chaining,
24 | and will throw an exception if proxy has a chain proxy attached. The
25 | reason for that is not my laziness, but rather the restrictions of
26 | the SOCKSv5 protocol. Basicaly SOCKSv5 proxy server, needs to know from
27 | which host:port datagrams will be send for association, and returns address
28 | to which datagrams should be send by the client, but it does not
29 | inform client from which host:port it is going to send datagrams, in fact
30 | there is even no guarantee they will be send at all and from the same address
31 | each time.
32 |
33 | */
34 | public class Socks5DatagramSocket extends DatagramSocket{
35 |
36 | InetAddress relayIP;
37 | int relayPort;
38 | Socks5Proxy proxy;
39 | private boolean server_mode = false;
40 | UDPEncapsulation encapsulation;
41 |
42 |
43 | /**
44 | Construct Datagram socket for communication over SOCKS5 proxy
45 | server. This constructor uses default proxy, the one set with
46 | CProxy.setDefaultProxy() method. If default proxy is not set or
47 | it is set to version4 proxy, which does not support datagram
48 | forwarding, throws SocksException.
49 |
50 | */
51 | public Socks5DatagramSocket() throws SocksException,
52 | IOException{
53 | this(CProxy.defaultProxy,0,null);
54 | }
55 | /**
56 | Construct Datagram socket for communication over SOCKS5 proxy
57 | server. And binds it to the specified local port.
58 | This constructor uses default proxy, the one set with
59 | CProxy.setDefaultProxy() method. If default proxy is not set or
60 | it is set to version4 proxy, which does not support datagram
61 | forwarding, throws SocksException.
62 | */
63 | public Socks5DatagramSocket(int port) throws SocksException,
64 | IOException{
65 | this(CProxy.defaultProxy,port,null);
66 | }
67 | /**
68 | Construct Datagram socket for communication over SOCKS5 proxy
69 | server. And binds it to the specified local port and address.
70 | This constructor uses default proxy, the one set with
71 | CProxy.setDefaultProxy() method. If default proxy is not set or
72 | it is set to version4 proxy, which does not support datagram
73 | forwarding, throws SocksException.
74 | */
75 | public Socks5DatagramSocket(int port,InetAddress ip) throws SocksException,
76 | IOException{
77 | this(CProxy.defaultProxy,port,ip);
78 | }
79 |
80 | /**
81 | Constructs datagram socket for communication over specified proxy.
82 | And binds it to the given local address and port. Address of null
83 | and port of 0, signify any availabale port/address.
84 | Might throw SocksException, if:
85 |
143 | See documentation on java.net.DatagramSocket 144 | for full details on how to use this method. 145 | @param dp Datagram to send. 146 | @throws IOException If error happens with I/O. 147 | */ 148 | public void send(DatagramPacket dp) throws IOException{ 149 | //If the host should be accessed directly, send it as is. 150 | if(!server_mode && proxy.isDirect(dp.getAddress())){ 151 | super.send(dp); 152 | //debug("Sending directly:"); 153 | return; 154 | } 155 | 156 | byte[] head = formHeader(dp.getAddress(),dp.getPort()); 157 | byte[] buf = new byte[head.length + dp.getLength()]; 158 | byte[] data = dp.getData(); 159 | //Merge head and data 160 | System.arraycopy(head,0,buf,0,head.length); 161 | //System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength()); 162 | System.arraycopy(data,0,buf,head.length,dp.getLength()); 163 | 164 | if(encapsulation != null) 165 | buf = encapsulation.udpEncapsulate(buf,true); 166 | 167 | super.send(new DatagramPacket(buf,buf.length,relayIP,relayPort)); 168 | } 169 | /** 170 | This method allows to send datagram packets with address type DOMAINNAME. 171 | SOCKS5 allows to specify host as names rather than ip addresses.Using 172 | this method one can send udp datagrams through the proxy, without having 173 | to know the ip address of the destination host. 174 |
175 | If proxy specified for that socket has an option resolveAddrLocally set
176 | to true host will be resolved, and the datagram will be send with address
177 | type IPV4, if resolve fails, UnknownHostException is thrown.
178 | @param dp Datagram to send, it should contain valid port and data
179 | @param host Host name to which datagram should be send.
180 | @throws IOException If error happens with I/O, or the host can't be
181 | resolved when proxy settings say that hosts should be resolved locally.
182 | @see Socks5Proxy#resolveAddrLocally(boolean)
183 | */
184 | public void send(DatagramPacket dp, String host) throws IOException{
185 | if(proxy.isDirect(host)){
186 | dp.setAddress(InetAddress.getByName(host));
187 | super.send(dp);
188 | return;
189 | }
190 |
191 | if(((Socks5Proxy)proxy).resolveAddrLocally){
192 | dp.setAddress(InetAddress.getByName(host));
193 | }
194 |
195 | byte[] head = formHeader(host,dp.getPort());
196 | byte[] buf = new byte[head.length + dp.getLength()];
197 | byte[] data = dp.getData();
198 | //Merge head and data
199 | System.arraycopy(head,0,buf,0,head.length);
200 | //System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength());
201 | System.arraycopy(data,0,buf,head.length,dp.getLength());
202 |
203 | if(encapsulation != null)
204 | buf = encapsulation.udpEncapsulate(buf,true);
205 |
206 | super.send(new DatagramPacket(buf,buf.length,relayIP,relayPort));
207 | }
208 |
209 | /**
210 | * Receives udp packet. If packet have arrived from the proxy relay server,
211 | * it is processed and address and port of the packet are set to the
212 | * address and port of sending host.
213 | * If the packet arrived from anywhere else it is not changed.
214 | * NOTE: DatagramPacket size should be at least 10 bytes bigger
215 | * than the largest packet you expect (this is for IPV4 addresses).
216 | * For hostnames and IPV6 it is even more.
217 | @param dp Datagram in which all relevent information will be copied.
218 | */
219 | public void receive(DatagramPacket dp) throws IOException{
220 | super.receive(dp);
221 |
222 | if(server_mode){
223 | //Drop all datagrams not from relayIP/relayPort
224 | int init_length = dp.getLength();
225 | int initTimeout = getSoTimeout();
226 | long startTime = System.currentTimeMillis();
227 |
228 | while(!relayIP.equals(dp.getAddress()) ||
229 | relayPort != dp.getPort()){
230 |
231 | //Restore datagram size
232 | dp.setLength(init_length);
233 |
234 | //If there is a non-infinit timeout on this socket
235 | //Make sure that it happens no matter how often unexpected
236 | //packets arrive.
237 | if(initTimeout != 0){
238 | int newTimeout = initTimeout - (int)(System.currentTimeMillis() -
239 | startTime);
240 | if(newTimeout <= 0) throw new InterruptedIOException(
241 | "In Socks5DatagramSocket->receive()");
242 | setSoTimeout(newTimeout);
243 | }
244 |
245 | super.receive(dp);
246 | }
247 |
248 | //Restore timeout settings
249 | if(initTimeout != 0) setSoTimeout(initTimeout);
250 |
251 | }else if(!relayIP.equals(dp.getAddress()) ||
252 | relayPort != dp.getPort())
253 | return; // Recieved direct packet
254 | //If the datagram is not from the relay server, return it it as is.
255 |
256 | byte[] data;
257 | data = dp.getData();
258 |
259 | if(encapsulation != null)
260 | data = encapsulation.udpEncapsulate(data,false);
261 |
262 | int offset = 0; //Java 1.1
263 | //int offset = dp.getOffset(); //Java 1.2
264 |
265 | ByteArrayInputStream bIn = new ByteArrayInputStream(data,offset,
266 | dp.getLength());
267 |
268 |
269 | ProxyMessage msg = new Socks5Message(bIn);
270 | dp.setPort(msg.port);
271 | dp.setAddress(msg.getInetAddress());
272 |
273 | //what wasn't read by the Message is the data
274 | int data_length = bIn.available();
275 | //Shift data to the left
276 | System.arraycopy(data,offset+dp.getLength()-data_length,
277 | data,offset,data_length);
278 |
279 |
280 | dp.setLength(data_length);
281 | }
282 |
283 | /**
284 | * Returns port assigned by the proxy, to which datagrams are relayed.
285 | * It is not the same port to which other party should send datagrams.
286 | @return Port assigned by socks server to which datagrams are send
287 | for association.
288 | */
289 | public int getLocalPort(){
290 | if(server_mode) return super.getLocalPort();
291 | return relayPort;
292 | }
293 | /**
294 | * Address assigned by the proxy, to which datagrams are send for relay.
295 | * It is not necesseraly the same address, to which other party should send
296 | * datagrams.
297 | @return Address to which datagrams are send for association.
298 | */
299 | public InetAddress getLocalAddress(){
300 | if(server_mode) return super.getLocalAddress();
301 | return relayIP;
302 | }
303 |
304 | /**
305 | * Closes datagram socket, and proxy connection.
306 | */
307 | public void close(){
308 | if(!server_mode) proxy.endSession();
309 | super.close();
310 | }
311 |
312 | /**
313 | This method checks wether proxy still runs udp forwarding service
314 | for this socket.
315 |
316 | This methods checks wether the primary connection to proxy server 317 | is active. If it is, chances are that proxy continues to forward 318 | datagrams being send from this socket. If it was closed, most likely 319 | datagrams are no longer being forwarded by the server. 320 |
321 | CProxy might decide to stop forwarding datagrams, in which case it 322 | should close primary connection. This method allows to check, wether 323 | this have been done. 324 |
325 | You can specify timeout for which we should be checking EOF condition 326 | on the primary connection. Timeout is in milliseconds. Specifying 0 as 327 | timeout implies infinity, in which case method will block, until 328 | connection to proxy is closed or an error happens, and then return false. 329 |
330 | One possible scenario is to call isProxyactive(0) in separate thread,
331 | and once it returned notify other threads about this event.
332 |
333 | @param timeout For how long this method should block, before returning.
334 | @return true if connection to proxy is active, false if eof or error
335 | condition have been encountered on the connection.
336 | */
337 | public boolean isProxyAlive(int timeout){
338 | if(server_mode) return false;
339 | if(proxy != null){
340 | try{
341 | proxy.proxySocket.setSoTimeout(timeout);
342 |
343 | int eof = proxy.in.read();
344 | if(eof < 0) return false; // EOF encountered.
345 | else return true; // This really should not happen
346 |
347 | }catch(InterruptedIOException iioe){
348 | return true; // read timed out.
349 | }catch(IOException ioe){
350 | return false;
351 | }
352 | }
353 | return false;
354 | }
355 |
356 | //PRIVATE METHODS
357 | //////////////////
358 |
359 |
360 | private byte[] formHeader(InetAddress ip, int port){
361 | Socks5Message request = new Socks5Message(0,ip,port);
362 | request.data[0] = 0;
363 | return request.data;
364 | }
365 |
366 |
367 | private byte[] formHeader(String host,int port){
368 | Socks5Message request = new Socks5Message(0,host,port);
369 | request.data[0] = 0;
370 | return request.data;
371 | }
372 |
373 |
374 | /*======================================================================
375 |
376 | //Mainly Test functions
377 | //////////////////////
378 |
379 | private String bytes2String(byte[] b){
380 | String s="";
381 | char[] hex_digit = { '0','1','2','3','4','5','6','7','8','9',
382 | 'A','B','C','D','E','F'};
383 | for(int i=0;i
78 | SOCKS5 protocol allows to send host names rather then IPs in the
79 | requests, this option controls wether the hostnames should be send
80 | to the proxy server as names, or should they be resolved locally.
81 | @param doResolve Wether to perform resolution locally.
82 | @return Previous settings.
83 | */
84 | public boolean resolveAddrLocally(boolean doResolve){
85 | boolean old = resolveAddrLocally;
86 | resolveAddrLocally = doResolve;
87 | return old;
88 | }
89 | /**
90 | Get current setting on how the addresses should be handled.
91 | @return Current setting for address resolution.
92 | @see Socks5Proxy#resolveAddrLocally(boolean doResolve)
93 | */
94 | public boolean resolveAddrLocally(){
95 | return resolveAddrLocally;
96 | }
97 |
98 | /**
99 | Adds another authentication method.
100 | @param methodId Authentication method id, see rfc1928
101 | @param method Implementation of Authentication
102 | @see Authentication
103 | */
104 | public boolean setAuthenticationMethod(int methodId,
105 | Authentication method){
106 | if(methodId<0 || methodId > 255)
107 | return false;
108 | if(method == null){
109 | //Want to remove a particular method
110 | return (authMethods.remove(new Integer(methodId)) != null);
111 | }else{//Add the method, or rewrite old one
112 | authMethods.put(new Integer(methodId),method);
113 | }
114 | return true;
115 | }
116 |
117 | /**
118 | Get authentication method, which corresponds to given method id
119 | @param methodId Authentication method id.
120 | @return Implementation for given method or null, if one was not set.
121 | */
122 | public Authentication getAuthenticationMethod(int methodId){
123 | Object method = authMethods.get(new Integer(methodId));
124 | if(method == null) return null;
125 | return (Authentication)method;
126 | }
127 |
128 | /**
129 | Creates a clone of this CProxy.
130 | */
131 | public Object clone(){
132 | Socks5Proxy newProxy = new Socks5Proxy(proxyIP,proxyPort);
133 | newProxy.authMethods = (Hashtable) this.authMethods.clone();
134 | newProxy.directHosts = (InetRange)directHosts.clone();
135 | newProxy.resolveAddrLocally = resolveAddrLocally;
136 | newProxy.chainProxy = chainProxy;
137 | return newProxy;
138 | }
139 |
140 | //Public Static(Class) Methods
141 | //==============================
142 |
143 |
144 | //Protected Methods
145 | //=================
146 |
147 | protected CProxy copy(){
148 | Socks5Proxy copy = new Socks5Proxy(proxyIP,proxyPort);
149 | copy.authMethods = this.authMethods; //same Hash, no copy
150 | copy.directHosts = this.directHosts;
151 | copy.chainProxy = this.chainProxy;
152 | copy.resolveAddrLocally = this.resolveAddrLocally;
153 | return copy;
154 | }
155 | /**
156 | *
157 | *
158 | */
159 | protected void startSession()throws SocksException{
160 | super.startSession();
161 | Authentication auth;
162 | Socket ps = proxySocket; //The name is too long
163 |
164 | try{
165 |
166 | byte nMethods = (byte) authMethods.size(); //Number of methods
167 |
168 | byte[] buf = new byte[2+nMethods]; //2 is for VER,NMETHODS
169 | buf[0] = (byte) version;
170 | buf[1] = nMethods; //Number of methods
171 | int i=2;
172 |
173 | Enumeration ids = authMethods.keys();
174 | while(ids.hasMoreElements())
175 | buf[i++] = (byte)((Integer)ids.nextElement()).intValue();
176 |
177 | out.write(buf);
178 | out.flush();
179 |
180 | int versionNumber = in.read();
181 | selectedMethod = in.read();
182 |
183 | if(versionNumber < 0 || selectedMethod < 0){
184 | //EOF condition was reached
185 | endSession();
186 | throw(new SocksException(SOCKS_PROXY_IO_ERROR,
187 | "Connection to proxy lost."));
188 | }
189 | if(versionNumber < version){
190 | //What should we do??
191 | }
192 | if(selectedMethod == 0xFF){ //No method selected
193 | ps.close();
194 | throw ( new SocksException(SOCKS_AUTH_NOT_SUPPORTED));
195 | }
196 |
197 | auth = getAuthenticationMethod(selectedMethod);
198 | if(auth == null){
199 | //This shouldn't happen, unless method was removed by other
200 | //thread, or the server stuffed up
201 | throw(new SocksException(SOCKS_JUST_ERROR,
202 | "Speciefied Authentication not found!"));
203 | }
204 | Object[] in_out = auth.doSocksAuthentication(selectedMethod,ps);
205 | if(in_out == null){
206 | //Authentication failed by some reason
207 | throw(new SocksException(SOCKS_AUTH_FAILURE));
208 | }
209 | //Most authentication methods are expected to return
210 | //simply the input/output streams associated with
211 | //the socket. However if the auth. method requires
212 | //some kind of encryption/decryption being done on the
213 | //connection it should provide classes to handle I/O.
214 |
215 | in = (InputStream) in_out[0];
216 | out = (OutputStream) in_out[1];
217 | if(in_out.length > 2)
218 | udp_encapsulation = (UDPEncapsulation) in_out[2];
219 |
220 | }catch(SocksException s_ex){
221 | throw s_ex;
222 | }catch(UnknownHostException uh_ex){
223 | throw(new SocksException(SOCKS_PROXY_NO_CONNECT));
224 | }catch(SocketException so_ex){
225 | throw(new SocksException(SOCKS_PROXY_NO_CONNECT));
226 | }catch(IOException io_ex){
227 | //System.err.println(io_ex);
228 | throw(new SocksException(SOCKS_PROXY_IO_ERROR,""+io_ex));
229 | }
230 | }
231 |
232 | protected ProxyMessage formMessage(int cmd,InetAddress ip,int port){
233 | return new Socks5Message(cmd,ip,port);
234 | }
235 | protected ProxyMessage formMessage(int cmd,String host,int port)
236 | throws UnknownHostException{
237 | if(resolveAddrLocally)
238 | return formMessage(cmd,InetAddress.getByName(host),port);
239 | else
240 | return new Socks5Message(cmd,host,port);
241 | }
242 | protected ProxyMessage formMessage(InputStream in)
243 | throws SocksException,
244 | IOException{
245 | return new Socks5Message(in);
246 | }
247 |
248 | }
249 |
--------------------------------------------------------------------------------
/src/main/java/socks/SocksException.java:
--------------------------------------------------------------------------------
1 | package socks;
2 |
3 | /**
4 | Exception thrown by various socks classes to indicate errors
5 | with protocol or unsuccessfull server responses.
6 | */
7 | public class SocksException extends java.io.IOException{
8 | /**
9 | Construct a SocksException with given errorcode.
10 |
11 | Tries to look up message which corresponds to this error code.
12 | @param errCode Error code for this exception.
13 | */
14 | public SocksException(int errCode){
15 | this.errCode = errCode;
16 | if((errCode >> 16) == 0){
17 | //Server reply error message
18 | errString = errCode <= serverReplyMessage.length ?
19 | serverReplyMessage[errCode] :
20 | UNASSIGNED_ERROR_MESSAGE;
21 | }else{
22 | //Local error
23 | errCode = (errCode >> 16) -1;
24 | errString = errCode <= localErrorMessage.length ?
25 | localErrorMessage[errCode] :
26 | UNASSIGNED_ERROR_MESSAGE;
27 | }
28 | }
29 | /**
30 | Constructs a SocksException with given error code and message.
31 | @param errCode Error code.
32 | @param errString Error Message.
33 | */
34 | public SocksException(int errCode,String errString){
35 | this.errCode = errCode;
36 | this.errString = errString;
37 | }
38 | /**
39 | Get the error code associated with this exception.
40 | @return Error code associated with this exception.
41 | */
42 | public int getErrorCode(){
43 | return errCode;
44 | }
45 | /**
46 | Get human readable representation of this exception.
47 | @return String represntation of this exception.
48 | */
49 | public String toString(){
50 | return errString;
51 | }
52 |
53 | static final String UNASSIGNED_ERROR_MESSAGE =
54 | "Unknown error message";
55 | static final String serverReplyMessage[] = {
56 | "Succeeded",
57 | "General SOCKS server failure",
58 | "Connection not allowed by ruleset",
59 | "Network unreachable",
60 | "Host unreachable",
61 | "Connection refused",
62 | "TTL expired",
63 | "Command not supported",
64 | "Address type not supported" };
65 |
66 | static final String localErrorMessage[] ={
67 | "SOCKS server not specified",
68 | "Unable to contact SOCKS server",
69 | "IO error",
70 | "None of Authentication methods are supported",
71 | "Authentication failed",
72 | "General SOCKS fault" };
73 |
74 | String errString;
75 | int errCode;
76 |
77 | }//End of SocksException class
78 |
79 |
--------------------------------------------------------------------------------
/src/main/java/socks/SocksServerSocket.java:
--------------------------------------------------------------------------------
1 | package socks;
2 |
3 | import java.net.*;
4 | import java.io.*;
5 |
6 | /**
7 | SocksServerSocket allows to accept connections from one particular
8 | host through the SOCKS4 or SOCKS5 proxy.
9 | */
10 | public class SocksServerSocket extends ServerSocket{
11 | //Data members
12 | protected CProxy proxy;
13 | protected String localHost;
14 | protected InetAddress localIP;
15 | protected int localPort;
16 |
17 | boolean doing_direct = false;
18 | InetAddress remoteAddr;
19 |
20 | /**
21 | * Creates ServerSocket capable of accepting one connection
22 | * through the firewall, uses default CProxy.
23 | *@param host Host from which the connection should be recieved.
24 | *@param port Port number of the primary connection.
25 | */
26 | public SocksServerSocket(String host,int port)
27 | throws SocksException,UnknownHostException,IOException{
28 | this(CProxy.defaultProxy,host,port);
29 | }
30 | /**
31 | *Creates ServerSocket capable of accepting one connection
32 | *through the firewall, uses given proxy.
33 | *@param p CProxy object to use.
34 | *@param host Host from which the connection should be recieved.
35 | *@param port Port number of the primary connection.
36 | */
37 | public SocksServerSocket(CProxy p,String host,int port)
38 | throws SocksException,UnknownHostException,IOException{
39 |
40 |
41 | super(0);
42 | if(p == null) throw new SocksException(CProxy.SOCKS_NO_PROXY);
43 | //proxy=p;
44 | proxy = p.copy();
45 | if(proxy.isDirect(host)){
46 | remoteAddr = InetAddress.getByName(host);
47 | proxy = null;
48 | doDirect();
49 | }else{
50 | processReply(proxy.bind(host,port));
51 | }
52 | }
53 |
54 | /**
55 | * Creates ServerSocket capable of accepting one connection
56 | * through the firewall, uses default CProxy.
57 | *@param ip Host from which the connection should be recieved.
58 | *@param port Port number of the primary connection.
59 | */
60 | public SocksServerSocket(InetAddress ip, int port) throws SocksException,
61 | IOException{
62 | this(CProxy.defaultProxy,ip,port);
63 | }
64 |
65 | /**
66 | *Creates ServerSocket capable of accepting one connection
67 | *through the firewall, uses given proxy.
68 | *@param p CProxy object to use.
69 | *@param ip Host from which the connection should be recieved.
70 | *@param port Port number of the primary connection.
71 | */
72 | public SocksServerSocket(CProxy p,InetAddress ip, int port)
73 | throws SocksException,IOException{
74 | super(0);
75 |
76 | if(p == null) throw new SocksException(CProxy.SOCKS_NO_PROXY);
77 | this.proxy = p.copy();
78 |
79 | if(proxy.isDirect(ip)){
80 | remoteAddr = ip;
81 | doDirect();
82 | }else{
83 | processReply(proxy.bind(ip,port));
84 | }
85 | }
86 |
87 |
88 | /**
89 | * Accepts the incoming connection.
90 | */
91 | public Socket accept() throws IOException{
92 | Socket s;
93 |
94 | if(!doing_direct){
95 | if(proxy == null) return null;
96 |
97 | ProxyMessage msg = proxy.accept();
98 | s = msg.ip == null? new SocksSocket(msg.host,msg.port,proxy)
99 | : new SocksSocket(msg.ip,msg.port,proxy);
100 | //Set timeout back to 0
101 | proxy.proxySocket.setSoTimeout(0);
102 | }else{ //Direct Connection
103 |
104 | //Mimic the proxy behaviour,
105 | //only accept connections from the speciefed host.
106 | while(true){
107 | s = super.accept();
108 | if(s.getInetAddress().equals(remoteAddr)){
109 | //got the connection from the right host
110 | //Close listenning socket.
111 | break;
112 | }else
113 | s.close(); //Drop all connections from other hosts
114 | }
115 |
116 | }
117 | proxy = null;
118 | //Return accepted socket
119 | return s;
120 | }
121 |
122 | /**
123 | * Closes the connection to proxy if socket have not been accepted, if
124 | * the direct connection is used, closes direct ServerSocket. If the
125 | * client socket have been allready accepted, does nothing.
126 | */
127 | public void close() throws IOException{
128 | super.close();
129 | if(proxy != null) proxy.endSession();
130 | proxy = null;
131 | }
132 |
133 | /**
134 | Get the name of the host proxy is using to listen for incoming
135 | connection.
136 |
137 | Usefull when address is returned by proxy as the hostname.
138 | @return the hostname of the address proxy is using to listen
139 | for incoming connection.
140 | */
141 | public String getHost(){
142 | return localHost;
143 | }
144 |
145 | /**
146 | * Get address assigned by proxy to listen for incomming
147 | * connections, or the local machine address if doing direct
148 | * connection.
149 | */
150 | public InetAddress getInetAddress(){
151 | if(localIP == null){
152 | try{
153 | localIP = InetAddress.getByName(localHost);
154 | }catch(UnknownHostException e){
155 | return null;
156 | }
157 | }
158 | return localIP;
159 | }
160 |
161 | /**
162 | * Get port assigned by proxy to listen for incoming connections, or
163 | the port chosen by local system, if accepting directly.
164 | */
165 | public int getLocalPort(){
166 | return localPort;
167 | }
168 |
169 | /**
170 | Set Timeout.
171 |
172 | @param timeout Amount of time in milliseconds, accept should wait for
173 | incoming connection before failing with exception.
174 | Zero timeout implies infinity.
175 | */
176 | public void setSoTimeout(int timeout) throws SocketException{
177 | super.setSoTimeout(timeout);
178 | if(!doing_direct) proxy.proxySocket.setSoTimeout(timeout);
179 | }
180 |
181 |
182 | //Private Methods
183 | //////////////////
184 |
185 | private void processReply(ProxyMessage reply)throws SocksException{
186 | localPort = reply.port;
187 | /*
188 | * If the server have assigned same host as it was contacted on
189 | * it might return an address of all zeros
190 | */
191 | if(reply.host.equals("0.0.0.0")){
192 | localIP = proxy.proxyIP;
193 | localHost = localIP.getHostName();
194 | }else{
195 | localHost = reply.host;
196 | localIP = reply.ip;
197 | }
198 | }
199 |
200 | private void doDirect(){
201 | doing_direct = true;
202 | localPort = super.getLocalPort();
203 | localIP = super.getInetAddress();
204 | localHost = localIP.getHostName();
205 | }
206 |
207 | }
208 |
--------------------------------------------------------------------------------
/src/main/java/socks/SocksSocket.java:
--------------------------------------------------------------------------------
1 | package socks;
2 |
3 | import java.net.*;
4 | import java.io.*;
5 |
6 | /**
7 | * SocksSocket tryies to look very similar to normal Socket,
8 | * while allowing connections through the SOCKS4 or 5 proxy.
9 | * To use this class you will have to identify proxy you need
10 | * to use, CProxy class allows you to set default proxy, which
11 | * will be used by all Socks aware sockets. You can also create
12 | * either Socks4Proxy or Socks5Proxy, and use them by passing to the
13 | * appropriate constructors.
14 | *
15 | * Using Socks package can be as easy as that:
16 | *
17 | *
39 | * However if the need exist for more control, like resolving addresses
40 | * remotely, or using some non-trivial authentication schemes, it can be done.
41 | */
42 |
43 | public class SocksSocket extends Socket{
44 | //Data members
45 | protected CProxy proxy;
46 | protected String localHost, remoteHost;
47 | protected InetAddress localIP, remoteIP;
48 | protected int localPort,remotePort;
49 |
50 | private Socket directSock = null;
51 |
52 | /**
53 | * Tryies to connect to given host and port
54 | * using default proxy. If no default proxy speciefied
55 | * it throws SocksException with error code SOCKS_NO_PROXY.
56 | @param host Machine to connect to.
57 | @param port Port to which to connect.
58 | * @see SocksSocket#SocksSocket(CProxy,String,int)
59 | * @see Socks5Proxy#resolveAddrLocally
60 | */
61 | public SocksSocket(String host,int port)
62 | throws SocksException,UnknownHostException{
63 | this(CProxy.defaultProxy,host,port);
64 | }
65 | /**
66 | * Connects to host port using given proxy server.
67 | @param p CProxy to use.
68 | @param host Machine to connect to.
69 | @param port Port to which to connect.
70 | @throws UnknownHostException
71 | If one of the following happens:
72 |
8 | SOCKSv5 allows for datagrams to be encapsulated for purposes of integrity
9 | and/or authenticity. How it should be done is aggreed during the
10 | authentication stage, and is authentication dependent. This interface is
11 | provided to allow this encapsulation.
12 | @see Authentication
13 | */
14 | public interface UDPEncapsulation{
15 |
16 | /**
17 | This method should provide any authentication depended transformation
18 | on datagrams being send from/to the client.
19 |
20 | @param data Datagram data (including any SOCKS related bytes), to be
21 | encapsulated/decapsulated.
22 | @param out Wether the data is being send out. If true method should
23 | encapsulate/encrypt data, otherwise it should decapsulate/
24 | decrypt data.
25 | @throws IOException if for some reason data can be transformed correctly.
26 | @return Should return byte array containing data after transformation.
27 | It is possible to return same array as input, if transformation
28 | only involves bit mangling, and no additional data is being
29 | added or removed.
30 | */
31 | byte[] udpEncapsulate(byte[] data, boolean out) throws IOException;
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/socks/UDPRelayServer.java:
--------------------------------------------------------------------------------
1 | package socks;
2 | import org.slf4j.Logger;
3 | import org.slf4j.LoggerFactory;
4 | import socks.server.*;
5 | import java.net.*;
6 | import java.io.*;
7 |
8 | /**
9 | UDP Relay server, used by ProxyServer to perform udp forwarding.
10 | */
11 | class UDPRelayServer implements Runnable{
12 | private static final Logger log = LoggerFactory.getLogger(UDPRelayServer.class);
13 |
14 | DatagramSocket client_sock;
15 | DatagramSocket remote_sock;
16 |
17 | Socket controlConnection;
18 |
19 | int relayPort;
20 | InetAddress relayIP;
21 |
22 | Thread pipe_thread1,pipe_thread2;
23 | Thread master_thread;
24 |
25 | ServerAuthenticator auth;
26 |
27 | long lastReadTime;
28 |
29 | static CProxy proxy = null;
30 | static int datagramSize = 0xFFFF;//64K, a bit more than max udp size
31 | static int iddleTimeout = 180000;//3 minutes
32 |
33 |
34 | /**
35 | Constructs UDP relay server to communicate with client
36 | on given ip and port.
37 | @param clientIP Address of the client from whom datagrams
38 | will be recieved and to whom they will be forwarded.
39 | @param clientPort Clients port.
40 | @param master_thread Thread which will be interrupted, when
41 | UDP relay server stoppes for some reason.
42 | @param controlConnection Socket which will be closed, before
43 | interrupting the master thread, it is introduced due to a bug
44 | in windows JVM which does not throw InterruptedIOException in
45 | threads which block in I/O operation.
46 | */
47 | public UDPRelayServer(InetAddress clientIP,int clientPort,
48 | Thread master_thread,
49 | Socket controlConnection,
50 | ServerAuthenticator auth)
51 | throws IOException{
52 | this.master_thread = master_thread;
53 | this.controlConnection = controlConnection;
54 | this.auth = auth;
55 |
56 | client_sock = new Socks5DatagramSocket(true,auth.getUdpEncapsulation(),
57 | clientIP,clientPort);
58 | relayPort = client_sock.getLocalPort();
59 | relayIP = client_sock.getLocalAddress();
60 |
61 | if(relayIP.getHostAddress().equals("0.0.0.0"))
62 | relayIP = InetAddress.getLocalHost();
63 |
64 | if(proxy == null)
65 | remote_sock = new DatagramSocket();
66 | else
67 | remote_sock = new Socks5DatagramSocket(proxy,0,null);
68 | }
69 |
70 |
71 | //Public methods
72 | /////////////////
73 |
74 |
75 | /**
76 | Sets the timeout for UDPRelay server.
133 | Does not close control connection, does not interrupt master_thread.
134 | */
135 | public synchronized void stop(){
136 | master_thread = null;
137 | controlConnection = null;
138 | abort();
139 | }
140 |
141 | //Runnable interface
142 | ////////////////////
143 | public void run(){
144 | try{
145 | if(Thread.currentThread().getName().equals("pipe1"))
146 | pipe(remote_sock,client_sock,false);
147 | else
148 | pipe(client_sock,remote_sock,true);
149 | }catch(IOException ioe){
150 | }finally{
151 | abort();
152 | log.info("UDP Pipe thread {} stopped.", Thread.currentThread().getName());
153 | }
154 |
155 | }
156 |
157 | //Private methods
158 | /////////////////
159 | private synchronized void abort(){
160 | if(pipe_thread1 == null) return;
161 |
162 | log.info("Aborting UDP Relay Server");
163 |
164 | remote_sock.close();
165 | client_sock.close();
166 |
167 | if(controlConnection != null)
168 | try{ controlConnection.close();} catch(IOException ioe){}
169 |
170 | if(master_thread!=null) master_thread.interrupt();
171 |
172 | pipe_thread1.interrupt();
173 | pipe_thread2.interrupt();
174 |
175 | pipe_thread1 = null;
176 | }
177 |
178 | private void pipe(DatagramSocket from,DatagramSocket to,boolean out)
179 | throws IOException{
180 | byte[] data = new byte[datagramSize];
181 | DatagramPacket dp = new DatagramPacket(data,data.length);
182 |
183 | while(true){
184 | try{
185 | from.receive(dp);
186 | lastReadTime = System.currentTimeMillis();
187 |
188 | if(auth.checkRequest(dp,out))
189 | to.send(dp);
190 |
191 | }catch(UnknownHostException uhe){
192 | log.warn("Dropping datagram for unknown host");
193 | }catch(InterruptedIOException iioe){
194 | //log("Interrupted: "+iioe);
195 | //If we were interrupted by other thread.
196 | if(iddleTimeout == 0) return;
197 |
198 | //If last datagram was received, long time ago, return.
199 | long timeSinceRead = System.currentTimeMillis() - lastReadTime;
200 | if(timeSinceRead >= iddleTimeout -100) //-100 for adjustment
201 | return;
202 | }
203 | dp.setLength(data.length);
204 | }
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/src/main/java/socks/UserPasswordAuthentication.java:
--------------------------------------------------------------------------------
1 | package socks;
2 |
3 | /**
4 | SOCKS5 User Password authentication scheme.
5 | */
6 | public class UserPasswordAuthentication implements Authentication{
7 |
8 | /**SOCKS ID for User/Password authentication method*/
9 | public final static int METHOD_ID = 2;
10 |
11 | String userName, password;
12 | byte[] request;
13 |
14 | /**
15 | Create an instance of UserPasswordAuthentication.
16 | @param userName User Name to send to SOCKS server.
17 | @param password Password to send to SOCKS server.
18 | */
19 | public UserPasswordAuthentication(String userName,String password){
20 | this.userName = userName;
21 | this.password = password;
22 | formRequest();
23 | }
24 | /** Get the user name.
25 | @return User name.
26 | */
27 | public String getUser(){
28 | return userName;
29 | }
30 | /** Get password
31 | @return Password
32 | */
33 | public String getPassword(){
34 | return password;
35 | }
36 | /**
37 | Does User/Password authentication as defined in rfc1929.
38 | @return An array containnig in, out streams, or null if authentication
39 | fails.
40 | */
41 | public Object[] doSocksAuthentication(int methodId,
42 | java.net.Socket proxySocket)
43 | throws java.io.IOException{
44 |
45 | if(methodId != METHOD_ID) return null;
46 |
47 | java.io.InputStream in = proxySocket.getInputStream();
48 | java.io.OutputStream out = proxySocket.getOutputStream();
49 |
50 | out.write(request);
51 | int version = in.read();
52 | if(version < 0) return null; //Server closed connection
53 | int status = in.read();
54 | if(status != 0) return null; //Server closed connection, or auth failed.
55 |
56 | return new Object[] {in,out};
57 | }
58 |
59 | //Private methods
60 | //////////////////
61 |
62 | /** Convert UserName password in to binary form, ready to be send to server*/
63 | private void formRequest(){
64 | byte[] user_bytes = userName.getBytes();
65 | byte[] password_bytes = password.getBytes();
66 |
67 | request = new byte[3+user_bytes.length+password_bytes.length];
68 | request[0] = (byte) 1;
69 | request[1] = (byte) user_bytes.length;
70 | System.arraycopy(user_bytes,0,request,2,user_bytes.length);
71 | request[2+user_bytes.length] = (byte) password_bytes.length;
72 | System.arraycopy(password_bytes,0,
73 | request,3+user_bytes.length,password_bytes.length);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/socks/server/Ident.java:
--------------------------------------------------------------------------------
1 | package socks.server;
2 | import socks.*;
3 | import java.net.*;
4 | import java.io.*;
5 | import java.util.StringTokenizer;
6 |
7 | /**
8 | Class Ident provides means to obtain user name of the owner of the socket
9 | on remote machine, providing remote machine runs identd daemon.
10 |
11 | To use it:
12 |
62 | If user name was successfully retrieved successful is set to true,
63 | and userName and hostType are set to whatever server returned. If
64 | however for some reason user name was not obtained, successful is set
65 | to false and errorCode contains the code explaining the reason of
66 | failure, and errorMessage contains human readable explanation.
67 |
68 | Constructor may block, for a while.
69 | @param s Socket whose ownership on remote end should be obtained.
70 | */
71 | public Ident(Socket s ){
72 | Socket sock = null;
73 | successful = false; //We are pessimistic
74 |
75 | try{
76 | sock = new Socket(s.getInetAddress(),113);
77 | sock.setSoTimeout(connectionTimeout);
78 | byte[] request = (""+s.getPort()+" , "+
79 | s.getLocalPort()+"\r\n").getBytes();
80 |
81 | sock.getOutputStream().write(request);
82 |
83 | BufferedReader in = new BufferedReader(
84 | new InputStreamReader(sock.getInputStream()));
85 |
86 | parseResponse(in.readLine());
87 |
88 | }catch(InterruptedIOException iioe){
89 | errorCode = ERR_TIMEOUT;
90 | errorMessage = "Connection to identd timed out.";
91 | }catch(ConnectException ce){
92 | errorCode = ERR_NO_CONNECT;
93 | errorMessage = "Connection to identd server failed.";
94 |
95 | }catch(IOException ioe){
96 | errorCode = ERR_NO_CONNECT;
97 | errorMessage = ""+ioe;
98 | }finally{
99 | try{ if(sock!=null) sock.close();}catch(IOException ioe){};
100 | }
101 | }
102 |
103 | private void parseResponse(String response){
104 | if(response == null){
105 | errorCode = ERR_PROTOCOL_INCORRECT;
106 | errorMessage = "Identd server closed connection.";
107 | return;
108 | }
109 |
110 | StringTokenizer st = new StringTokenizer(response,":");
111 | if(st.countTokens() < 3){
112 | errorCode = ERR_PROTOCOL_INCORRECT;
113 | errorMessage = "Can't parse server response.";
114 | return;
115 | }
116 |
117 | st.nextToken(); //Discard first token, it's basically what we have send
118 | String command = st.nextToken().trim().toUpperCase();
119 |
120 | if(command.equals("USERID") && st.countTokens() >= 2){
121 | successful = true;
122 | hostType = st.nextToken().trim();
123 | userName = st.nextToken("").substring(1);//Get all that is left
124 | }else if(command.equals("ERROR")){
125 | errorCode = ERR_PROTOCOL;
126 | errorMessage = st.nextToken();
127 | }else{
128 | errorCode = ERR_PROTOCOL_INCORRECT;
129 | System.out.println("Opa!");
130 | errorMessage = "Can't parse server response.";
131 | }
132 |
133 |
134 | }
135 |
136 | ///////////////////////////////////////////////
137 | //USED for Testing
138 | /*
139 | public static void main(String[] args) throws IOException{
140 |
141 | Socket s = null;
142 | s = new Socket("gp101-16", 1391);
143 |
144 | Ident id = new Ident(s);
145 | if(id.successful){
146 | System.out.println("User: "+id.userName);
147 | System.out.println("HostType: "+id.hostType);
148 | }else{
149 | System.out.println("ErrorCode: "+id.errorCode);
150 | System.out.println("ErrorMessage: "+id.errorMessage);
151 |
152 | }
153 |
154 | if(s!= null) s.close();
155 | }
156 | //*/
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/src/main/java/socks/server/IdentAuthenticator.java:
--------------------------------------------------------------------------------
1 | package socks.server;
2 | import socks.InetRange;
3 | import socks.ProxyMessage;
4 | import java.util.Hashtable;
5 | import java.util.Vector;
6 | import java.util.Enumeration;
7 | import java.net.*;
8 | import java.io.*;
9 |
10 | /**
11 | An implementation of socks.ServerAuthentication which provides
12 | simple authentication based on the host from which the connection
13 | is made and the name of the user on the remote machine, as reported
14 | by identd daemon on the remote machine.
15 |
16 | It can also be used to provide authentication based only on the contacting
17 | host address.
18 | */
19 |
20 | public class IdentAuthenticator extends ServerAuthenticatorNone{
21 | /** Vector of InetRanges */
22 | Vector hosts;
23 |
24 | /** Vector of user hashes*/
25 | Vector users;
26 |
27 | String user;
28 |
29 |
30 | /**
31 | Constructs empty IdentAuthenticator.
32 | */
33 | public IdentAuthenticator(){
34 | hosts = new Vector();
35 | users = new Vector();
36 | }
37 | /**
38 | Used to create instances returned from startSession.
39 | @param in Input stream.
40 | @param out OutputStream.
41 | @param user Username associated with this connection,could be
42 | null if name was not required.
43 | */
44 | IdentAuthenticator(InputStream in,OutputStream out, String user){
45 | super(in,out);
46 | this.user = user;
47 | }
48 |
49 | /**
50 | Adds range of addresses from which connection is allowed. Hashtable
51 | users should contain user names as keys and anything as values
52 | (value is not used and will be ignored).
53 | @param hostRange Range of ip addresses from which connection is allowed.
54 | @param users Hashtable of users for whom connection is allowed, or null
55 | to indicate that anybody is allowed to connect from the hosts within given
56 | range.
57 | */
58 | public synchronized void add(InetRange hostRange,Hashtable users){
59 | this.hosts.addElement(hostRange);
60 | this.users.addElement(users);
61 | }
62 |
63 | /**
64 | Grants permission only to those users, who connect from one of the
65 | hosts registered with add(InetRange,Hashtable) and whose names, as
66 | reported by identd daemon, are listed for the host the connection
67 | came from.
68 | */
69 | public ServerAuthenticator startSession(Socket s)
70 | throws IOException{
71 |
72 | int ind = getRangeIndex(s.getInetAddress());
73 | String user = null;
74 |
75 | //System.out.println("getRangeReturned:"+ind);
76 |
77 | if(ind < 0) return null; //Host is not on the list.
78 |
79 | ServerAuthenticatorNone auth = (ServerAuthenticatorNone)
80 | super.startSession(s);
81 |
82 | //System.out.println("super.startSession() returned:"+auth);
83 | if(auth == null) return null;
84 |
85 | //do the authentication
86 |
87 | Hashtable user_names = (Hashtable) users.elementAt(ind);
88 |
89 | if(user_names != null){ //If need to do authentication
90 | Ident ident;
91 | ident = new Ident(s);
92 | //If can't obtain user name, fail
93 | if(!ident.successful) return null;
94 | //If user name is not listed for this address, fail
95 | if(!user_names.containsKey(ident.userName)) return null;
96 | user = ident.userName;
97 | }
98 | return new IdentAuthenticator(auth.in,auth.out,user);
99 |
100 | }
101 | /**
102 | For SOCKS5 requests allways returns true. For SOCKS4 requests
103 | checks wether the user name supplied in the request corresponds
104 | to the name obtained from the ident daemon.
105 | */
106 | public boolean checkRequest(ProxyMessage msg,java.net.Socket s){
107 | //If it's version 5 request, or if anybody is permitted, return true;
108 | if(msg.version == 5 || user == null)
109 | return true;
110 |
111 | if(msg.version != 4) return false; //Who knows?
112 |
113 | return user.equals(msg.user);
114 | }
115 |
116 | /** Get String representaion of the IdentAuthenticator.*/
117 | public String toString(){
118 | String s = "";
119 |
120 | for(int i=0;i
28 | If authentication was successful an instance of ServerAuthentication
29 | should be returned, it later will be used by the server to perform
30 | authorization and some other things. If authentication fails null should
31 | be returned, or an exception may be thrown.
32 |
33 | @param s Accepted Socket.
34 | @return An instance of ServerAuthenticator to be used for this connection
35 | or null
36 | */
37 | ServerAuthenticator startSession(Socket s) throws IOException;
38 |
39 | /**
40 | This method should return input stream which should be used on the
41 | accepted socket.
42 |
43 | SOCKSv5 allows to have multiple authentication methods, and these methods
44 | might require some kind of transformations being made on the data.
45 |
46 | This method is called on the object returned from the startSession
47 | function.
48 | */
49 | InputStream getInputStream();
50 | /**
51 | This method should return output stream to use to write to the accepted
52 | socket.
53 |
54 | SOCKSv5 allows to have multiple authentication methods, and these methods
55 | might require some kind of transformations being made on the data.
56 |
57 | This method is called on the object returned from the startSession
58 | function.
59 | */
60 | OutputStream getOutputStream();
61 |
62 | /**
63 | This method should return UDPEncapsulation, which should be used
64 | on the datagrams being send in/out.
65 |
66 | If no transformation should be done on the datagrams, this method
67 | should return null.
68 |
69 | This method is called on the object returned from the startSession
70 | function.
71 | */
72 |
73 | UDPEncapsulation getUdpEncapsulation();
74 |
75 | /**
76 | This method is called when a request have been read.
77 |
78 | Implementation should decide wether to grant request or not. Returning
79 | true implies granting the request, false means request should be rejected.
80 |
81 | This method is called on the object returned from the startSession
82 | function.
83 | @param msg Request message.
84 | @return true to grant request, false to reject it.
85 | */
86 | boolean checkRequest(ProxyMessage msg);
87 |
88 | /**
89 | This method is called when datagram is received by the server.
90 |
91 | Implementaions should decide wether it should be forwarded or dropped.
92 | It is expecteed that implementation will use datagram address and port
93 | information to make a decision, as well as anything else. Address and
94 | port of the datagram are always correspond to remote machine. It is
95 | either destination or source address. If out is true address is destination
96 | address, else it is a source address, address of the machine from which
97 | datagram have been received for the client.
98 |
99 | Implementaions should return true if the datagram is to be forwarded, and
100 | false if the datagram should be dropped.
101 |
102 | This method is called on the object returned from the startSession
103 | function.
104 |
105 | @param out If true the datagram is being send out(from the client),
106 | otherwise it is an incoming datagram.
107 | @return True to forward datagram false drop it silently.
108 | */
109 | boolean checkRequest(DatagramPacket dp, boolean out);
110 |
111 | /**
112 | This method is called when session is completed. Either due to normal
113 | termination or due to any error condition.
114 |
115 | This method is called on the object returned from the startSession
116 | function.
117 | */
118 | void endSession();
119 | }
120 |
--------------------------------------------------------------------------------
/src/main/java/socks/server/ServerAuthenticatorNone.java:
--------------------------------------------------------------------------------
1 | package socks.server;
2 | import socks.ProxyMessage;
3 | import socks.UDPEncapsulation;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.DataInputStream;
8 | import java.io.OutputStream;
9 | import java.io.PushbackInputStream;
10 | import java.net.Socket;
11 |
12 | /**
13 | An implementation of ServerAuthenticator, which does not do
14 | any authentication.
15 |
16 | Warning!!
19 | It is only provided to make implementing other authentication schemes
20 | easier.
131 | This method reads in authentication methods that client supports,
132 | checks wether it supports given method. If it does, the notification
133 | method is written back to client, that this method have been chosen
134 | for authentication. If given method was not found, authentication
135 | failure message is send to client ([5,FF]).
136 | @param in Input stream, version byte should be removed from the stream
137 | before calling this method.
138 | @param out Output stream.
139 | @param methodId Method which should be selected.
140 | @return true if methodId was found, false otherwise.
141 | */
142 | static public boolean selectSocks5Authentication(InputStream in,
143 | OutputStream out,
144 | int methodId)
145 | throws IOException{
146 |
147 | int num_methods = in.read();
148 | if (num_methods <= 0) return false;
149 | byte method_ids[] = new byte[num_methods];
150 | byte response[] = new byte[2];
151 | boolean found = false;
152 |
153 | response[0] = (byte) 5; //SOCKS version
154 | response[1] = (byte) 0xFF; //Not found, we are pessimistic
155 |
156 | int bread = 0; //bytes read so far
157 | while(bread < num_methods)
158 | bread += in.read(method_ids,bread,num_methods-bread);
159 |
160 | for(int i=0;i
38 | When connection is accepted, speciefied service is performed.
39 | It is being done in separate thread.
40 | @return Never returns.
41 | */
42 | static public void server(int service) throws IOException{
43 | ServerSocket ss = new ServerSocket(TestService.servicePorts[service]);
44 | Socket s;
45 |
46 | s = ss.accept();
47 | while(s!=null){
48 | TestService st = new TestService(s,service);
49 | Thread t = new Thread(st);
50 | t.start();
51 | s = ss.accept();
52 | }
53 | }
54 |
55 |
56 | /**
57 | Performs logging.
58 | */
59 | static synchronized void log(String s){
60 | if(log != null) log.println(s);
61 | }
62 |
63 | //Main Function
64 | ///////////////
65 | public static void main(String[] args){
66 | log = System.out;
67 | TestService.log = log;
68 |
69 | TestServer st;
70 | for( int i = 0; i< TestService.serviceNames.length;++i){
71 | log("Starting service "+TestService.serviceNames[i]+" at port "+
72 | TestService.servicePorts[i]+".");
73 | st = new TestServer(i);
74 | Thread t = new Thread(st);
75 | t.start();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/test/java/TestService.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.net.*;
3 |
4 | /**
5 | Server to used perform tests for SOCKS library.
6 | */
7 | public class TestService implements Runnable{
8 | static final String chargenSequence = " !\"#$%&'()*+,-./0123456789:;<=>?@"+
9 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefg";
10 |
11 | static final String serviceNames[] = {"echo","discard","chargen","connect"};
12 | static final int servicePorts[] = {5678,5679,5680,5681};
13 |
14 | static final int ECHO = 0;
15 | static final int DISCARD = 1;
16 | static final int CHARGEN = 2;
17 | static final int CONNECT = 3;
18 |
19 | static final int BUF_SIZE = 1024;
20 |
21 | static final int CHARGEN_WAIT = 1000; //1 second
22 | static final int MAX_WAIT = 60000; //1 minute
23 |
24 | static PrintStream log = null;
25 |
26 | //Class constants
27 | Socket s;
28 | int service;
29 |
30 | /**
31 | Creates new TestService object, which will perform particular
32 | service on given socket.
33 |
34 | @param s Socket on which to perform service.
35 | @param service Service which to provide.
36 | */
37 | public TestService(Socket s, int service){
38 | this.s = s;
39 | this.service = service;
40 | }
41 |
42 | /**
43 | Default constructor.
44 | */
45 | public TestService(){
46 | this.s = null;
47 | this.service = -1;
48 | }
49 |
50 | public void run(){
51 | try{
52 | serve(s,service);
53 | }catch(IOException ioe){
54 | log("Exception:"+ioe);
55 | ioe.printStackTrace();
56 | }
57 | try{ s.close();}catch(IOException ioe){}
58 | }
59 |
60 | //Static functions
61 | /////////////////
62 |
63 | /**
64 | Maps service name to the integer id, does it in simple
65 | linear search manner.
66 | @param serviceName Name of the service whose id one needs.
67 | @return Integer identifier for this servuce, or -1, if service
68 | can't be found.
69 | */
70 | static public int getServiceId(String serviceName){
71 | serviceName = serviceName.toLowerCase();
72 | for(int i = 0;i < serviceNames.length;++i)
73 | if(serviceName.equals(serviceNames[i]))
74 | return i;
75 |
76 | //Couldn't find one.
77 | return -1;
78 | }
79 |
80 | /**
81 | Performs given service on given socket.
82 |
83 | Simply looks up and calls associated method.
84 | @param s Socket on which to perform service.
85 | @param service Id of the service to perform.
86 | @return true if service have been found, false otherwise.
87 | */
88 | static public boolean serve(Socket s, int service) throws IOException{
89 | switch(service){
90 | case ECHO:
91 | echo(s);
92 | break;
93 | case DISCARD:
94 | discard(s);
95 | break;
96 | case CHARGEN:
97 | chargen(s,CHARGEN_WAIT,MAX_WAIT);
98 | break;
99 | case CONNECT:
100 | connect(s);
101 | break;
102 | default:
103 | log("Unknown service:"+service);
104 | return false;
105 | }
106 | return true;
107 | }
108 | /**
109 | Echos any input on the socket to the output.
110 | Echo is being done line by line.
111 | @param s Socket on which to perform service.
112 | */
113 | static public void echo(Socket s) throws IOException{
114 | BufferedReader in = new BufferedReader(new InputStreamReader(
115 | s.getInputStream()));
116 | OutputStream out = s.getOutputStream();
117 |
118 | log("Starting \"echo\" on "+s);
119 |
120 | String line = in.readLine();
121 | while(line != null){
122 | out.write((line+"\n").getBytes());
123 | log(line);
124 | line = in.readLine();
125 | }
126 |
127 | log("Echo done.");
128 | }
129 |
130 | /**
131 | Reads input from the socket, and does not write anything back.
132 | logs input in line by line fashion.
133 | @param s Socket on which to perform service.
134 | */
135 | static public void discard(Socket s) throws IOException{
136 | BufferedReader in = new BufferedReader(new InputStreamReader(
137 | s.getInputStream()));
138 | log("Starting discard on "+s);
139 |
140 | String line = in.readLine();
141 | while(line != null){
142 | log(line);
143 | line = in.readLine();
144 | }
145 |
146 | log("Discard finished.");
147 | }
148 |
149 | /**
150 | Generates characters and sends them to the socket.
151 |
152 | Unlike usual chargen (port 19), each next line of the generated
153 | output is send after increasingly larger time intervals. It starts
154 | from wait_time (ms), and each next time wait time is doubled.
155 | Eg. 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 ... well
156 | you got the idea.
157 |
158 | It starts if either connection is clsoed or the wait time grows
159 | bigger than max_wait.
160 |
161 | @param s Socket on which to perform service.
162 | @param wait_time Time in ms, from which timing sequence should begin.
163 | @param max_wait Time in ms, after reaching timeout greater than this
164 | value, chargen will stop.
165 | */
166 | static public void chargen(Socket s,long wait_time,long max_wait)
167 | throws IOException{
168 | byte[] buf = chargenSequence.getBytes();
169 | int pos = 0;
170 | OutputStream out = s.getOutputStream();
171 | InputStream in = s.getInputStream();
172 | s.setSoTimeout(100); //0.1 ms
173 |
174 | log("Starting \"chargen\" on "+s);
175 | while(true){
176 | log("Sending message.");
177 | out.write(buf,pos,buf.length - pos);
178 | out.write(buf,0,pos);
179 | out.write("\n".getBytes());
180 | pos++;
181 | try{
182 | if(wait_time > max_wait) break;
183 |
184 | log("Going to sleep for "+wait_time+" ms.");
185 | Thread.currentThread().sleep(wait_time);
186 | wait_time *= 2;
187 | if(in.read() < 0) break; //Connection closed
188 | }catch(InterruptedException ie){
189 | }catch(InterruptedIOException ioe){
190 | }
191 | }
192 | log("Chargen finished.");
193 | }
194 |
195 | /**
196 | Models connect back situation.
197 |
198 | Reads a line from the socket connection, line should be in the
199 | form port service_id. Connects back to the remote host to port
200 | specified in the line, if successful performs a service speciefied
201 | by id on that new connection. If accept was successful closes the
202 | control connection, else outputs error message, and then closes
203 | the connection.
204 |
205 | @param s Control connection.
206 | */
207 | static public void connect(Socket s)throws IOException{
208 | String line = null;
209 | Socket sock;
210 | int port;
211 | int service_id;
212 |
213 | BufferedReader in = new BufferedReader(new InputStreamReader(
214 | s.getInputStream()));
215 | OutputStream out = s.getOutputStream();
216 |
217 | log("Starting \"connect\" on "+s);
218 | line = in.readLine();
219 | if(line == null) return; //They closed connection
220 |
221 | java.util.StringTokenizer st = new java.util.StringTokenizer(line);
222 | if(st.countTokens() < 2){ //We need at least 'port' and "id"
223 | out.write("Expect: port serviceId.\n".getBytes());
224 | log("Invalid arguments.");
225 | return;
226 | }
227 | try{
228 | port = Integer.parseInt(st.nextToken());
229 | service_id = Integer.parseInt(st.nextToken());
230 | }catch(NumberFormatException nfe){
231 | out.write("Expect: port serviceId.\n".getBytes());
232 | log("Invalid arguments.");
233 | return;
234 | }
235 | try{
236 | log("Connecting to "+s.getInetAddress()+":"+port);
237 | sock = new Socket(s.getInetAddress(),port);
238 | }catch(IOException ioe){
239 | out.write(("Connect to "+s.getInetAddress()+
240 | ":"+port+" failed").getBytes());
241 | log("Connect failed.");
242 | return;
243 | }
244 | s.close();
245 | log("About to serve "+service_id);
246 | serve(sock,service_id);
247 | }
248 |
249 | /**
250 | Pipes data from the input stream to the output.
251 | @param in Input stream.
252 | @param out Output stream.
253 | */
254 | static public void pipe(InputStream in, OutputStream out)
255 | throws IOException{
256 | byte[] buf = new byte[BUF_SIZE];
257 | int bread = 0;
258 | while(bread >= 0){
259 | bread = in.read(buf);
260 | out.write(buf,0,bread);
261 | }
262 | }
263 |
264 | /**
265 | Performs logging.
266 | */
267 | static synchronized void log(String s){
268 | if(log != null) log.println(s);
269 | }
270 |
271 | }
272 |
--------------------------------------------------------------------------------
/src/test/java/UDPEcho.java:
--------------------------------------------------------------------------------
1 | import java.net.*;
2 | import java.io.*;
3 | /**
4 | Plain SOCKS unaware UDP echo server and client.
5 | */
6 | public class UDPEcho implements Runnable{
7 |
8 | private int port;
9 | private InetAddress hostIP;
10 | private DatagramSocket sock;
11 |
12 | private static final int BUF_SIZE = 1024;
13 |
14 |
15 | public UDPEcho(String host,int port)
16 | throws IOException,UnknownHostException{
17 | this.hostIP = InetAddress.getByName(host);
18 | this.port = port;
19 | sock = new DatagramSocket();
20 | System.out.println("UDP: "+sock.getLocalAddress()+":"+
21 | sock.getLocalPort());
22 | //sock.connect(hostIP,port);
23 | }
24 |
25 | public void send(String s) throws IOException{
26 | System.out.println("Sending:"+s);
27 | DatagramPacket packet = new DatagramPacket(s.getBytes(),
28 | s.length(),
29 | hostIP,
30 | port);
31 | sock.send(packet);
32 | }
33 |
34 | public void run(){
35 | byte[] buf = new byte[BUF_SIZE];
36 | DatagramPacket incomingData = new DatagramPacket(buf,buf.length);
37 | try{
38 | while(true){
39 | sock.receive(incomingData);
40 | System.out.println("UDP From:"+
41 | incomingData.getAddress().getHostAddress()+":"+
42 | incomingData.getPort());
43 | System.out.println(new String(incomingData.getData(),
44 | 0,incomingData.getLength()));
45 | System.out.flush();
46 | incomingData.setLength(buf.length);
47 | }
48 | }catch(IOException io_ex){
49 | io_ex.printStackTrace();
50 | }
51 | }
52 |
53 | public static void usage(){
54 | System.err.print(
55 | "Usage: java UDPEcho host port\n"+
56 | "OR\n"+
57 | "Usage: java UDPEcho port\n");
58 | }
59 |
60 | public static void doEcho(int port)throws IOException{
61 | byte[] buf = new byte[BUF_SIZE];
62 | DatagramPacket packet = new DatagramPacket(buf,buf.length);
63 | DatagramSocket sock = new DatagramSocket(port);
64 |
65 | System.out.println("Starting UDP echo on"+
66 | sock.getLocalAddress().getHostAddress()+
67 | ":"+sock.getLocalPort());
68 | while(true){
69 | try{
70 | sock.receive(packet);
71 | sock.send(packet);
72 | System.out.print(
73 | "UDP From: "+packet.getAddress().getHostAddress()+":"+
74 | packet.getPort()+
75 | "\n"+
76 | new String(packet.getData(),0,packet.getLength())+
77 | "\n"
78 | );
79 | System.out.flush();
80 |
81 | packet.setLength(buf.length);
82 | //packet = new DatagramPacket(buf,buf.length);
83 | }catch(IOException io_ex){
84 | }
85 | }
86 | }
87 |
88 | public static void main(String args[]){
89 | int port;
90 | String host;
91 |
92 | if(args.length == 1){
93 | try{
94 | port = Integer.parseInt(args[0]);
95 |
96 | doEcho(port);
97 |
98 | }catch(IOException io_ex){
99 | io_ex.printStackTrace();
100 | System.exit(1);
101 |
102 | }catch(NumberFormatException num_ex){
103 | num_ex.printStackTrace();
104 | System.exit(1);
105 | }
106 | }else if(args.length == 2){
107 | try{
108 | host = args[0];
109 | port = Integer.parseInt(args[1]);
110 |
111 | UDPEcho ut = new UDPEcho(host,port);
112 | Thread thread = new Thread(ut);
113 | thread.start();
114 |
115 | BufferedReader in = new BufferedReader(
116 | new InputStreamReader(System.in));
117 | String s;
118 | System.out.print("Enter datagram:");
119 | s = in.readLine();
120 | while(s != null){
121 | ut.send(s);
122 | try{
123 | Thread.currentThread().sleep(100);
124 | }catch(InterruptedException i_ex){
125 | }
126 | System.out.print("Enter datagram:");
127 | s = in.readLine();
128 | }
129 | System.exit(1);
130 |
131 | }catch(IOException io_ex){
132 | io_ex.printStackTrace();
133 | System.exit(1);
134 | }catch(NumberFormatException num_ex){
135 | num_ex.printStackTrace();
136 | System.exit(1);
137 | }
138 |
139 | }else{
140 | usage();
141 | }
142 | }
143 |
144 | }//End of class
145 |
--------------------------------------------------------------------------------
/src/test/java/UPSOCKS.java:
--------------------------------------------------------------------------------
1 | import socks.*;
2 | import socks.server.*;
3 | import java.net.Socket;
4 |
5 | /** Test file for UserPasswordAuthentictor */
6 |
7 | public class UPSOCKS implements UserValidation{
8 | String user, password;
9 |
10 | UPSOCKS(String user,String password){
11 | this.user = user;
12 | this.password = password;
13 | }
14 |
15 | public boolean isUserValid(String user,String password,Socket s){
16 | System.err.println("User:"+user+"\tPassword:"+password);
17 | System.err.println("Socket:"+s);
18 | return (user.equals(this.user) && password.equals(this.password));
19 | }
20 |
21 | public static void main(String args[]){
22 | String user, password;
23 |
24 | if(args.length == 2){
25 | user = args[0];
26 | password = args[1];
27 | }else{
28 | user = "user";
29 | password = "password";
30 | }
31 |
32 | UPSOCKS us = new UPSOCKS(user,password);
33 | UserPasswordAuthenticator auth = new UserPasswordAuthenticator(us);
34 | ProxyServer server = new ProxyServer(auth);
35 |
36 | server.start(1080);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/resources/SocksEcho.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jitsi/jsocks/6365edb93e2c9193f26201a7dec9cac684938490/src/test/resources/SocksEcho.gif
--------------------------------------------------------------------------------
/src/test/resources/socks.properties:
--------------------------------------------------------------------------------
1 | #SOCKS server initialisation file.
2 |
3 |
4 |
5 |
6 |
7 | #Port on which the socks server should run
8 |
9 | #If not set deafults to 1080
10 |
11 | port = 1080
12 |
13 |
14 |
15 | #Timeout settings for the SOCKS server,in milliseconds.
16 |
17 | #If not defined, all default to 3 minutes(18000ms).
18 |
19 | #
20 |
21 | # iddleTimeout If no data received from/to user during this interval
22 |
23 | # connection will be aborted.
24 |
25 | # acceptTimeout If no connection is accepted during this interval,
26 |
27 | # connection will be aborted.
28 |
29 | # udpTimeout If no datagrams are received from/to user, in this interval
30 |
31 | # UDP relay server stops, and control connection is closed.
32 |
33 | # Any of these can be 0, implying infinit timeout, that is once the
34 |
35 | # connection is made, it is kept alive until one of the parties closes it.
36 |
37 | # In case of the BIND command, it implies that server will be listenning
38 |
39 | # for incoming connection until it is accepted, or until client closes
40 |
41 | # control connection.
42 |
43 | # For UDP servers it implies, that they will run as long, as client
44 |
45 | # keeps control connection open.
46 |
47 |
48 |
49 | iddleTimeout = 600000 # 10 minutes
50 |
51 | acceptTimeout = 60000 # 1 minute
52 |
53 | udpTimeout = 600000 # 10 minutes
54 |
55 |
56 |
57 | #datagramSize -- Size of the datagrams to use for udp relaying.
58 |
59 | #Defaults to 64K bytes(0xFFFF = 65535 a bit more than maximum possible size).
60 |
61 | #datagramSize = 8192
62 |
63 |
64 |
65 | #log -- Name of the file, to which logging should be done
66 |
67 | # If log is - (minus sine) do logging to standart output.
68 |
69 | #Optional field, if not defined, no logging is done.
70 |
71 | #
72 |
73 |
74 |
75 | #log = -
76 |
77 |
78 |
79 | #host -- Host on which to run, for multihomed hosts,
80 |
81 | # Default -- all(system dependent)
82 |
83 | #host = some.hostOfMine.com
84 |
85 |
86 |
87 | #range -- Semicolon(;) separated range of addresses, from which
88 |
89 | #connections should be accepted.
90 |
91 | #
92 |
93 | # Range could be one of those
94 |
95 | # 1. Stand alone host name -- some.host.com or 33.33.44.101
96 |
97 | # 2. Host range
98 |
99 | # .my.domain.net
100 |
101 | # 190.220.34.
102 |
103 | # host1:host2
104 |
105 | # 33.44.100:33.44.200
106 |
107 | #
108 |
109 | # Example: .myDomain.com;100.220.30.;myHome.host.com;\
110 |
111 | # comp1.myWork.com:comp10.myWork.com
112 |
113 | #
114 |
115 | # This will include all computers in the domain myDomain.com,
116 |
117 | # all computers whose addresses start with 100.200.30,
118 |
119 | # host with the name myHome.host.com,
120 |
121 | # and computers comp1 through to comp2 in the domain myWork.com,
122 |
123 | # assuming their names correspond to there ip addresses.
124 |
125 | #
126 |
127 | # NOTE: Dot(.) by itself implies all hosts, be sure not to include
128 |
129 | # one of those.
130 |
131 | range = localhost
132 |
133 |
134 |
135 | #users
136 |
137 | # Semicolon(;) separated list of users, for whom permissions should be
138 |
139 | # granted, given they connect from one of the hosts speciefied by range.
140 |
141 | # This field is optional, if not defined, ANY user will be allowed to use
142 |
143 | # SOCKS server, given he\she is connecting from one of the hosts in the
144 |
145 | # range.
146 |
147 | # NOTE: Whitespaces are not ignored (except for the first name, it's how java
148 |
149 | # parses Property files).
150 |
151 | # User names are CASESenSitive.
152 |
153 | # You have been warned.
154 |
155 | # NOTE2: Misspelling users with Users, or anything, will be understood as
156 |
157 | # if users were not defined, and hence will imply that ANYBODY, will
158 |
159 | # be granted access from the hosts in the range.
160 |
161 | #
162 |
163 |
164 |
165 | # users = sockuser:sockpassword
166 |
167 |
168 |
169 | # Proxy configurations, that is what proxy this proxy should use, if any.
170 |
171 | # proxy should be a semicolon(;) separated list of proxy entries.
172 |
173 | # Each entry shoud be in the form: host[:port:user:password].
174 |
175 | # If only host is supplied SOCKSv5 proxy is assumed, running on port 1080.
176 |
177 | # If user is supplied, but password not supplied, SOCKSv4 is assumed,
178 |
179 | # running oon machine 'host' on port 'port', user name will be supplied as
180 |
181 | # authentication for that proxy.
182 |
183 | # If both user and password is supplied, SOCKSv5 proxy is assumed, with
184 |
185 | # user/password authentication.
186 |
187 | #
188 |
189 | # directHosts should contain ;-separated list of inetaddress and ranges.
190 |
191 | # These machines will be addressed directly rather than through
192 |
193 | # the proxy. See range for more details, what sort of entries
194 |
195 | # permitted and understood.
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 | # proxy = www-proxy:1080
204 |
205 | # directHosts = 130.220.;.unisa.edu.au;localhost
206 |
207 | #logfile, '-' for stdout
208 | log=-
209 |
--------------------------------------------------------------------------------
18 | *
19 | * import Socks.*;
20 | * ....
21 | *
22 | * try{
23 | * //Specify SOCKS5 proxy
24 | * CProxy.setDefaultProxy("socks-proxy",1080);
25 | *
26 | * //OR you still use SOCKS4
27 | * //Code below uses SOCKS4 proxy
28 | * //CProxy.setDefaultProxy("socks-proxy",1080,userName);
29 | *
30 | * Socket s = SocksSocket("some.host.of.mine",13);
31 | * readTimeFromSock(s);
32 | * }catch(SocksException sock_ex){
33 | * //Usually it will turn in more or less meaningfull message
34 | * System.err.println("SocksException:"+sock_ex);
35 | * }
36 | *
37 | *
38 | *
73 |
74 |
79 | @throws SocksException
80 | If one of the following happens:
81 |
82 |
92 | @throws IOexception if anything is wrong with I/O.
93 | @see Socks5Proxy#resolveAddrLocally
94 | */
95 | public SocksSocket(CProxy p,String host,int port)
96 | throws SocksException,UnknownHostException{
97 |
98 |
99 | if(p == null) throw new SocksException(CProxy.SOCKS_NO_PROXY);
100 | //proxy=p;
101 | proxy = p.copy();
102 | remoteHost = host;
103 | remotePort = port;
104 | if(proxy.isDirect(host)){
105 | remoteIP = InetAddress.getByName(host);
106 | doDirect();
107 | }
108 | else
109 | processReply(proxy.connect(host,port));
110 | }
111 |
112 |
113 | /**
114 | * Tryies to connect to given ip and port
115 | * using default proxy. If no default proxy speciefied
116 | * it throws SocksException with error code SOCKS_NO_PROXY.
117 | @param ip Machine to connect to.
118 | @param port Port to which to connect.
119 | * @see SocksSocket#SocksSocket(CProxy,String,int)
120 | */
121 | public SocksSocket(InetAddress ip, int port) throws SocksException{
122 | this(CProxy.defaultProxy,ip,port);
123 | }
124 |
125 | /**
126 | Connects to given ip and port using given CProxy server.
127 | @param p CProxy to use.
128 | @param ip Machine to connect to.
129 | @param port Port to which to connect.
130 |
131 | */
132 | public SocksSocket(CProxy p,InetAddress ip, int port) throws SocksException{
133 | if(p == null) throw new SocksException(CProxy.SOCKS_NO_PROXY);
134 | this.proxy = p.copy();
135 | this.remoteIP = ip;
136 | this.remotePort = port;
137 | this.remoteHost = ip.getHostName();
138 | if(proxy.isDirect(remoteIP))
139 | doDirect();
140 | else
141 | processReply(proxy.connect(ip,port));
142 | }
143 |
144 |
145 | /**
146 | * These 2 constructors are used by the SocksServerSocket.
147 | * This socket simply overrides remoteHost, remotePort
148 | */
149 | protected SocksSocket(String host,int port,CProxy proxy){
150 | this.remotePort = port;
151 | this.proxy = proxy;
152 | this.localIP = proxy.proxySocket.getLocalAddress();
153 | this.localPort = proxy.proxySocket.getLocalPort();
154 | this.remoteHost = host;
155 | }
156 | protected SocksSocket(InetAddress ip,int port,CProxy proxy){
157 | remoteIP = ip;
158 | remotePort = port;
159 | this.proxy = proxy;
160 | this.localIP = proxy.proxySocket.getLocalAddress();
161 | this.localPort = proxy.proxySocket.getLocalPort();
162 | remoteHost = remoteIP.getHostName();
163 | }
164 |
165 | /**
166 | * Same as Socket
167 | */
168 | public void close() throws IOException{
169 | if(proxy!= null)proxy.endSession();
170 | proxy = null;
171 | }
172 | /**
173 | * Same as Socket
174 | */
175 | public InputStream getInputStream(){
176 | return proxy.in;
177 | }
178 | /**
179 | * Same as Socket
180 | */
181 | public OutputStream getOutputStream(){
182 | return proxy.out;
183 | }
184 | /**
185 | * Same as Socket
186 | */
187 | public int getPort(){
188 | return remotePort;
189 | }
190 | /**
191 | * Returns remote host name, it is usefull in cases when addresses
192 | * are resolved by proxy, and we can't create InetAddress object.
193 | @return The name of the host this socket is connected to.
194 | */
195 | public String getHost(){
196 | return remoteHost;
197 | }
198 | /**
199 | * Get remote host as InetAddress object, might return null if
200 | * addresses are resolved by proxy, and it is not possible to resolve
201 | * it locally
202 | @return Ip address of the host this socket is connected to, or null
203 | if address was returned by the proxy as DOMAINNAME and can't be
204 | resolved locally.
205 | */
206 | public InetAddress getInetAddress(){
207 | if(remoteIP == null){
208 | try{
209 | remoteIP = InetAddress.getByName(remoteHost);
210 | }catch(UnknownHostException e){
211 | return null;
212 | }
213 | }
214 | return remoteIP;
215 | }
216 |
217 | /**
218 | * Get the port assigned by the proxy for the socket, not
219 | * the port on locall machine as in Socket.
220 | @return Port of the socket used on the proxy server.
221 | */
222 | public int getLocalPort(){
223 | return localPort;
224 | }
225 |
226 | /**
227 | * Get address assigned by proxy to make a remote connection,
228 | * it might be different from the host specified for the proxy.
229 | * Can return null if socks server returned this address as hostname
230 | * and it can't be resolved locally, use getLocalHost() then.
231 | @return Address proxy is using to make a connection.
232 | */
233 | public InetAddress getLocalAddress(){
234 | if(localIP == null){
235 | try{
236 | localIP = InetAddress.getByName(localHost);
237 | }catch(UnknownHostException e){
238 | return null;
239 | }
240 | }
241 | return localIP;
242 | }
243 | /**
244 | Get name of the host, proxy has assigned to make a remote connection
245 | for this socket. This method is usefull when proxy have returned
246 | address as hostname, and we can't resolve it on this machine.
247 | @return The name of the host proxy is using to make a connection.
248 | */
249 | public String getLocalHost(){
250 | return localHost;
251 | }
252 |
253 | /**
254 | Same as socket.
255 | */
256 | public void setSoLinger(boolean on,int val) throws SocketException{
257 | proxy.proxySocket.setSoLinger(on,val);
258 | }
259 | /**
260 | Same as socket.
261 | */
262 | public int getSoLinger(int timeout) throws SocketException{
263 | return proxy.proxySocket.getSoLinger();
264 | }
265 | /**
266 | Same as socket.
267 | */
268 | public void setSoTimeout(int timeout) throws SocketException{
269 | proxy.proxySocket.setSoTimeout(timeout);
270 | }
271 | /**
272 | Same as socket.
273 | */
274 | public int getSoTimeout(int timeout) throws SocketException{
275 | return proxy.proxySocket.getSoTimeout();
276 | }
277 | /**
278 | Same as socket.
279 | */
280 | public void setTcpNoDelay(boolean on) throws SocketException{
281 | proxy.proxySocket.setTcpNoDelay(on);
282 | }
283 | /**
284 | Same as socket.
285 | */
286 | public boolean getTcpNoDelay() throws SocketException{
287 | return proxy.proxySocket.getTcpNoDelay();
288 | }
289 |
290 | /**
291 | Get string representation of the socket.
292 | */
293 | public String toString(){
294 | if(directSock!=null) return "Direct connection:"+directSock;
295 | return ("Proxy:"+proxy+";"+"addr:"+remoteHost+",port:"+remotePort
296 | +",localport:"+localPort);
297 |
298 | }
299 |
300 | //Private Methods
301 | //////////////////
302 |
303 | private void processReply(ProxyMessage reply)throws SocksException{
304 | localPort = reply.port;
305 | /*
306 | * If the server have assigned same host as it was contacted on
307 | * it might return an address of all zeros
308 | */
309 | if(reply.host.equals("0.0.0.0")){
310 | localIP = proxy.proxyIP;
311 | localHost = localIP.getHostName();
312 | }else{
313 | localHost = reply.host;
314 | localIP = reply.ip;
315 | }
316 | }
317 | private void doDirect()throws SocksException{
318 | try{
319 | //System.out.println("IP:"+remoteIP+":"+remotePort);
320 | directSock = new Socket(remoteIP,remotePort);
321 | proxy.out = directSock.getOutputStream();
322 | proxy.in = directSock.getInputStream();
323 | proxy.proxySocket = directSock;
324 | localIP = directSock.getLocalAddress();
325 | localPort = directSock.getLocalPort();
326 | }catch(IOException io_ex){
327 | throw new SocksException(CProxy.SOCKS_DIRECT_FAILED,
328 | "Direct connect failed:"+io_ex);
329 | }
330 | }
331 |
332 | }
333 |
--------------------------------------------------------------------------------
/src/main/java/socks/UDPEncapsulation.java:
--------------------------------------------------------------------------------
1 | package socks;
2 |
3 | import java.io.IOException;
4 |
5 | /**
6 | This interface provides for datagram encapsulation for SOCKSv5 protocol.
7 |
77 | Zero timeout implies infinity.
78 | Default timeout is 3 minutes.
79 | */
80 |
81 | static public void setTimeout(int timeout){
82 | iddleTimeout = timeout;
83 | }
84 |
85 |
86 | /**
87 | Sets the size of the datagrams used in the UDPRelayServer.
88 | Default size is 64K, a bit more than maximum possible size of the
89 | datagram.
90 | */
91 | static public void setDatagramSize(int size){
92 | datagramSize = size;
93 | }
94 |
95 | /**
96 | Port to which client should send datagram for association.
97 | */
98 | public int getRelayPort(){
99 | return relayPort;
100 | }
101 | /**
102 | IP address to which client should send datagrams for association.
103 | */
104 | public InetAddress getRelayIP(){
105 | return relayIP;
106 | }
107 |
108 | /**
109 | Starts udp relay server.
110 | Spawns two threads of execution and returns.
111 | */
112 | public void start() throws IOException{
113 | remote_sock.setSoTimeout(iddleTimeout);
114 | client_sock.setSoTimeout(iddleTimeout);
115 |
116 | log.info("Starting UDP relay server on {}:{}, Remote socket {}:{}",
117 | relayIP, relayPort,
118 | remote_sock.getLocalAddress(), remote_sock.getLocalPort()
119 | );
120 |
121 | pipe_thread1 = new Thread(this,"pipe1");
122 | pipe_thread2 = new Thread(this,"pipe2");
123 |
124 | lastReadTime = System.currentTimeMillis();
125 |
126 | pipe_thread1.start();
127 | pipe_thread2.start();
128 | }
129 |
130 | /**
131 | Stops Relay server.
132 |
13 | Socket s = ss.accept();
14 | Ident id = new Ident(s);
15 | if(id.successful) goUseUser(id.userName);
16 | else handleIdentError(id.errorCode,id.errorMessage)
17 |
18 | */
19 | public class Ident{
20 |
21 | /** Error Message can be null.*/
22 | public String errorMessage;
23 | /** Host type as returned by daemon, can be null, if error happened*/
24 | public String hostType;
25 | /** User name as returned by the identd daemon, or null, if it failed*/
26 | public String userName;
27 |
28 | /** If this is true then userName and hostType contain valid values.
29 | Else errorCode contain the error code, and errorMessage contains
30 | the corresponding message.
31 | */
32 | public boolean successful;
33 | /** Error code*/
34 | public int errorCode;
35 | /** Identd on port 113 can't be contacted*/
36 | public static final int ERR_NO_CONNECT = 1;
37 | /** Connection timed out*/
38 | public static final int ERR_TIMEOUT = 2;
39 | /** Identd daemon responded with ERROR, in this case errorMessage
40 | contains the string explanation, as send by the daemon.
41 | */
42 | public static final int ERR_PROTOCOL = 3;
43 | /**
44 | When parsing server response protocol error happened.
45 | */
46 | public static final int ERR_PROTOCOL_INCORRECT = 4;
47 |
48 |
49 | /** Maximum amount of time we should wait before dropping the
50 | connection to identd server.Setting it to 0 implies infinit
51 | timeout.
52 | */
53 | public static final int connectionTimeout = 10000;
54 |
55 |
56 | /**
57 | Constructor tries to connect to Identd daemon on the host of the
58 | given socket, and retrieve user name of the owner of given socket
59 | connection on remote machine. After constructor returns public
60 | fields are initialised to whatever the server returned.
61 |
Should not be
17 | used on machines which are not behind the firewall.
18 |
21 | For Example:
22 | class MyAuth extends socks.server.ServerAuthenticator{
23 | ...
24 | public ServerAuthenticator startSession(java.net.Socket s){
25 | if(!checkHost(s.getInetAddress()) return null;
26 | return super.startSession(s);
27 | }
28 |
29 | boolean checkHost(java.net.Inetaddress addr){
30 | boolean allow;
31 | //Do it somehow
32 | return allow;
33 | }
34 | }
35 |
36 | */
37 | public class ServerAuthenticatorNone implements ServerAuthenticator{
38 |
39 | static final byte[] socks5response = {5,0};
40 |
41 | InputStream in;
42 | OutputStream out;
43 |
44 | /**
45 | Creates new instance of the ServerAuthenticatorNone.
46 | */
47 | public ServerAuthenticatorNone(){
48 | this.in = null;
49 | this.out = null;
50 | }
51 | /**
52 | Constructs new ServerAuthenticatorNone object suitable for returning
53 | from the startSession function.
54 | @param in Input stream to return from getInputStream method.
55 | @param out Output stream to return from getOutputStream method.
56 | */
57 | public ServerAuthenticatorNone(InputStream in, OutputStream out){
58 | this.in = in;
59 | this.out = out;
60 | }
61 | /**
62 | Grants access to everyone.Removes authentication related bytes from
63 | the stream, when a SOCKS5 connection is being made, selects an
64 | authentication NONE.
65 | */
66 | public ServerAuthenticator startSession(Socket s)
67 | throws IOException{
68 |
69 | PushbackInputStream in = new PushbackInputStream(s.getInputStream());
70 | OutputStream out = s.getOutputStream();
71 |
72 | int version = in.read();
73 | if(version == 5){
74 | if(!selectSocks5Authentication(in,out,0))
75 | return null;
76 | }else if(version == 4){
77 | //Else it is the request message allready, version 4
78 | in.unread(version);
79 | }else
80 | return null;
81 |
82 |
83 | return new ServerAuthenticatorNone(in,out);
84 | }
85 |
86 | /**
87 | Get input stream.
88 | @return Input stream speciefied in the constructor.
89 | */
90 | public InputStream getInputStream(){
91 | return in;
92 | }
93 | /**
94 | Get output stream.
95 | @return Output stream speciefied in the constructor.
96 | */
97 | public OutputStream getOutputStream(){
98 | return out;
99 | }
100 | /**
101 | Allways returns null.
102 | @return null
103 | */
104 | public UDPEncapsulation getUdpEncapsulation(){
105 | return null;
106 | }
107 |
108 | /**
109 | Allways returns true.
110 | */
111 | public boolean checkRequest(ProxyMessage msg){
112 | return true;
113 | }
114 |
115 | /**
116 | Allways returns true.
117 | */
118 | public boolean checkRequest(java.net.DatagramPacket dp, boolean out){
119 | return true;
120 | }
121 |
122 | /**
123 | Does nothing.
124 | */
125 | public void endSession(){
126 | }
127 |
128 | /**
129 | Convinience routine for selecting SOCKSv5 authentication.
130 |
6 | Reads input line by line and sends it to given on command line
7 | host and port, using given proxy, then blocks until reply datagram
8 | recieved, not really echo, single threaded client, I just used it
9 | for testing before UDP actually worked.
10 | */
11 | public class SocksUDPEcho{
12 |
13 | public static void usage(){
14 | System.err.print(
15 | "Usage: java SocksUDPEcho host port [socksHost socksPort]\n");
16 | }
17 |
18 | static final int defaultProxyPort = 1080; //Default Port
19 | static final String defaultProxyHost = "www-proxy"; //Default proxy
20 |
21 | public static void main(String args[]){
22 | int port;
23 | String host;
24 | int proxyPort;
25 | String proxyHost;
26 | InetAddress ip;
27 |
28 | if(args.length > 1 && args.length < 5){
29 | try{
30 |
31 | host = args[0];
32 | port = Integer.parseInt(args[1]);
33 |
34 | proxyPort =(args.length > 3)? Integer.parseInt(args[3])
35 | : defaultProxyPort;
36 |
37 | host = args[0];
38 | ip = InetAddress.getByName(host);
39 |
40 | proxyHost =(args.length > 2)? args[2]
41 | : defaultProxyHost;
42 |
43 | CProxy.setDefaultProxy(proxyHost,proxyPort);
44 | CProxy p = CProxy.getDefaultProxy();
45 | p.addDirect("lux");
46 |
47 |
48 | DatagramSocket ds = new Socks5DatagramSocket();
49 |
50 |
51 | BufferedReader in = new BufferedReader(
52 | new InputStreamReader(System.in));
53 | String s;
54 |
55 | System.out.print("Enter line:");
56 | s = in.readLine();
57 |
58 | while(s != null){
59 | byte[] data = (s+"\r\n").getBytes();
60 | DatagramPacket dp = new DatagramPacket(data,0,data.length,
61 | ip,port);
62 | System.out.println("Sending to: "+ip+":"+port);
63 | ds.send(dp);
64 | dp = new DatagramPacket(new byte[1024],1024);
65 |
66 | System.out.println("Trying to recieve on port:"+
67 | ds.getLocalPort());
68 | ds.receive(dp);
69 | System.out.print("Recieved:\n"+
70 | "From:"+dp.getAddress()+":"+dp.getPort()+
71 | "\n\n"+
72 | new String(dp.getData(),dp.getOffset(),dp.getLength())+"\n"
73 | );
74 | System.out.print("Enter line:");
75 | s = in.readLine();
76 |
77 | }
78 | ds.close();
79 | System.exit(1);
80 |
81 | }catch(SocksException s_ex){
82 | System.err.println("SocksException:"+s_ex);
83 | s_ex.printStackTrace();
84 | System.exit(1);
85 | }catch(IOException io_ex){
86 | io_ex.printStackTrace();
87 | System.exit(1);
88 | }catch(NumberFormatException num_ex){
89 | usage();
90 | num_ex.printStackTrace();
91 | System.exit(1);
92 | }
93 |
94 | }else{
95 | usage();
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/test/java/TestClient.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.net.*;
3 | import socks.*;
4 |
5 | public class TestClient extends TestService{
6 | /** Proxy which should be used*/
7 | CProxy proxy;
8 | /** Host on which TestServer is running*/
9 | String testHost;
10 |
11 | int timeout = 15000;
12 | int acceptTimeout = 0;
13 |
14 | BufferedReader in;
15 | Writer out;
16 |
17 | public TestClient(CProxy p,String testHost){
18 | this.proxy = p;
19 | this.testHost = testHost;
20 | if(log == null) log = System.out;
21 | }
22 |
23 | public void start(){
24 | connectTests(true);
25 | acceptTests(true);
26 | udpTests(true);
27 |
28 | connectTests(false);
29 | acceptTests(false);
30 | udpTests(false);
31 | }
32 |
33 | void connectTests(boolean useString){
34 | try{
35 | open(ECHO, useString);
36 | testEcho();
37 | s.close();
38 |
39 | open(DISCARD, useString);
40 | testDiscard();
41 | s.close();
42 |
43 | open(CHARGEN, useString);
44 |
45 | for(int i = 0; i< 3;){
46 | try{
47 | testChargen();
48 | break;
49 | }catch(InterruptedIOException ioe){
50 | log("IO interrupted:"+i);
51 | i++;
52 | }
53 | }
54 |
55 | s.close();
56 |
57 | }catch(IOException ioe){
58 | ioe.printStackTrace();
59 | }
60 |
61 | }
62 |
63 | void acceptTests(boolean useString){
64 | try{
65 | testAccept(ECHO, useString);
66 | testEcho();
67 | s.close();
68 |
69 | testAccept(DISCARD, useString);
70 | testDiscard();
71 | s.close();
72 |
73 | testAccept(CHARGEN, useString);
74 |
75 | for(int i = 0; i< 3;){
76 | try{
77 | testChargen();
78 | break;
79 | }catch(InterruptedIOException ioe){
80 | log("IO interrupted:"+i);
81 | i++;
82 | }
83 | }
84 | s.close();
85 |
86 | }catch(IOException ioe){
87 | ioe.printStackTrace();
88 | }
89 | }
90 |
91 | void udpTests(boolean useString){
92 | log("Udp tests are not yet implemented");
93 | }
94 |
95 | void testEcho() throws IOException{
96 | log("Testing echo.");
97 | for(int i=0;i<5;++i){
98 | out.write("String number "+i+"\r\n");
99 | out.flush();
100 | log("Echo:"+in.readLine());;
101 | }
102 | log("Echo finished");
103 | }
104 |
105 | void testDiscard() throws IOException{
106 | log("Testing discard");
107 | for(int i =0; i < 5;++i){
108 | log("Sending discard message:"+i);
109 | out.write("Discard message:"+i+"\r\n");
110 | out.flush();
111 | }
112 | log("Discard finished");
113 | }
114 |
115 | void testChargen() throws IOException{
116 | log("Testing chargen");
117 | String s;
118 | s = in.readLine();
119 | while(s!=null){
120 | log("ChGen:"+s);
121 | s = in.readLine();
122 | }
123 | log("Chargen finished.");
124 | }
125 |
126 | void testAccept(int service,boolean useString)throws IOException{
127 | open(CONNECT,useString);
128 |
129 | log("Testing accept");
130 | ServerSocket ss;
131 |
132 | if(useString)
133 | ss = new SocksServerSocket(proxy,testHost,servicePorts[service]);
134 | else
135 | ss = new SocksServerSocket(proxy,InetAddress.getByName(testHost),
136 | servicePorts[service]);
137 | log("Listenning on "+ss.getInetAddress()+":"+ss.getLocalPort());
138 | ss.setSoTimeout(acceptTimeout);
139 |
140 | out.write(""+ss.getLocalPort()+" "+service+"\r\n");
141 | out.flush();
142 |
143 | String line = in.readLine();
144 | if(line != null){
145 | log("Accept failed:"+line);
146 | }
147 |
148 | s.close();
149 |
150 | s = ss.accept();
151 | log("Accepted:"+s);
152 |
153 | s.setSoTimeout(timeout);
154 |
155 | out = new OutputStreamWriter(s.getOutputStream());
156 | in = new BufferedReader(new InputStreamReader(s.getInputStream()));
157 |
158 | ss.close();
159 | }
160 |
161 | void open(int service,boolean useString) throws IOException{
162 |
163 | if(!useString){
164 | s = new SocksSocket(proxy,InetAddress.getByName(testHost),
165 | servicePorts[service]);
166 | }else{
167 | s = new SocksSocket(proxy,testHost,servicePorts[service]);
168 | }
169 |
170 | s.setSoTimeout(timeout);
171 |
172 | out = new OutputStreamWriter(s.getOutputStream());
173 | in = new BufferedReader(new InputStreamReader(s.getInputStream()));
174 | }
175 |
176 | //Main function
177 | ///////////////
178 |
179 | static void usage(){
180 | System.err.println(
181 | "Usage: java Testclient testhost proxy [directhosts]");
182 | }
183 |
184 | static CProxy initProxy(String ps){
185 | java.util.StringTokenizer st = new java.util.StringTokenizer(ps,",;");
186 | CProxy proxy = null;
187 | while(st.hasMoreElements()){
188 | String entry = st.nextToken();
189 | CProxy p = CProxy.parseProxy(entry);
190 | if( p == null){
191 | log("CProxy "+entry+" invalid.");
192 | return null;
193 | }
194 | p.setChainProxy(proxy);
195 | proxy = p;
196 | }
197 | return proxy;
198 | }
199 | static void addDirectHosts(CProxy p, String directHosts){
200 | java.util.StringTokenizer st = new java.util.StringTokenizer(
201 | directHosts,",;");
202 |
203 | while(st.hasMoreElements()){
204 | String entry = st.nextToken();
205 | log("Adding direct host:"+entry);
206 | p.addDirect(entry);
207 | }
208 | }
209 |
210 | public static void main(String[] argv){
211 | if(argv.length < 2){
212 | usage();
213 | return;
214 | }
215 |
216 | log = System.out;
217 |
218 | String testHost = argv[0];
219 | String proxyHost = argv[1];
220 | String directHosts = argv.length >2 ? argv[2] : null;
221 |
222 | CProxy p = initProxy(proxyHost);
223 | if(p == null){
224 | log("Can't init proxy.");
225 | return;
226 | }
227 | if(directHosts!=null) addDirectHosts(p,directHosts);
228 |
229 | if(p instanceof Socks5Proxy)
230 | ((Socks5Proxy) p).resolveAddrLocally(false);
231 |
232 | TestClient tc = new TestClient(p,testHost);
233 | tc.start();
234 |
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/src/test/java/TestServer.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.net.*;
3 |
4 | /**
5 | Server to used perform tests for SOCKS library.
6 | */
7 | public class TestServer implements Runnable{
8 | static PrintStream log = null;
9 |
10 | int service;
11 |
12 |
13 | /**
14 | Creates a TestServer object which will listen on the port associated
15 | with given service.
16 |
17 | @param service Service to provide
18 | */
19 | public TestServer(int service){
20 | this.service = service;
21 | }
22 |
23 | public void run(){
24 | try{
25 | server(service);
26 | }catch(IOException ioe){
27 | log("Exception:"+ioe);
28 | ioe.printStackTrace();
29 | }
30 | }
31 | //Static functions
32 | /////////////////
33 |
34 |
35 | /**
36 | Listens on the port associated with given service.
37 |