├── .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 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/jitsi/jsocks/badge.svg)](https://search.maven.org/artifact/jitsi/jsocks) 2 | [![Javadocs](http://javadoc.io/badge/jitsi/jsocks.svg)](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 | 5 | 4.0.0 6 | bundle 7 | 8 | org.jitsi 9 | jsocks 10 | 1.0.1-jitsi-2-SNAPSHOT 11 | https://github.com/jitsi/jsocks 12 | 13 | jsocks 14 | 15 | SOCKS Server and Library for Java. Support for versions 4 and 5 of SOCKS protocol. Designed 16 | to be easily expandable to support different encryption/authentication/authorization 17 | methods. Sample server and client are available. 18 | 19 | 20 | 21 | 22 | LGPL-2.0-only 23 | https://spdx.org/licenses/LGPL-2.0-only.html 24 | repo 25 | 26 | 27 | 28 | 29 | 30 | org.jitsi 31 | Jitsi Team 32 | dev@jitsi.org 33 | 34 | 35 | 36 | 37 | scm:git:https://github.com/jitsi/jsocks 38 | scm:git:https://github.com/jitsi/jsocks 39 | https://github.com/jitsi/jsocks 40 | 41 | 42 | 43 | GitHub 44 | https://github.com/jitsi/jsocks/issues 45 | 46 | 47 | 48 | UTF-8 49 | 50 | 51 | 52 | 53 | org.slf4j 54 | slf4j-api 55 | 1.7.30 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-compiler-plugin 64 | 3.8.1 65 | 66 | 1.8 67 | 1.8 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-source-plugin 74 | 3.2.1 75 | 76 | 77 | 78 | org.apache.felix 79 | maven-bundle-plugin 80 | 5.1.1 81 | true 82 | 83 | 84 | <_noclassforname>true 85 | 86 | LGPL-2.0-only;link="https://raw.githubusercontent.com/jitsi/jsocks/master/LICENSE" 87 | 88 | <_removeheaders> 89 | Bnd-*, 90 | Tool, 91 | Require-Capability, 92 | Include-Resource, 93 | Build-Jdk-Spec 94 | 95 | 96 | 97 | 98 | 99 | 100 | org.apache.maven.plugins 101 | maven-javadoc-plugin 102 | 3.2.0 103 | 104 | none 105 | 106 | 107 | 108 | 109 | org.apache.maven.plugins 110 | maven-surefire-plugin 111 | 2.22.2 112 | 113 | 114 | 115 | org.apache.maven.plugins 116 | maven-deploy-plugin 117 | 2.8.2 118 | 119 | 120 | 121 | org.sonatype.plugins 122 | nexus-staging-maven-plugin 123 | 1.6.8 124 | true 125 | 126 | ossrh 127 | https://oss.sonatype.org/ 128 | true 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | release-central 137 | 138 | 139 | performRelease 140 | true 141 | 142 | 143 | 144 | 145 | 146 | org.apache.maven.plugins 147 | maven-gpg-plugin 148 | 1.6 149 | 150 | 151 | sign-artifacts 152 | verify 153 | 154 | sign 155 | 156 | 157 | 158 | 159 | 160 | --pinentry-mode 161 | loopback 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | ossrh 173 | https://oss.sonatype.org/content/repositories/snapshots 174 | 175 | 176 | ossrh 177 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 178 | 179 | 180 | -------------------------------------------------------------------------------- /src/main/java/socks/Authentication.java: -------------------------------------------------------------------------------- 1 | package socks; 2 | 3 | /** 4 | The Authentication interface provides for performing method specific 5 | authentication for SOCKS5 connections. 6 | */ 7 | public interface Authentication{ 8 | /** 9 | This method is called when SOCKS5 server have selected a particular 10 | authentication method, for whch an implementaion have been registered. 11 | 12 |

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 |

54 | */ 55 | public synchronized boolean add(String s){ 56 | if(s == null) return false; 57 | 58 | s = s.trim(); 59 | if(s.length() == 0) return false; 60 | 61 | Object[] entry; 62 | 63 | if(s.charAt(s.length()-1) == '.'){ 64 | //thing like: 111.222.33. 65 | //it is being treated as range 111.222.33.000 - 111.222.33.255 66 | 67 | int[] addr = ip2intarray(s); 68 | long from,to; 69 | from = to = 0; 70 | 71 | if(addr == null) return false; 72 | for(int i = 0; i< 4;++i){ 73 | if(addr[i]>=0) 74 | from += (((long)addr[i]) << 8*(3-i)); 75 | else{ 76 | to = from; 77 | while(i<4) 78 | to += 255l << 8*(3-i++); 79 | break; 80 | } 81 | } 82 | entry = new Object[] {s,null,new Long(from),new Long(to)}; 83 | all.addElement(entry); 84 | 85 | }else if(s.charAt(0) == '.'){ 86 | //Thing like: .myhost.com 87 | 88 | end_names.addElement(s); 89 | all.addElement(new Object[]{s,null,null,null}); 90 | }else{ 91 | StringTokenizer tokens = new StringTokenizer(s," \t\r\n\f:"); 92 | if(tokens.countTokens() > 1){ 93 | entry = new Object[] {s,null,null,null}; 94 | resolve(entry,tokens.nextToken(),tokens.nextToken()); 95 | all.addElement(entry); 96 | }else{ 97 | entry = new Object[] {s,null,null,null}; 98 | all.addElement(entry); 99 | host_names.put(s,entry); 100 | resolve(entry); 101 | } 102 | 103 | } 104 | 105 | return true; 106 | } 107 | 108 | /** 109 | * Adds another ip for this range. 110 | @param ip IP os the host which should be added to this range. 111 | */ 112 | public synchronized void add(InetAddress ip){ 113 | long from, to; 114 | from = to = ip2long(ip); 115 | all.addElement(new Object[]{ip.getHostName(),ip,new Long(from), 116 | new Long(to)}); 117 | } 118 | 119 | /** 120 | * Adds another range of ips for this range.Any host with ip address 121 | greater than or equal to the address of from and smaller than or equal 122 | to the address of to will be included in the range. 123 | @param from IP from where range starts(including). 124 | @param to IP where range ends(including). 125 | */ 126 | public synchronized void add(InetAddress from,InetAddress to){ 127 | all.addElement(new Object[]{from.getHostAddress()+":"+to.getHostAddress() 128 | ,null,new Long(ip2long(from)), 129 | new Long(ip2long(to))}); 130 | } 131 | 132 | /** 133 | * Checks wether the givan host is in the range. Attempts to resolve 134 | host name if required. 135 | @param host Host name to check. 136 | @return true If host is in the range, false otherwise. 137 | * @see InetRange#contains(String,boolean) 138 | */ 139 | public synchronized boolean contains(String host){ 140 | return contains(host,true); 141 | } 142 | 143 | /** 144 | * Checks wether the given host is in the range. 145 | *

146 | * Algorithm:
147 | *

    148 | *
  1. Look up if the hostname is in the range (in the Hashtable). 149 | *
  2. Check if it ends with one of the speciefied endings. 150 | *
  3. Check if it is ip(eg.130.220.35.98). If it is check if it is 151 | * in the range. 152 | *
  4. If attemptResolve is true, host is name, rather than ip, and 153 | * all previous attempts failed, try to resolve the hostname, and 154 | * check wether the ip associated with the host is in the range.It 155 | * also repeats all previos steps with the hostname obtained from 156 | * InetAddress, but the name is not allways the full name,it is 157 | * quite likely to be the same. Well it was on my machine. 158 | *
159 | @param host Host name to check. 160 | @param attemptResolve Wether to lookup ip address which corresponds 161 | to the host,if required. 162 | @return true If host is in the range, false otherwise. 163 | */ 164 | public synchronized boolean contains(String host,boolean attemptResolve){ 165 | if(all.size() ==0) return false; //Empty range 166 | 167 | host = host.trim(); 168 | if(host.length() == 0) return false; 169 | 170 | if(checkHost(host)) return true; 171 | if(checkHostEnding(host)) return true; 172 | 173 | long l = host2long(host); 174 | if(l >=0) return contains(l); 175 | 176 | if(!attemptResolve) return false; 177 | 178 | try{ 179 | InetAddress ip = InetAddress.getByName(host); 180 | return contains(ip); 181 | }catch(UnknownHostException uhe){ 182 | 183 | } 184 | 185 | return false; 186 | } 187 | 188 | /** 189 | * Checks wether the given ip is in the range. 190 | @param ip Address of the host to check. 191 | @return true If host is in the range, false otherwise. 192 | */ 193 | public synchronized boolean contains(InetAddress ip){ 194 | if(checkHostEnding(ip.getHostName())) return true; 195 | if(checkHost(ip.getHostName())) return true; 196 | return contains(ip2long(ip)); 197 | } 198 | /** 199 | Get all entries in the range as strings.
200 | These strings can be used to delete entries from the range 201 | with remove function. 202 | @return Array of entries as strings. 203 | @see InetRange#remove(String) 204 | */ 205 | public synchronized String[] getAll(){ 206 | int size = all.size(); 207 | Object entry[]; 208 | String all_names[] = new String[size]; 209 | 210 | for(int i=0;i 218 | @param s Entry to remove. 219 | @return true if successfull. 220 | */ 221 | public synchronized boolean remove(String s){ 222 | Enumeration eEnum = all.elements(); 223 | while(eEnum.hasMoreElements()){ 224 | Object[] entry = (Object[]) eEnum.nextElement(); 225 | if(s.equals(entry[0])){ 226 | all.removeElement(entry); 227 | end_names.removeElement(s); 228 | host_names.remove(s); 229 | return true; 230 | } 231 | } 232 | return false; 233 | } 234 | 235 | /** Get string representaion of this Range.*/ 236 | public String toString(){ 237 | String all[] = getAll(); 238 | if(all.length == 0) return ""; 239 | 240 | String s = all[0]; 241 | for(int i=1;i= ip) return true; 270 | 271 | } 272 | return false; 273 | } 274 | 275 | private boolean checkHost(String host){ 276 | return host_names.containsKey(host); 277 | } 278 | private boolean checkHostEnding(String host){ 279 | Enumeration eEnum = end_names.elements(); 280 | while(eEnum.hasMoreElements()){ 281 | if(host.endsWith((String) eEnum.nextElement())) return true; 282 | } 283 | return false; 284 | } 285 | private void resolve(Object[] entry){ 286 | //First check if it's in the form ddd.ddd.ddd.ddd. 287 | long ip = host2long((String) entry[0]); 288 | if(ip >= 0){ 289 | entry[2] = entry[3] = new Long(ip); 290 | }else{ 291 | InetRangeResolver res = new InetRangeResolver(entry); 292 | res.resolve(useSeparateThread); 293 | } 294 | } 295 | private void resolve(Object[] entry,String from,String to){ 296 | long f,t; 297 | if((f=host2long(from))>= 0 && (t=host2long(to)) >= 0){ 298 | entry[2] = new Long(f); 299 | entry[3] = new Long(t); 300 | }else{ 301 | InetRangeResolver res = new InetRangeResolver(entry,from,to); 302 | res.resolve(useSeparateThread); 303 | } 304 | } 305 | 306 | 307 | 308 | //Class methods 309 | /////////////// 310 | 311 | //Converts ipv4 to long value(unsigned int) 312 | /////////////////////////////////////////// 313 | static long ip2long(InetAddress ip){ 314 | long l=0; 315 | byte[] addr = ip.getAddress(); 316 | 317 | if(addr.length ==4){ //IPV4 318 | for(int i=0;i<4;++i) 319 | l += (((long)addr[i] &0xFF) << 8*(3-i)); 320 | }else{ //IPV6 321 | return 0; //Have no idea how to deal with those 322 | } 323 | return l; 324 | } 325 | 326 | 327 | long host2long(String host){ 328 | long ip=0; 329 | 330 | //check if it's ddd.ddd.ddd.ddd 331 | if(!Character.isDigit(host.charAt(0))) return -1; 332 | 333 | int[] addr = ip2intarray(host); 334 | if(addr == null) return -1; 335 | 336 | for(int i=0;i=0 ? addr[i] : 0)) << 8*(3-i); 338 | 339 | return ip; 340 | } 341 | 342 | static int[] ip2intarray(String host){ 343 | int[] address = {-1,-1,-1,-1}; 344 | int i=0; 345 | StringTokenizer tokens = new StringTokenizer(host,"."); 346 | if(tokens.countTokens() > 4) return null; 347 | while(tokens.hasMoreTokens()){ 348 | try{ 349 | address[i++] = Integer.parseInt(tokens.nextToken()) & 0xFF; 350 | }catch(NumberFormatException nfe){ 351 | return null; 352 | } 353 | 354 | } 355 | return address; 356 | } 357 | 358 | 359 | /* 360 | //* This was the test main function 361 | //********************************** 362 | 363 | public static void main(String args[])throws UnknownHostException{ 364 | int i; 365 | 366 | InetRange ir = new InetRange(); 367 | 368 | 369 | for(i=0;i> 8); 57 | msgBytes[3] = (byte) port; 58 | 59 | byte[] addr; 60 | 61 | if(ip != null) 62 | addr = ip.getAddress(); 63 | else{ 64 | addr = new byte[4]; 65 | addr[0]=addr[1]=addr[2]=addr[3]=0; 66 | } 67 | System.arraycopy(addr,0,msgBytes,4,4); 68 | 69 | if(user != null){ 70 | byte[] buf = user.getBytes(); 71 | System.arraycopy(buf,0,msgBytes,8,buf.length); 72 | msgBytes[msgBytes.length -1 ] = 0; 73 | } 74 | } 75 | 76 | /** 77 | *Initialise from the stream 78 | *If clientMode is true attempts to read a server response 79 | *otherwise reads a client request 80 | *see read for more detail 81 | */ 82 | public Socks4Message(InputStream in, boolean clientMode) throws IOException{ 83 | msgBytes = null; 84 | read(in,clientMode); 85 | } 86 | 87 | public void read(InputStream in) throws IOException{ 88 | read(in,true); 89 | } 90 | 91 | public void read(InputStream in, boolean clientMode) throws IOException{ 92 | DataInputStream d_in = new DataInputStream(in); 93 | version= d_in.readUnsignedByte(); 94 | command = d_in.readUnsignedByte(); 95 | if(clientMode && command != REPLY_OK){ 96 | String errMsg; 97 | if(command >REPLY_OK && command < REPLY_BAD_IDENTD) 98 | errMsg = replyMessage[command-REPLY_OK]; 99 | else 100 | errMsg = "Unknown Reply Code"; 101 | throw new SocksException(command,errMsg); 102 | } 103 | port = d_in.readUnsignedShort(); 104 | byte[] addr = new byte[4]; 105 | d_in.readFully(addr); 106 | ip=bytes2IP(addr); 107 | host = ip.getHostName(); 108 | if(!clientMode){ 109 | int b = in.read(); 110 | //Hope there are no idiots with user name bigger than this 111 | byte[] userBytes = new byte[256]; 112 | int i = 0; 113 | for(i =0;i0;++i){ 114 | userBytes[i] = (byte) b; 115 | b = in.read(); 116 | } 117 | user = new String(userBytes,0,i); 118 | } 119 | } 120 | public void write(OutputStream out) throws IOException{ 121 | if(msgBytes == null){ 122 | Socks4Message msg = new Socks4Message(version,command,ip,port,user); 123 | msgBytes = msg.msgBytes; 124 | msgLength = msg.msgLength; 125 | } 126 | out.write(msgBytes); 127 | } 128 | 129 | //Class methods 130 | static InetAddress bytes2IP(byte[] addr){ 131 | String s = bytes2IPV4(addr,0); 132 | try{ 133 | return InetAddress.getByName(s); 134 | }catch(UnknownHostException uh_ex){ 135 | return null; 136 | } 137 | } 138 | 139 | //Constants 140 | 141 | static final String[] replyMessage ={ 142 | "Request Granted", 143 | "Request Rejected or Failed", 144 | "Failed request, can't connect to Identd", 145 | "Failed request, bad user name"}; 146 | 147 | static final int SOCKS_VERSION = 4; 148 | 149 | public final static int REQUEST_CONNECT = 1; 150 | public final static int REQUEST_BIND = 2; 151 | 152 | public final static int REPLY_OK = 90; 153 | public final static int REPLY_REJECTED = 91; 154 | public final static int REPLY_NO_CONNECT = 92; 155 | public final static int REPLY_BAD_IDENTD = 93; 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/socks/Socks4Proxy.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 | CProxy which describes SOCKS4 proxy. 9 | */ 10 | 11 | public class Socks4Proxy extends CProxy implements Cloneable{ 12 | 13 | //Data members 14 | String user; 15 | 16 | //Public Constructors 17 | //==================== 18 | 19 | /** 20 | Creates the SOCKS4 proxy 21 | @param p CProxy to use to connect to this proxy, allows proxy chaining. 22 | @param proxyHost Address of the proxy server. 23 | @param proxyPort Port of the proxy server 24 | @param user User name to use for identification purposes. 25 | @throws UnknownHostException If proxyHost can't be resolved. 26 | */ 27 | public Socks4Proxy(CProxy p,String proxyHost,int proxyPort,String user) 28 | throws UnknownHostException{ 29 | super(p,proxyHost,proxyPort); 30 | this.user = new String(user); 31 | version = 4; 32 | } 33 | 34 | /** 35 | Creates the SOCKS4 proxy 36 | @param proxyHost Address of the proxy server. 37 | @param proxyPort Port of the proxy server 38 | @param user User name to use for identification purposes. 39 | @throws UnknownHostException If proxyHost can't be resolved. 40 | */ 41 | public Socks4Proxy(String proxyHost,int proxyPort,String user) 42 | throws UnknownHostException{ 43 | this(null,proxyHost,proxyPort,user); 44 | } 45 | 46 | /** 47 | Creates the SOCKS4 proxy 48 | @param p CProxy to use to connect to this proxy, allows proxy chaining. 49 | @param proxyIP Address of the proxy server. 50 | @param proxyPort Port of the proxy server 51 | @param user User name to use for identification purposes. 52 | */ 53 | public Socks4Proxy(CProxy p,InetAddress proxyIP,int proxyPort,String user){ 54 | super(p,proxyIP,proxyPort); 55 | this.user = new String(user); 56 | version = 4; 57 | } 58 | 59 | /** 60 | Creates the SOCKS4 proxy 61 | @param proxyIP Address of the proxy server. 62 | @param proxyPort Port of the proxy server 63 | @param user User name to use for identification purposes. 64 | */ 65 | public Socks4Proxy(InetAddress proxyIP,int proxyPort,String user){ 66 | this(null,proxyIP,proxyPort,user); 67 | } 68 | 69 | //Public instance methods 70 | //======================== 71 | 72 | /** 73 | * Creates a clone of this proxy. Changes made to the clone should not 74 | * affect this object. 75 | */ 76 | public Object clone(){ 77 | Socks4Proxy newProxy = new Socks4Proxy(proxyIP,proxyPort,user); 78 | newProxy.directHosts = (InetRange)directHosts.clone(); 79 | newProxy.chainProxy = chainProxy; 80 | return newProxy; 81 | } 82 | 83 | 84 | //Public Static(Class) Methods 85 | //============================== 86 | 87 | 88 | //Protected Methods 89 | //================= 90 | 91 | protected CProxy copy(){ 92 | Socks4Proxy copy = new Socks4Proxy(proxyIP,proxyPort,user); 93 | copy.directHosts = this.directHosts; 94 | copy.chainProxy = chainProxy; 95 | return copy; 96 | } 97 | 98 | protected ProxyMessage formMessage(int cmd,InetAddress ip,int port){ 99 | switch(cmd){ 100 | case SOCKS_CMD_CONNECT: 101 | cmd = Socks4Message.REQUEST_CONNECT; 102 | break; 103 | case SOCKS_CMD_BIND: 104 | cmd = Socks4Message.REQUEST_BIND; 105 | break; 106 | default: 107 | return null; 108 | } 109 | return new Socks4Message(cmd,ip,port,user); 110 | } 111 | protected ProxyMessage formMessage(int cmd,String host,int port) 112 | throws UnknownHostException{ 113 | return formMessage(cmd,InetAddress.getByName(host),port); 114 | } 115 | protected ProxyMessage formMessage(InputStream in) 116 | throws SocksException, 117 | IOException{ 118 | return new Socks4Message(in,true); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/socks/Socks5DatagramSocket.java: -------------------------------------------------------------------------------- 1 | package socks; 2 | import java.net.*; 3 | import java.io.*; 4 | 5 | /** 6 | Datagram socket to interract through the firewall.
7 | Can be used same way as the normal DatagramSocket. One should 8 | be carefull though with the datagram sizes used, as additional data 9 | is present in both incomming and outgoing datagrams. 10 |

11 | SOCKS5 protocol allows to send host address as either: 12 |

    13 |
  • IPV4, normal 4 byte address. (10 bytes header size) 14 |
  • IPV6, version 6 ip address (not supported by Java as for now). 15 | 22 bytes header size. 16 |
  • Host name,(7+length of the host name bytes header size). 17 |
18 | As with other Socks equivalents, direct addresses are handled 19 | transparently, that is data will be send directly when required 20 | by the proxy settings. 21 |

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 |

    86 |
  1. Given version of proxy does not support UDP_ASSOCIATE. 87 |
  2. CProxy can't be reached. 88 |
  3. Authorization fails. 89 |
  4. CProxy does not want to perform udp forwarding, for any reason. 90 |
91 | Might throw IOException if binding dtagram socket to given address/port 92 | fails. 93 | See java.net.DatagramSocket for more details. 94 | */ 95 | public Socks5DatagramSocket(CProxy p,int port,InetAddress ip) 96 | throws SocksException, 97 | IOException{ 98 | super(port,ip); 99 | if(p == null) throw new SocksException(CProxy.SOCKS_NO_PROXY); 100 | if(!(p instanceof Socks5Proxy)) 101 | throw new SocksException(-1,"Datagram Socket needs Proxy version 5"); 102 | 103 | if(p.chainProxy != null) 104 | throw new SocksException(CProxy.SOCKS_JUST_ERROR, 105 | "Datagram Sockets do not support proxy chaining."); 106 | 107 | proxy =(Socks5Proxy) p.copy(); 108 | 109 | ProxyMessage msg = proxy.udpAssociate(super.getLocalAddress(), 110 | super.getLocalPort()); 111 | relayIP = msg.ip; 112 | if(relayIP.getHostAddress().equals("0.0.0.0")) relayIP = proxy.proxyIP; 113 | relayPort = msg.port; 114 | 115 | encapsulation = proxy.udp_encapsulation; 116 | 117 | //debug("Datagram Socket:"+getLocalAddress()+":"+getLocalPort()+"\n"); 118 | //debug("Socks5Datagram: "+relayIP+":"+relayPort+"\n"); 119 | } 120 | 121 | /** 122 | Used by UDPRelayServer. 123 | */ 124 | Socks5DatagramSocket(boolean server_mode,UDPEncapsulation encapsulation, 125 | InetAddress relayIP,int relayPort) 126 | throws IOException{ 127 | super(); 128 | this.server_mode = server_mode; 129 | this.relayIP = relayIP; 130 | this.relayPort = relayPort; 131 | this.encapsulation = encapsulation; 132 | this.proxy = null; 133 | } 134 | 135 | /** 136 | Sends the Datagram either through the proxy or directly depending 137 | on current proxy settings and destination address.
138 | 139 | NOTE: DatagramPacket size should be at least 10 bytes less 140 | than the systems limit. 141 | 142 |

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> 4; 385 | int i2 = b[i] & 0xF; 386 | s+=hex_digit[i1]; 387 | s+=hex_digit[i2]; 388 | s+=" "; 389 | } 390 | return s; 391 | } 392 | private static final void debug(String s){ 393 | if(DEBUG) 394 | System.out.print(s); 395 | } 396 | 397 | private static final boolean DEBUG = true; 398 | 399 | 400 | public static void usage(){ 401 | System.err.print( 402 | "Usage: java Socks.SocksDatagramSocket host port [socksHost socksPort]\n"); 403 | } 404 | 405 | static final int defaultProxyPort = 1080; //Default Port 406 | static final String defaultProxyHost = "www-proxy"; //Default proxy 407 | 408 | public static void main(String args[]){ 409 | int port; 410 | String host; 411 | int proxyPort; 412 | String proxyHost; 413 | InetAddress ip; 414 | 415 | if(args.length > 1 && args.length < 5){ 416 | try{ 417 | 418 | host = args[0]; 419 | port = Integer.parseInt(args[1]); 420 | 421 | proxyPort =(args.length > 3)? Integer.parseInt(args[3]) 422 | : defaultProxyPort; 423 | 424 | host = args[0]; 425 | ip = InetAddress.getByName(host); 426 | 427 | proxyHost =(args.length > 2)? args[2] 428 | : defaultProxyHost; 429 | 430 | CProxy.setDefaultProxy(proxyHost,proxyPort); 431 | CProxy p = CProxy.getDefaultProxy(); 432 | p.addDirect("lux"); 433 | 434 | 435 | DatagramSocket ds = new Socks5DatagramSocket(); 436 | 437 | 438 | BufferedReader in = new BufferedReader( 439 | new InputStreamReader(System.in)); 440 | String s; 441 | 442 | System.out.print("Enter line:"); 443 | s = in.readLine(); 444 | 445 | while(s != null){ 446 | byte[] data = (s+"\r\n").getBytes(); 447 | DatagramPacket dp = new DatagramPacket(data,0,data.length, 448 | ip,port); 449 | System.out.println("Sending to: "+ip+":"+port); 450 | ds.send(dp); 451 | dp = new DatagramPacket(new byte[1024],1024); 452 | 453 | System.out.println("Trying to recieve on port:"+ 454 | ds.getLocalPort()); 455 | ds.receive(dp); 456 | System.out.print("Recieved:\n"+ 457 | "From:"+dp.getAddress()+":"+dp.getPort()+ 458 | "\n\n"+ 459 | new String(dp.getData(),dp.getOffset(),dp.getLength())+"\n" 460 | ); 461 | System.out.print("Enter line:"); 462 | s = in.readLine(); 463 | 464 | } 465 | ds.close(); 466 | System.exit(1); 467 | 468 | }catch(SocksException s_ex){ 469 | System.err.println("SocksException:"+s_ex); 470 | s_ex.printStackTrace(); 471 | System.exit(1); 472 | }catch(IOException io_ex){ 473 | io_ex.printStackTrace(); 474 | System.exit(1); 475 | }catch(NumberFormatException num_ex){ 476 | usage(); 477 | num_ex.printStackTrace(); 478 | System.exit(1); 479 | } 480 | 481 | }else{ 482 | usage(); 483 | } 484 | } 485 | */ 486 | 487 | } 488 | -------------------------------------------------------------------------------- /src/main/java/socks/Socks5Message.java: -------------------------------------------------------------------------------- 1 | package socks; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.io.DataInputStream; 7 | import java.net.InetAddress; 8 | import java.net.UnknownHostException; 9 | 10 | /** 11 | SOCKS5 request/response message. 12 | */ 13 | 14 | class Socks5Message extends ProxyMessage{ 15 | /** Address type of given message*/ 16 | public int addrType; 17 | 18 | byte[] data; 19 | 20 | /** 21 | Server error response. 22 | @param cmd Error code. 23 | */ 24 | public Socks5Message(int cmd){ 25 | super(cmd,null,0); 26 | data = new byte[3]; 27 | data[0] = SOCKS_VERSION; //Version. 28 | data[1] = (byte)cmd; //Reply code for some kind of failure. 29 | data[2] = 0; //Reserved byte. 30 | } 31 | 32 | /** 33 | Construct client request or server response. 34 | @param cmd - Request/Response code. 35 | @param ip - IP field. 36 | @paarm port - port field. 37 | */ 38 | public Socks5Message(int cmd,InetAddress ip,int port){ 39 | super(cmd,ip,port); 40 | this.host = ip==null?"0.0.0.0":ip.getHostName(); 41 | this.version = SOCKS_VERSION; 42 | 43 | byte[] addr; 44 | 45 | if(ip == null){ 46 | addr = new byte[4]; 47 | addr[0]=addr[1]=addr[2]=addr[3]=0; 48 | }else 49 | addr = ip.getAddress(); 50 | 51 | addrType = addr.length == 4 ? SOCKS_ATYP_IPV4 52 | : SOCKS_ATYP_IPV6; 53 | 54 | data = new byte[6+addr.length]; 55 | data[0] = (byte) SOCKS_VERSION; //Version 56 | data[1] = (byte) command; //Command 57 | data[2] = (byte) 0; //Reserved byte 58 | data[3] = (byte) addrType; //Address type 59 | 60 | //Put Address 61 | System.arraycopy(addr,0,data,4,addr.length); 62 | //Put port 63 | data[data.length-2] = (byte)(port>>8); 64 | data[data.length-1] = (byte)(port); 65 | } 66 | 67 | 68 | /** 69 | Construct client request or server response. 70 | @param cmd - Request/Response code. 71 | @param hostName - IP field as hostName, uses ADDR_TYPE of HOSTNAME. 72 | @paarm port - port field. 73 | */ 74 | public Socks5Message(int cmd,String hostName,int port){ 75 | super(cmd,null,port); 76 | this.host = hostName; 77 | this.version = SOCKS_VERSION; 78 | 79 | //System.out.println("Doing ATYP_DOMAINNAME"); 80 | 81 | addrType = SOCKS_ATYP_DOMAINNAME; 82 | byte addr[] = hostName.getBytes(); 83 | 84 | data =new byte[7+addr.length]; 85 | data[0] = (byte) SOCKS_VERSION; //Version 86 | data[1] = (byte) command; //Command 87 | data[2] = (byte) 0; //Reserved byte 88 | data[3] = (byte) SOCKS_ATYP_DOMAINNAME; //Address type 89 | data[4] = (byte) addr.length; //Length of the address 90 | 91 | //Put Address 92 | System.arraycopy(addr,0,data,5,addr.length); 93 | //Put port 94 | data[data.length-2] = (byte)(port >>8); 95 | data[data.length-1] = (byte)(port); 96 | } 97 | 98 | /** 99 | Initialises Message from the stream. Reads server response from 100 | given stream. 101 | @param in Input stream to read response from. 102 | @throws SocksException If server response code is not SOCKS_SUCCESS(0), or 103 | if any error with protocol occurs. 104 | @throws IOException If any error happens with I/O. 105 | */ 106 | public Socks5Message(InputStream in) throws SocksException, 107 | IOException{ 108 | this(in,true); 109 | } 110 | 111 | /** 112 | Initialises Message from the stream. Reads server response or client 113 | request from given stream. 114 | 115 | @param in Input stream to read response from. 116 | @param clinetMode If true read server response, else read client request. 117 | @throws SocksException If server response code is not SOCKS_SUCCESS(0) and 118 | reading in client mode, or if any error with protocol occurs. 119 | @throws IOException If any error happens with I/O. 120 | */ 121 | public Socks5Message(InputStream in,boolean clientMode)throws SocksException, 122 | IOException{ 123 | read(in,clientMode); 124 | } 125 | 126 | 127 | /** 128 | Initialises Message from the stream. Reads server response from 129 | given stream. 130 | @param in Input stream to read response from. 131 | @throws SocksException If server response code is not SOCKS_SUCCESS(0), or 132 | if any error with protocol occurs. 133 | @throws IOException If any error happens with I/O. 134 | */ 135 | public void read(InputStream in) throws SocksException, 136 | IOException{ 137 | read(in,true); 138 | } 139 | 140 | 141 | /** 142 | Initialises Message from the stream. Reads server response or client 143 | request from given stream. 144 | 145 | @param in Input stream to read response from. 146 | @param clinetMode If true read server response, else read client request. 147 | @throws SocksException If server response code is not SOCKS_SUCCESS(0) and 148 | reading in client mode, or if any error with protocol occurs. 149 | @throws IOException If any error happens with I/O. 150 | */ 151 | public void read(InputStream in,boolean clientMode) throws SocksException, 152 | IOException{ 153 | data = null; 154 | ip = null; 155 | 156 | DataInputStream di = new DataInputStream(in); 157 | 158 | version = di.readUnsignedByte(); 159 | command = di.readUnsignedByte(); 160 | if(clientMode && command != 0) 161 | throw new SocksException(command); 162 | 163 | int reserved = di.readUnsignedByte(); 164 | addrType = di.readUnsignedByte(); 165 | 166 | byte addr[]; 167 | 168 | switch(addrType){ 169 | case SOCKS_ATYP_IPV4: 170 | addr = new byte[4]; 171 | di.readFully(addr); 172 | host = bytes2IPV4(addr,0); 173 | break; 174 | case SOCKS_ATYP_IPV6: 175 | addr = new byte[SOCKS_IPV6_LENGTH];//I believe it is 16 bytes,huge! 176 | di.readFully(addr); 177 | host = bytes2IPV6(addr,0); 178 | break; 179 | case SOCKS_ATYP_DOMAINNAME: 180 | //System.out.println("Reading ATYP_DOMAINNAME"); 181 | addr = new byte[di.readUnsignedByte()];//Next byte shows the length 182 | di.readFully(addr); 183 | host = new String(addr); 184 | break; 185 | default: 186 | throw(new SocksException(CProxy.SOCKS_JUST_ERROR)); 187 | } 188 | 189 | port = di.readUnsignedShort(); 190 | 191 | if(addrType != SOCKS_ATYP_DOMAINNAME && doResolveIP){ 192 | try{ 193 | ip = InetAddress.getByName(host); 194 | }catch(UnknownHostException uh_ex){ 195 | } 196 | } 197 | } 198 | 199 | /** 200 | Writes the message to the stream. 201 | @param out Output stream to which message should be written. 202 | */ 203 | public void write(OutputStream out)throws SocksException, 204 | IOException{ 205 | if(data == null){ 206 | Socks5Message msg; 207 | 208 | if(addrType == SOCKS_ATYP_DOMAINNAME) 209 | msg = new Socks5Message(command,host,port); 210 | else{ 211 | if(ip == null){ 212 | try{ 213 | ip = InetAddress.getByName(host); 214 | }catch(UnknownHostException uh_ex){ 215 | throw new SocksException(CProxy.SOCKS_JUST_ERROR); 216 | } 217 | } 218 | msg = new Socks5Message(command,ip,port); 219 | } 220 | data = msg.data; 221 | } 222 | out.write(data); 223 | } 224 | 225 | /** 226 | Returns IP field of the message as IP, if the message was created 227 | with ATYP of HOSTNAME, it will attempt to resolve the hostname, 228 | which might fail. 229 | @throws UnknownHostException if host can't be resolved. 230 | */ 231 | public InetAddress getInetAddress() throws UnknownHostException{ 232 | if(ip!=null) return ip; 233 | 234 | return (ip=InetAddress.getByName(host)); 235 | } 236 | 237 | /** 238 | Returns string representation of the message. 239 | */ 240 | public String toString(){ 241 | String s= 242 | "Socks5Message:"+"\n"+ 243 | "VN "+version+"\n"+ 244 | "CMD "+command+"\n"+ 245 | "ATYP "+addrType+"\n"+ 246 | "ADDR "+host+"\n"+ 247 | "PORT "+port+"\n"; 248 | return s; 249 | } 250 | 251 | 252 | /** 253 | *Wether to resolve hostIP returned from SOCKS server 254 | *that is wether to create InetAddress object from the 255 | *hostName string 256 | */ 257 | static public boolean resolveIP(){ return doResolveIP;} 258 | 259 | /** 260 | *Wether to resolve hostIP returned from SOCKS server 261 | *that is wether to create InetAddress object from the 262 | *hostName string 263 | *@param doResolve Wether to resolve hostIP from SOCKS server. 264 | *@return Previous value. 265 | */ 266 | static public boolean resolveIP(boolean doResolve){ 267 | boolean old = doResolveIP; 268 | doResolveIP = doResolve; 269 | return old; 270 | } 271 | 272 | /* 273 | private static final void debug(String s){ 274 | if(DEBUG) 275 | System.out.print(s); 276 | } 277 | private static final boolean DEBUG = false; 278 | */ 279 | 280 | //SOCKS5 constants 281 | public static final int SOCKS_VERSION =5; 282 | 283 | public static final int SOCKS_ATYP_IPV4 =0x1; //Where is 2?? 284 | public static final int SOCKS_ATYP_DOMAINNAME =0x3; //!!!!rfc1928 285 | public static final int SOCKS_ATYP_IPV6 =0x4; 286 | 287 | public static final int SOCKS_IPV6_LENGTH =16; 288 | 289 | static boolean doResolveIP = true; 290 | 291 | } 292 | -------------------------------------------------------------------------------- /src/main/java/socks/Socks5Proxy.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 | SOCKS5 CProxy. 9 | */ 10 | 11 | public class Socks5Proxy extends CProxy implements Cloneable{ 12 | 13 | //Data members 14 | private Hashtable authMethods = new Hashtable(); 15 | private int selectedMethod; 16 | 17 | boolean resolveAddrLocally = true; 18 | UDPEncapsulation udp_encapsulation=null; 19 | 20 | 21 | //Public Constructors 22 | //==================== 23 | 24 | /** 25 | Creates SOCKS5 proxy. 26 | @param p CProxy to use to connect to this proxy, allows proxy chaining. 27 | @param proxyHost Host on which a CProxy server runs. 28 | @param proxyPort Port on which a CProxy server listens for connections. 29 | @throws UnknownHostException If proxyHost can't be resolved. 30 | */ 31 | public Socks5Proxy(CProxy p,String proxyHost,int proxyPort) 32 | throws UnknownHostException{ 33 | super(p,proxyHost,proxyPort); 34 | version = 5; 35 | setAuthenticationMethod(0,new AuthenticationNone()); 36 | } 37 | 38 | /** 39 | Creates SOCKS5 proxy. 40 | @param proxyHost Host on which a CProxy server runs. 41 | @param proxyPort Port on which a CProxy server listens for connections. 42 | @throws UnknownHostException If proxyHost can't be resolved. 43 | */ 44 | public Socks5Proxy(String proxyHost,int proxyPort) 45 | throws UnknownHostException{ 46 | this(null,proxyHost,proxyPort); 47 | } 48 | 49 | 50 | /** 51 | Creates SOCKS5 proxy. 52 | @param p CProxy to use to connect to this proxy, allows proxy chaining. 53 | @param proxyIP Host on which a CProxy server runs. 54 | @param proxyPort Port on which a CProxy server listens for connections. 55 | */ 56 | public Socks5Proxy(CProxy p,InetAddress proxyIP,int proxyPort){ 57 | super(p,proxyIP,proxyPort); 58 | version = 5; 59 | setAuthenticationMethod(0,new AuthenticationNone()); 60 | } 61 | 62 | /** 63 | Creates SOCKS5 proxy. 64 | @param proxyIP Host on which a CProxy server runs. 65 | @param proxyPort Port on which a CProxy server listens for connections. 66 | */ 67 | public Socks5Proxy(InetAddress proxyIP,int proxyPort){ 68 | this(null,proxyIP,proxyPort); 69 | } 70 | 71 | //Public instance methods 72 | //======================== 73 | 74 | 75 | /** 76 | * Wether to resolve address locally or to let proxy do so. 77 |

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 | *


 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 | *

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 |

    73 | 74 |
  1. CProxy settings say that address should be resolved locally, but 75 | this fails. 76 |
  2. CProxy settings say that the host should be contacted directly but 77 | host name can't be resolved. 78 |
79 | @throws SocksException 80 | If one of the following happens: 81 |
    82 |
  • CProxy is is null. 83 |
  • CProxy settings say that the host should be contacted directly but 84 | this fails. 85 |
  • Socks Server can't be contacted. 86 |
  • Authentication fails. 87 |
  • Connection is not allowed by the SOCKS proxy. 88 |
  • SOCKS proxy can't establish the connection. 89 |
  • Any IO error occured. 90 |
  • Any protocol error occured. 91 |
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 |

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.
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 |

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 |

 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 |

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 20 | At this point no data have been extracted from the connection. It is 21 | responsibility of this method to ensure that the next byte in the 22 | stream after this method have been called is the first byte of the 23 | socks request message. For SOCKSv4 there is no authentication data and 24 | the first byte in the stream is part of the request. With SOCKSv5 however 25 | there is an authentication data first. It is expected that implementaions 26 | will process this authentication data. 27 |

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!!
Should not be 17 | used on machines which are not behind the firewall. 18 |

19 | It is only provided to make implementing other authentication schemes 20 | easier.
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 |

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 0){ 56 | System.out.write(buf,0,bytes_read); 57 | System.out.flush(); 58 | } 59 | }catch(IOException io_ex){ 60 | io_ex.printStackTrace(); 61 | } 62 | } 63 | 64 | public static void usage(){ 65 | System.err.print( 66 | "Usage: java Echo host port [peerHost peerPort]\n"); 67 | } 68 | 69 | 70 | public static void main(String args[]){ 71 | int port; 72 | String host,peerHost; 73 | int peerPort; 74 | Echo echo = null; 75 | 76 | if(args.length > 1){ 77 | try{ 78 | 79 | host = args[0]; 80 | port = Integer.parseInt(args[1]); 81 | 82 | if(args.length ==4){ 83 | peerHost = args[2]; 84 | peerPort =Integer.parseInt(args[3]); 85 | echo = new Echo(host,port,peerHost,peerPort); 86 | }else{ 87 | echo = new Echo(host,port); 88 | } 89 | 90 | Thread thread = new Thread(echo); 91 | thread.start(); 92 | 93 | BufferedReader in = new BufferedReader( 94 | new InputStreamReader(System.in)); 95 | String s; 96 | 97 | s = in.readLine(); 98 | Thread.currentThread().setPriority(Thread.NORM_PRIORITY); 99 | while(s != null){ 100 | echo.send(s+"\r\n"); 101 | s = in.readLine(); 102 | } 103 | }catch(IOException io_ex){ 104 | io_ex.printStackTrace(); 105 | System.exit(1); 106 | }catch(NumberFormatException num_ex){ 107 | usage(); 108 | num_ex.printStackTrace(); 109 | System.exit(1); 110 | }finally{ 111 | if(echo!=null) try{echo.ss.close();}catch(Exception e){} 112 | } 113 | 114 | }else{ 115 | usage(); 116 | } 117 | } 118 | 119 | }//End of class 120 | -------------------------------------------------------------------------------- /src/test/java/SOCKS.java: -------------------------------------------------------------------------------- 1 | import java.util.*; 2 | import java.io.*; 3 | import java.net.*; 4 | import socks.server.*; 5 | import socks.*; 6 | 7 | public class SOCKS{ 8 | 9 | static public void usage(){ 10 | System.out.println( 11 | "Usage: java SOCKS [inifile1 inifile2 ...]\n"+ 12 | "If no inifile is given, uses socks.properties.\n" 13 | ); 14 | } 15 | 16 | static public void main(String[] args){ 17 | 18 | String[] file_names; 19 | int port=1080; 20 | String logFile = null; 21 | String host = null; 22 | 23 | IdentAuthenticator auth = new IdentAuthenticator(); 24 | OutputStream log = null; 25 | InetAddress localIP = null; 26 | 27 | if(args.length == 0){ 28 | file_names = new String[1]; 29 | file_names[0] = "socks.properties"; 30 | }else{ 31 | file_names = args; 32 | } 33 | 34 | 35 | Properties pr = null; 36 | inform("Loading properties..."); 37 | for(int i=0;i=0){ 165 | ProxyServer.setIddleTimeout(val); 166 | inform("Setting iddle timeout to "+val+" ms."); 167 | } 168 | val = readInt(props,"acceptTimeout"); 169 | if(val>=0){ 170 | ProxyServer.setAcceptTimeout(val); 171 | inform("Setting accept timeout to "+val+" ms."); 172 | } 173 | val = readInt(props,"udpTimeout"); 174 | if(val>=0){ 175 | ProxyServer.setUDPTimeout(val); 176 | inform("Setting udp timeout to "+val+" ms."); 177 | } 178 | 179 | val = readInt(props,"datagramSize"); 180 | if(val>=0){ 181 | ProxyServer.setDatagramSize(val); 182 | inform("Setting datagram size to "+val+" bytes."); 183 | } 184 | 185 | proxyInit(props); 186 | 187 | } 188 | 189 | /** 190 | Initialises proxy, if any specified. 191 | */ 192 | static void proxyInit(Properties props){ 193 | String proxy_list; 194 | CProxy proxy = null; 195 | StringTokenizer st; 196 | 197 | proxy_list = (String) props.get("proxy"); 198 | if(proxy_list == null) return; 199 | 200 | st = new StringTokenizer(proxy_list,";"); 201 | while(st.hasMoreTokens()){ 202 | String proxy_entry = st.nextToken(); 203 | 204 | CProxy p = CProxy.parseProxy(proxy_entry); 205 | 206 | if(p == null) 207 | exit("Can't parse proxy entry:"+proxy_entry); 208 | 209 | 210 | inform("Adding CProxy:"+p); 211 | 212 | if(proxy != null) 213 | p.setChainProxy(proxy); 214 | 215 | proxy = p; 216 | 217 | } 218 | if(proxy == null) return; //Empty list 219 | 220 | String direct_hosts = (String) props.get("directHosts"); 221 | if(direct_hosts!=null){ 222 | InetRange ir = parseInetRange(direct_hosts); 223 | inform("Setting direct hosts:"+ir); 224 | proxy.setDirect(ir); 225 | } 226 | 227 | 228 | ProxyServer.setProxy(proxy); 229 | } 230 | /** 231 | Inits range from the string of semicolon separated ranges. 232 | */ 233 | static InetRange parseInetRange(String source){ 234 | InetRange irange = new InetRange(); 235 | 236 | StringTokenizer st = new StringTokenizer(source,";"); 237 | while(st.hasMoreTokens()) 238 | irange.add(st.nextToken()); 239 | 240 | return irange; 241 | } 242 | 243 | /** 244 | Integer representaion of the property named name, or -1 if one 245 | is not found. 246 | */ 247 | static int readInt(Properties props,String name){ 248 | int result = -1; 249 | String val = (String) props.get(name); 250 | if(val==null) return -1; 251 | StringTokenizer st = new StringTokenizer(val); 252 | if(!st.hasMoreElements()) return -1; 253 | try{ 254 | result = Integer.parseInt(st.nextToken()); 255 | }catch(NumberFormatException nfe){ 256 | inform("Bad value for "+name+":"+val); 257 | } 258 | return result; 259 | } 260 | 261 | //Display functions 262 | /////////////////// 263 | 264 | static void inform(String s){ 265 | System.out.println(s); 266 | } 267 | 268 | static void exit(String msg){ 269 | System.err.println("Error:"+msg); 270 | System.err.println("Aborting operation"); 271 | System.exit(0); 272 | } 273 | 274 | private static Properties load_defaults() { 275 | Properties sRet = new Properties(); 276 | 277 | sRet.setProperty("port", "1080"); 278 | 279 | sRet.setProperty("range", "."); 280 | 281 | sRet.setProperty("iddleTimeout", "600000"); 282 | sRet.setProperty("acceptTimeout", "60000"); 283 | sRet.setProperty("udpTimeout", "600000"); 284 | 285 | sRet.setProperty("log", "-"); 286 | 287 | return sRet; 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/test/java/SocksEcho.java: -------------------------------------------------------------------------------- 1 | import java.awt.*; 2 | import java.awt.event.*; 3 | import java.net.*; 4 | import java.io.*; 5 | import socks.*; 6 | 7 | public class SocksEcho extends Frame 8 | implements ActionListener, 9 | Runnable, 10 | WindowListener{ 11 | 12 | //GUI components 13 | TextField host_text, 14 | port_text, 15 | input_text; 16 | Button proxy_button, 17 | accept_button, 18 | clear_button, 19 | connect_button, 20 | udp_button, 21 | quit_button; 22 | TextArea output_textarea; 23 | Label status_label; 24 | 25 | SocksDialog socks_dialog; 26 | 27 | //Network related members 28 | CProxy proxy=null; 29 | int port; 30 | String host; 31 | Thread net_thread=null; 32 | InputStream in=null; 33 | OutputStream out=null; 34 | Socket sock=null; 35 | ServerSocket server_sock = null; 36 | Socks5DatagramSocket udp_sock; 37 | 38 | Object net_lock = new Object(); 39 | int mode=COMMAND_MODE; 40 | 41 | //Possible mode states. 42 | static final int LISTEN_MODE = 0; 43 | static final int CONNECT_MODE = 1; 44 | static final int UDP_MODE = 2; 45 | static final int COMMAND_MODE = 3; 46 | static final int ABORT_MODE = 4; 47 | 48 | //Maximum datagram size 49 | static final int MAX_DATAGRAM_SIZE = 1024; 50 | 51 | // Constructors 52 | //////////////////////////////////// 53 | public SocksEcho(){ 54 | super("SocksEcho"); 55 | guiInit(); 56 | socks_dialog = new SocksDialog(this); 57 | socks_dialog.useThreads = false; 58 | 59 | 60 | URL icon_url = SocksEcho.class.getResource("SocksEcho.gif"); 61 | if(icon_url != null){ 62 | try{ 63 | Object content = icon_url.getContent(); 64 | if(content instanceof java.awt.image.ImageProducer) 65 | setIconImage(createImage((java.awt.image.ImageProducer) content)); 66 | }catch(IOException ioe){ 67 | } 68 | } 69 | 70 | addWindowListener(this); 71 | Component component[] = getComponents(); 72 | for(int i=0;i 0){ 387 | print(new String(buf,0,bytes_read)); 388 | } 389 | 390 | } 391 | private void startUDP() throws IOException{ 392 | udp_sock = new Socks5DatagramSocket(proxy,0,null); 393 | println("UDP started on "+udp_sock.getLocalAddress()+":"+ 394 | udp_sock.getLocalPort()); 395 | status("UDP:"+udp_sock.getLocalAddress().getHostAddress()+":" 396 | +udp_sock.getLocalPort()); 397 | } 398 | 399 | private void doUDPPipe() throws IOException{ 400 | DatagramPacket dp = new DatagramPacket(new byte[MAX_DATAGRAM_SIZE], 401 | MAX_DATAGRAM_SIZE); 402 | while(true){ 403 | udp_sock.receive(dp); 404 | print("UDP\n"+ 405 | "From:"+dp.getAddress()+":"+dp.getPort()+"\n"+ 406 | "\n"+ 407 | //Java 1.2 408 | //new String(dp.getData(),dp.getOffset(),dp.getLength())+"\n" 409 | //Java 1.1 410 | new String(dp.getData(),0,dp.getLength())+"\n" 411 | ); 412 | dp.setLength(MAX_DATAGRAM_SIZE); 413 | } 414 | } 415 | 416 | private void sendUDP(String message,String host,int port){ 417 | if(!udp_sock.isProxyAlive(100)){ 418 | status("CProxy closed connection"); 419 | abort_connection(); 420 | return; 421 | } 422 | 423 | try{ 424 | byte[] data = message.getBytes(); 425 | DatagramPacket dp = new DatagramPacket(data,data.length,null,port); 426 | udp_sock.send(dp,host); 427 | }catch(UnknownHostException uhe){ 428 | status("Host "+host+" has no DNS entry."); 429 | }catch(IOException ioe){ 430 | status("IOException:"+ioe); 431 | abort_connection(); 432 | } 433 | 434 | } 435 | 436 | private void send(String s){ 437 | try{ 438 | out.write(s.getBytes()); 439 | }catch(IOException io_ex){ 440 | println("IOException:"+io_ex); 441 | abort_connection(); 442 | } 443 | } 444 | 445 | 446 | /*====================================================================== 447 | Form: 448 | Table: 449 | +---+---------------+ 450 | | | | 451 | +---+---+---+---+---+ 452 | | | | | | | 453 | +---+---+---+---+---+ 454 | | | 455 | +-------------------+ 456 | | | 457 | +---+---+---+---+---+ 458 | | | | | | | 459 | +---+---+---+---+---+ 460 | | | 461 | +-------------------+ 462 | */ 463 | 464 | void guiInit(){ 465 | //Some default names used 466 | Label label; 467 | Container container; 468 | 469 | GridBagConstraints c = new GridBagConstraints(); 470 | 471 | container = this; 472 | //container = new Panel(); 473 | container.setLayout(new GridBagLayout()); 474 | container.setBackground(SystemColor.menu); 475 | c.insets = new Insets(3,3,3,3); 476 | 477 | c.gridx=0; c.gridy=0; 478 | c.gridwidth=1; c.gridheight=1; 479 | c.anchor=GridBagConstraints.NORTHEAST; 480 | label = new Label("Host:"); 481 | container.add(label,c); 482 | 483 | c.gridx=0; c.gridy=1; 484 | c.gridwidth=1; c.gridheight=1; 485 | c.anchor=GridBagConstraints.NORTHEAST; 486 | label = new Label("Port:"); 487 | container.add(label,c); 488 | 489 | c.gridx = 0; c.gridy = 5; 490 | c.gridwidth=GridBagConstraints.REMAINDER;c.gridheight=1; 491 | c.fill = GridBagConstraints.HORIZONTAL; 492 | c.insets = new Insets(0,0,0,0); 493 | status_label = new Label(""); 494 | container.add(status_label,c); 495 | c.insets = new Insets(3,3,3,3); 496 | 497 | 498 | c.gridx=1; c.gridy=0; 499 | c.gridwidth=GridBagConstraints.REMAINDER; c.gridheight=1; 500 | c.anchor=GridBagConstraints.NORTHWEST; 501 | c.fill = GridBagConstraints.HORIZONTAL; 502 | host_text = new TextField(""); 503 | container.add(host_text,c); 504 | 505 | 506 | c.weightx = 1.0; 507 | c.fill = GridBagConstraints.NONE; 508 | c.gridx=1; c.gridy=1; 509 | c.gridwidth=1; c.gridheight=1; 510 | c.anchor=GridBagConstraints.NORTHWEST; 511 | port_text = new TextField("",5); 512 | container.add(port_text,c); 513 | 514 | c.weightx = 0.0; 515 | c.gridx=0; c.gridy=3; 516 | c.fill = GridBagConstraints.HORIZONTAL; 517 | c.gridwidth=GridBagConstraints.REMAINDER; c.gridheight=1; 518 | c.anchor=GridBagConstraints.NORTHWEST; 519 | input_text = new TextField(""); 520 | container.add(input_text,c); 521 | 522 | c.fill = GridBagConstraints.NONE; 523 | c.gridx=2; c.gridy=1; 524 | c.gridwidth=1; c.gridheight=1; 525 | c.anchor=GridBagConstraints.NORTHEAST; 526 | connect_button = new Button("Connect"); 527 | container.add(connect_button,c); 528 | 529 | c.gridx=3; c.gridy=1; 530 | c.gridwidth=1; c.gridheight=1; 531 | c.anchor=GridBagConstraints.EAST; 532 | accept_button = new Button("Accept"); 533 | container.add(accept_button,c); 534 | 535 | 536 | c.gridx=4; c.gridy=1; 537 | c.gridwidth=1; c.gridheight=1; 538 | c.anchor=GridBagConstraints.EAST; 539 | udp_button = new Button("UDP"); 540 | container.add(udp_button,c); 541 | 542 | 543 | c.gridx=0; c.gridy=4; 544 | c.gridwidth=1; c.gridheight=1; 545 | c.anchor=GridBagConstraints.NORTHWEST; 546 | proxy_button = new Button("CProxy..."); 547 | container.add(proxy_button,c); 548 | 549 | c.gridx=3; c.gridy=4; 550 | c.gridwidth=1; c.gridheight=1; 551 | c.anchor=GridBagConstraints.NORTHEAST; 552 | clear_button = new Button("Clear"); 553 | container.add(clear_button,c); 554 | 555 | c.gridx=4; c.gridy=4; 556 | c.gridwidth=1; c.gridheight=1; 557 | c.anchor=GridBagConstraints.EAST; 558 | quit_button = new Button("Quit"); 559 | container.add(quit_button,c); 560 | 561 | c.weightx = 1.0; 562 | c.weighty = 1.0; 563 | c.fill = GridBagConstraints.BOTH; 564 | c.gridx=0; c.gridy=2; 565 | c.gridwidth=GridBagConstraints.REMAINDER; c.gridheight=1; 566 | c.anchor=GridBagConstraints.NORTHWEST; 567 | output_textarea = new TextArea("",10,50); 568 | output_textarea.setFont(new Font("Monospaced",Font.PLAIN,11)); 569 | output_textarea.setEditable(false); 570 | //output_textarea.setEnabled(false); 571 | container.add(output_textarea,c); 572 | 573 | }//end guiInit 574 | 575 | 576 | // WindowListener Interface 577 | ///////////////////////////////// 578 | public void windowActivated(java.awt.event.WindowEvent e){ 579 | } 580 | public void windowDeactivated(java.awt.event.WindowEvent e){ 581 | } 582 | public void windowOpened(java.awt.event.WindowEvent e){ 583 | } 584 | public void windowClosing(java.awt.event.WindowEvent e){ 585 | if(e.getWindow() == this) onQuit(); 586 | else 587 | e.getWindow().dispose(); 588 | } 589 | public void windowClosed(java.awt.event.WindowEvent e){ 590 | } 591 | public void windowIconified(java.awt.event.WindowEvent e){ 592 | } 593 | public void windowDeiconified(java.awt.event.WindowEvent e){ 594 | } 595 | 596 | 597 | // Main 598 | //////////////////////////////////// 599 | public static void main(String[] args){ 600 | SocksEcho socksecho = new SocksEcho(); 601 | socksecho.pack(); 602 | socksecho.show(); 603 | } 604 | }//end class 605 | -------------------------------------------------------------------------------- /src/test/java/SocksTest.java: -------------------------------------------------------------------------------- 1 | import java.net.*; 2 | import java.io.*; 3 | import socks.*; 4 | 5 | /** SOCKS aware echo client*/ 6 | 7 | public class SocksTest implements Runnable{ 8 | 9 | private int port; 10 | private InetAddress hostIP; 11 | 12 | private Socket ss; 13 | private InputStream in; 14 | private OutputStream out; 15 | 16 | private static final int BUF_SIZE = 1024; 17 | static final int defaultProxyPort = 1080; //Default Port 18 | static final String defaultProxyHost = "www-proxy"; //Default proxy 19 | 20 | public SocksTest(String host,int port) 21 | throws IOException,UnknownHostException,SocksException{ 22 | this.port = port; 23 | 24 | ss = new SocksSocket(host, port); 25 | out = ss.getOutputStream(); 26 | in = ss.getInputStream(); 27 | System.out.println("Connected..."); 28 | System.out.println("TO: "+host+":"+port); 29 | System.out.println("ViaProxy: "+ss.getLocalAddress().getHostAddress() 30 | +":"+ss.getLocalPort()); 31 | 32 | } 33 | 34 | public void close()throws IOException{ 35 | ss.close(); 36 | } 37 | public void send(String s) throws IOException{ 38 | out.write(s.getBytes()); 39 | } 40 | 41 | public void run(){ 42 | byte[] buf = new byte[1024]; 43 | int bytes_read; 44 | try{ 45 | while((bytes_read = in.read(buf)) > 0){ 46 | System.out.write(buf,0,bytes_read); 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 SocksTest host port [socksHost socksPort]\n"); 56 | } 57 | 58 | 59 | public static void main(String args[]){ 60 | int port; 61 | String host; 62 | int proxyPort; 63 | String proxyHost; 64 | 65 | if(args.length > 1 && args.length < 5){ 66 | try{ 67 | 68 | host = args[0]; 69 | port = Integer.parseInt(args[1]); 70 | 71 | proxyPort =(args.length > 3)? Integer.parseInt(args[3]) 72 | : defaultProxyPort; 73 | 74 | host = args[0]; 75 | proxyHost =(args.length > 2)? args[2] 76 | : defaultProxyHost; 77 | 78 | CProxy.setDefaultProxy(proxyHost,proxyPort,"KOUKY001"); 79 | //Proxy.setDefaultProxy(proxyHost,proxyPort); 80 | 81 | //rsimac: commented out below line to make the source compile on java 1.6 82 | //I believe below line was -only- disabling the proxy for localhost. 83 | //TBD understand and fix properly 84 | //CProxy.getDefaultProxy().setDirect(InetAddress.getByName("localhost")); 85 | 86 | 87 | SocksTest st = new SocksTest(host,port); 88 | Thread thread = new Thread(st); 89 | thread.start(); 90 | 91 | BufferedReader in = new BufferedReader( 92 | new InputStreamReader(System.in)); 93 | String s; 94 | 95 | s = in.readLine(); 96 | while(s != null){ 97 | st.send(s+"\r\n"); 98 | //try{ 99 | //Thread.currentThread().sleep(10); 100 | //}catch(InterruptedException i_ex){ 101 | //} 102 | s = in.readLine(); 103 | } 104 | st.close(); 105 | System.exit(1); 106 | 107 | }catch(SocksException s_ex){ 108 | System.err.println("SocksException:"+s_ex); 109 | s_ex.printStackTrace(); 110 | System.exit(1); 111 | }catch(IOException io_ex){ 112 | io_ex.printStackTrace(); 113 | System.exit(1); 114 | }catch(NumberFormatException num_ex){ 115 | usage(); 116 | num_ex.printStackTrace(); 117 | System.exit(1); 118 | } 119 | 120 | }else{ 121 | usage(); 122 | } 123 | } 124 | 125 | }//End of class 126 | -------------------------------------------------------------------------------- /src/test/java/SocksUDPEcho.java: -------------------------------------------------------------------------------- 1 | import java.net.*; 2 | import java.io.*; 3 | import socks.*; 4 | 5 | /** SOCKS aware UDP echo client.
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 |

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 | --------------------------------------------------------------------------------