Path: blob/master/test/jdk/java/net/Socks/SocksServer.java
41149 views
/*1* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/22import java.net.*;23import java.io.*;24import java.util.HashMap;2526public class SocksServer extends Thread implements Closeable {27// Some useful SOCKS constant2829static final int PROTO_VERS4 = 4;30static final int PROTO_VERS = 5;31static final int DEFAULT_PORT = 1080;3233static final int NO_AUTH = 0;34static final int GSSAPI = 1;35static final int USER_PASSW = 2;36static final int NO_METHODS = -1;3738static final int CONNECT = 1;39static final int BIND = 2;40static final int UDP_ASSOC = 3;4142static final int IPV4 = 1;43static final int DOMAIN_NAME = 3;44static final int IPV6 = 4;4546static final int REQUEST_OK = 0;47static final int GENERAL_FAILURE = 1;48static final int NOT_ALLOWED = 2;49static final int NET_UNREACHABLE = 3;50static final int HOST_UNREACHABLE = 4;51static final int CONN_REFUSED = 5;52static final int TTL_EXPIRED = 6;53static final int CMD_NOT_SUPPORTED = 7;54static final int ADDR_TYPE_NOT_SUP = 8;5556private int port;57private ServerSocket server;58private boolean useV4 = false;59private HashMap<String,String> users = new HashMap<>();60private volatile boolean done = false;61// Inner class to handle protocol with client62// This is the bulk of the work (protocol handler)63class ClientHandler extends Thread {64private InputStream in;65private OutputStream out;66private Socket client;67private Socket dest;6869// Simple tunneling class, moving bits from one stream to another7071class Tunnel extends Thread {72private InputStream tin;73private OutputStream tout;7475Tunnel(InputStream in, OutputStream out) {76tin = in;77tout = out;78}7980public void run() {81int b;82while (true) {83try {84b = tin.read();85if (b == -1) {86tin.close();87tout.close();88return;89}90tout.write(b);91tout.flush();92} catch (IOException e) {93// actually exit from the thread94return;95}96}97}98}99100ClientHandler(Socket s) throws IOException {101client = s;102in = new BufferedInputStream(client.getInputStream());103out = new BufferedOutputStream(client.getOutputStream());104}105106private void readBuf(InputStream is, byte[] buf) throws IOException {107int l = buf.length;108int count = 0;109int i;110do {111i = is.read(buf, count, l - count);112if (i == -1)113throw new IOException("unexpected EOF");114count += i;115} while (count < l);116}117118119private boolean userPassAuth() throws IOException {120int ver = in.read();121int ulen = in.read();122if (ulen <= 0)123throw new SocketException("SOCKS protocol error");124byte[] buf = new byte[ulen];125readBuf(in, buf);126String uname = new String(buf);127String password = null;128ulen = in.read();129if (ulen < 0)130throw new SocketException("SOCKS protocol error");131if (ulen > 0) {132buf = new byte[ulen];133readBuf(in, buf);134password = new String(buf);135}136// Check username/password validity here137System.err.println("User: '" + uname);138System.err.println("PSWD: '" + password);139if (users.containsKey(uname)) {140String p1 = users.get(uname);141System.err.println("p1 = " + p1);142if (p1.equals(password)) {143out.write(PROTO_VERS);144out.write(REQUEST_OK);145out.flush();146return true;147}148}149out.write(PROTO_VERS);150out.write(NOT_ALLOWED);151out.flush();152return false;153}154155private void purge() throws IOException {156boolean done = false;157int i = 0;158client.setSoTimeout(1000);159while(!done && i != -1) {160try {161i = in.read();162} catch(IOException e) {163done = true;164}165}166}167168169// Handle the SOCKS version 4 protocl170171private void getRequestV4() throws IOException {172int ver = in.read();173int cmd = in.read();174if (ver == -1 || cmd == -1) {175// EOF176in.close();177out.close();178return;179}180181if (ver != 0 && ver != 4) {182out.write(PROTO_VERS4);183out.write(91); // Bad Request184out.write(0);185out.write(0);186out.write(0);187out.write(0);188out.write(0);189out.write(0);190out.write(0);191out.flush();192purge();193out.close();194in.close();195return;196}197198if (cmd == CONNECT) {199int port = ((in.read() & 0xff) << 8);200port += (in.read() & 0xff);201byte[] buf = new byte[4];202readBuf(in, buf);203InetAddress addr = InetAddress.getByAddress(buf);204// We don't use the username...205int c;206do {207c = (in.read() & 0xff);208} while (c!=0);209boolean ok = true;210try {211dest = new Socket(addr, port);212} catch (IOException e) {213ok = false;214}215if (!ok) {216out.write(PROTO_VERS4);217out.write(91);218out.write(0);219out.write(0);220out.write(buf);221out.flush();222purge();223out.close();224in.close();225return;226}227out.write(PROTO_VERS4);228out.write(90); // Success229out.write((port >> 8) & 0xff);230out.write(port & 0xff);231out.write(buf);232out.flush();233InputStream in2 = new BufferedInputStream(dest.getInputStream());234OutputStream out2 = new BufferedOutputStream(dest.getOutputStream());235236Tunnel tunnel = new Tunnel(in2, out);237tunnel.start();238239int b = 0;240do {241try {242b = in.read();243if (b == -1) {244in.close();245out2.close();246return;247}248out2.write(b);249out2.flush();250} catch (IOException ex) {251}252} while (!client.isClosed());253}254}255256257// Negociate the authentication scheme with the client258private void negociate() throws IOException {259int ver = in.read();260int n = in.read();261byte[] buf = null;262if (n > 0) {263buf = new byte[n];264readBuf(in, buf);265}266int scheme = NO_AUTH;267for (int i = 0; i < n; i++)268if (buf[i] == USER_PASSW)269scheme = USER_PASSW;270out.write(PROTO_VERS);271out.write(scheme);272out.flush();273if (scheme == USER_PASSW)274userPassAuth();275}276277// Send error message then close the streams278private void sendError(int code) {279try {280out.write(PROTO_VERS);281out.write(code);282out.write(0);283out.write(IPV4);284for (int i=0; i<6; i++)285out.write(0);286out.flush();287out.close();288} catch (IOException ex) {289}290}291292// Actually connect the proxy to the destination then initiate tunneling293294private void doConnect(InetSocketAddress addr) throws IOException {295dest = new Socket();296try {297dest.connect(addr, 10000);298} catch (SocketTimeoutException ex) {299sendError(HOST_UNREACHABLE);300return;301} catch (ConnectException cex) {302sendError(CONN_REFUSED);303return;304}305// Success306InetAddress iadd = addr.getAddress();307if (iadd instanceof Inet4Address) {308out.write(PROTO_VERS);309out.write(REQUEST_OK);310out.write(0);311out.write(IPV4);312out.write(iadd.getAddress());313} else if (iadd instanceof Inet6Address) {314out.write(PROTO_VERS);315out.write(REQUEST_OK);316out.write(0);317out.write(IPV6);318out.write(iadd.getAddress());319} else {320sendError(GENERAL_FAILURE);321return;322}323out.write((addr.getPort() >> 8) & 0xff);324out.write((addr.getPort() >> 0) & 0xff);325out.flush();326327InputStream in2 = new BufferedInputStream(dest.getInputStream());328OutputStream out2 = new BufferedOutputStream(dest.getOutputStream());329330Tunnel tunnel = new Tunnel(in2, out);331tunnel.start();332333int b = 0;334do {335// Note that the socket might be closed from another thread (the tunnel)336try {337b = in.read();338if (b == -1) {339in.close();340out2.close();341return;342}343out2.write(b);344out2.flush();345} catch(IOException ioe) {346}347} while (!client.isClosed());348}349350private void doBind(InetSocketAddress addr) throws IOException {351ServerSocket svr = new ServerSocket();352svr.bind(null);353InetSocketAddress bad = (InetSocketAddress) svr.getLocalSocketAddress();354out.write(PROTO_VERS);355out.write(REQUEST_OK);356out.write(0);357out.write(IPV4);358out.write(bad.getAddress().getAddress());359out.write((bad.getPort() >> 8) & 0xff);360out.write((bad.getPort() & 0xff));361out.flush();362dest = svr.accept();363bad = (InetSocketAddress) dest.getRemoteSocketAddress();364out.write(PROTO_VERS);365out.write(REQUEST_OK);366out.write(0);367out.write(IPV4);368out.write(bad.getAddress().getAddress());369out.write((bad.getPort() >> 8) & 0xff);370out.write((bad.getPort() & 0xff));371out.flush();372InputStream in2 = dest.getInputStream();373OutputStream out2 = dest.getOutputStream();374375Tunnel tunnel = new Tunnel(in2, out);376tunnel.start();377378int b = 0;379do {380// Note that the socket might be close from another thread (the tunnel)381try {382b = in.read();383if (b == -1) {384in.close();385out2.close();386return;387}388out2.write(b);389out2.flush();390} catch(IOException ioe) {391}392} while (!client.isClosed());393394}395396// Handle the SOCKS v5 requests397398private void getRequest() throws IOException {399int ver = in.read();400int cmd = in.read();401if (ver == -1 || cmd == -1) {402in.close();403out.close();404return;405}406int rsv = in.read();407int atyp = in.read();408String addr = null;409int port = 0;410411switch(atyp) {412case IPV4:413{414byte[] buf = new byte[4];415readBuf(in, buf);416addr = InetAddress.getByAddress(buf).getHostAddress();417}418break;419case DOMAIN_NAME:420{421int i = in.read();422byte[] buf = new byte[i];423readBuf(in, buf);424addr = new String(buf);425}426break;427case IPV6:428{429byte[] buf = new byte[16];430readBuf(in, buf);431addr = InetAddress.getByAddress(buf).getHostAddress();432}433break;434}435436port = ((in.read()&0xff) << 8);437port += (in.read()&0xff);438439InetSocketAddress socAddr = new InetSocketAddress(addr, port);440switch(cmd) {441case CONNECT:442doConnect(socAddr);443break;444case BIND:445doBind(socAddr);446break;447case UDP_ASSOC:448// doUDP(socAddr);449break;450}451}452453public void run() {454String line = null;455try {456if (useV4) {457getRequestV4();458} else {459negociate();460getRequest();461}462} catch (IOException ex) {463try {464sendError(GENERAL_FAILURE);465} catch (Exception e) {466}467} finally {468try {469client.close();470} catch (IOException e2) {471}472}473}474475}476477public SocksServer(int port, boolean v4) throws IOException {478this(port);479this.useV4 = v4;480}481482public SocksServer(int port) throws IOException {483this.port = port;484server = new ServerSocket();485if (port == 0) {486server.bind(null);487this.port = server.getLocalPort();488} else {489server.bind(new InetSocketAddress(port));490}491}492493public SocksServer(InetAddress addr, int port, boolean useV4) throws IOException {494this.port = port;495this.useV4 = useV4;496server = new ServerSocket();497if (port == 0 && addr == null) {498server.bind(null);499this.port = server.getLocalPort();500} else if (port == 0 && addr != null) {501server.bind(new InetSocketAddress(addr, 0));502this.port = server.getLocalPort();503} else if (addr == null) {504assert port != 0;505server.bind(new InetSocketAddress(port));506} else {507assert port != 0;508server.bind(new InetSocketAddress(addr, port));509}510}511512public SocksServer() throws IOException {513this (DEFAULT_PORT);514}515516public void addUser(String user, String passwd) {517users.put(user, passwd);518}519520public int getPort() {521return port;522}523524public void close() {525done = true;526try { server.close(); } catch (IOException unused) {}527}528529public void run() {530ClientHandler cl = null;531while (!done) {532try {533Socket s = server.accept();534cl = new ClientHandler(s);535cl.start();536} catch (IOException ex) {537if (cl != null)538cl.interrupt();539}540}541}542}543544545