java socks5 구현

10656 단어 socks5
자세히 보기
socks5의 기초 지식
socks5에 대한 정의]https://www.ietf.org/rfc/rfc1928.txt[/b[b] socks5 계정 비밀번호 인증 방식https://www.ietf.org/rfc/rfc1929.txt
socks5 상호 작용 프로세스
1.socks5 서버 오픈 포트
2. 클라이언트가 sockets 서버에 연결
3. 클라이언트가 Hello 메시지를 보내는데 구조는 다음과 같다.
VER
NMETHODS
METHODS
1바이트
1바이트
최대 255바이트
X05는 socks5를 나타내며, 임의의 identifier일 수도 있습니다.
세 번째 필드를 나타내는 데이터 길이(byte 수)
클라이언트가 지원하는 검증 방식, 모든 방법이 바이트를 차지한다
그 중에서 X02는 계정 비밀번호 검증을 표시하는데, 우리는 여기에서 이 방법만 실현할 수 있다
ClientHelloInfo clientHelloInfo = new ClientHelloInfo();
		clientHelloInfo.setVer(inputStream.read());
		clientHelloInfo.setNmethods(inputStream.read());
		
		if(clientHelloInfo.getNmethods() > 0) {
			clientHelloInfo.setMethods(read(clientHelloInfo.getNmethods()));
		}

4. 서비스 측은 다음과 같은 메시지에 회답해야 한다
VER
METHOD
1바이트
1바이트
X05는 socks5를 나타냅니다.
서비스 측이 선택한 검증 방법
METHODS
o  X'00' NO AUTHENTICATION REQUIRED
o  X'01' GSSAPI
o  X'02' USERNAME/PASSWORD
o  X'03' to X'7F' IANA ASSIGNED
o  X'80' to X'FE' RESERVED FOR PRIVATE METHODS
o  X'FF' NO ACCEPTABLE METHODS
만약에 서비스 측이 마지막으로 X'FF'를 선택했다면 적합한 검증 방법이 없고 클라이언트와 서비스 측의 검증 방안이 일치하지 않는다는 것을 의미한다. 이런 상황은 닫힐 수밖에 없다는 것을 의미한다.
public void writeDatas(ServerHelloInfo serverHelloInfo) throws IOException {
		outputStream.write(serverHelloInfo.getVer());
		outputStream.write(serverHelloInfo.getMethod());
		outputStream.flush();
	}

5. 클라이언트가 계정 암호 메시지를 보냅니다(계정 암호 인증이 아닌 경우 건너뛰기)
VER
ULEN
UNAME
PLEN
PASSWD
1바이트, 여기는 SOCKS 버전이 아닙니다.
1바이트, 사용자 이름이 몇 바이트인지 지정합니다.
최대 255바이트
암호 바이트 지정
최대 255바이트
public ClientUserPasswordInfo readDatas() throws IOException {
		ClientUserPasswordInfo clientUserPasswordInfo = new ClientUserPasswordInfo();
		
		clientUserPasswordInfo.setVer(read());
		clientUserPasswordInfo.setUlen(read());
		clientUserPasswordInfo.setUname(new String(read(clientUserPasswordInfo.getUlen())));
		clientUserPasswordInfo.setPlen(read());
		clientUserPasswordInfo.setPassword(new String(read(clientUserPasswordInfo.getPlen())));
		
		return clientUserPasswordInfo;
	}
	

6. 서버 반환 (계정 암호 인증이 아닌 경우 건너뛰기)
VER
STATUS
현재 SOCKS5의 암호 인증 프로토콜 버전이 1이므로 1바이트만 지원됩니다.
0X00은 올바른, 다른 것은 비밀번호 오류, 연결 닫기
public void writeDatas(ServerUserPasswordInfo serverUserPasswordInfo) throws IOException {
		outputStream.write(serverUserPasswordInfo.getVer());
		outputStream.write(serverUserPasswordInfo.getStatus());
		outputStream.flush();
	}

7. 클라이언트 요청 에이전트 대상
VER
CMD
  RSV 
ATYP
DST.ADDR
DST.PORT
1바이트
1바이트
X'00'
2바이트
대상 IP
대상 포트 2바이트
  • o VER 프로토콜 버전: X'05'
  • o CMD 세 가지 명령 모드
  • o CONNECT 모드 X'01'
  • o BIND 모드 X'02'(포트 감청을 켜면 클라이언트가 이 포트에 연결해야 하는 데이터가 대상 IP 포트로 직접 전송됨)
  • o UDP 모드 X'03'

  • .RSV 보존
  • o 아래 주소의 ATYP 주소 유형입니다
  • o IP V4 주소: X'01'
  • o DOMAINNAME: X'03'(도메인 이름 모드)
  • o IP V6 주소: X'04'
  • o DST.ADDR에 필요한 대상 주소입니다
  • o DST.PORT 네트워크 8바이트에 필요한 대상 포트입니다

  • IP4 모드, 4바이트
    IP6 모드, 16바이트
    도메인 이름 모드, 주소 첫 번째 필드는 도메인 이름 길이(255 최대)를 나타냅니다.
    주소 포트 확인
    포트 2 바이트
    
    package com.lsiding.nat.util;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.net.SocketTimeoutException;
    
    import com.lsiding.nat.core.InOutModel;
    
    public class UtilSocks5 {
    	/**
    	 * byte int  4 
    	 * 
    	 * @param bytes
    	 * @return
    	 */
    	public int byte2Int(Byte[] bytes) {
    		return (bytes[0] & 0xff) << 24 | (bytes[1] & 0xff) << 16
    				| (bytes[2] & 0xff) << 8 | (bytes[3] & 0xff);
    	}
    
    	/**
    	 * 2   int
    	 * 
    	 * @throws IOException
    	 */
    	public static final int byte2ToInteger(InOutModel> inOutModel)
    			throws IOException {
    		byte[] bs = inOutModel.read(2);
    		return ((bs[0] & 0xff) << 8 | (bs[1] & 0xff));
    	}
    
    	/**
    	 *    byte 0-255, byte 127 -128, 129, 127 -128 ,
    	 *  129==--127
    	 * 
    	 * @return liyixing
    	 * @throws IOException
    	 * @throws SocketTimeoutException
    	 */
    	public static final String getAddress(int type, InOutModel> inOutModel)
    			throws SocketTimeoutException, IOException {
    		if (type == 3) {
    			//  
    			int len = inOutModel.readLength();
    			byte[] bs = inOutModel.read(len);
    
    			return new String(bs);
    		} else if (type == 1) {
    			// ip 4
    			byte[] bs = inOutModel.read(4);
    			StringBuffer sb = new StringBuffer();
    
    			for (int i = 0; i < 4; i++) {
    				if (i != 0) {
    					sb.append(".");
    				}
    				int a = bs[i];
    				if (a < 0) {
    					a = 256 + a;
    				}
    				sb.append(a);
    			}
    
    			return sb.toString();
    		} else if (type == 4) {
    			// ip6 16 
    			StringBuffer sb = new StringBuffer();
    
    			for (int i = 0; i < 8; i++) {
    				if (i != 0) {
    					sb.append(":");
    				}
    				int a = byte2ToInteger(inOutModel);
    				// a & 0xffff  4 16 ,a & 0xff 2 16 
    				String x = String.format("%02x", new Integer(a & 0xffff))
    						.toUpperCase();
    
    				while (true) {
    					if (x.length() < 4) {
    						x = "0" + x;
    					} else {
    						break;
    					}
    				}
    				sb.append(x);
    			}
    
    			return sb.toString();
    		}
    
    		return null;
    	}
    
    	/**
    	 * int byte  4 
    	 * 
    	 * @param bytes
    	 * @return
    	 */
    	public byte[] intToByte(int num) {
    		byte[] bytes = new byte[4];
    		bytes[0] = (byte) ((num >> 24) & 0xff);
    		bytes[1] = (byte) ((num >> 16) & 0xff);
    		bytes[2] = (byte) ((num >> 8) & 0xff);
    		bytes[3] = (byte) (num & 0xff);
    		return bytes;
    	}
    
    	/**
    	 * int  2 byte
    	 * 
    	 * @param i
    	 * @return liyixing
    	 */
    	public static byte[] intTo2ByteArray(int i) {
    		byte[] result = new byte[2];
    		result[0] = (byte) ((i >> 8));
    		result[1] = (byte) (i & 0xff);
    		return result;
    	}
    
    	/**
    	 *  byte
    	 * 
    	 * @param type
    	 * @param ip
    	 * @return liyixing
    	 */
    	public static final byte[] getAddressBytes(int type, String ip) {
    		if (type == 3) {
    			//  
    			byte[] bs = ip.getBytes();
    			int len = bs.length + 1;
    			byte[] rs = new byte[len];
    
    			rs[0] = (byte) bs.length;
    
    			for (int index = 0; index < bs.length; index++) {
    				rs[index + 1] = bs[index];
    			}
    
    			return rs;
    		} else if (type == 1) {
    			// ip 4
    			String[] ips = ip.split("\\.");
    			byte[] rs = new byte[4];
    
    			for (int index = 0; index < 4; index++) {
    				rs[index] = (byte) Integer.valueOf(ips[index]).intValue();
    			}
    
    			return rs;
    		} else if (type == 4) {
    			// ip 4
    			String[] ips = ip.split(":");
    
    			byte[] rs = new byte[16];
    
    			for (int index = 0; index < 8; index++) {
    				String ipOne = ips[index];
    				byte[] tm = intTo2ByteArray(Integer.valueOf(ipOne, 16));
    				rs[index * 2] = tm[0];
    				rs[index * 2 + 1] = tm[1];
    			}
    
    			return rs;
    		}
    
    		return null;
    	}
    
    	public static void main(String[] args) throws IOException {
    		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
    				intTo2ByteArray(26381));
    		InOutModel inOutModel = new InOutModel(
    				byteArrayInputStream);
    		System.out.println(byte2ToInteger(inOutModel));
    
    		byteArrayInputStream = new ByteArrayInputStream(getAddressBytes(3,
    				"www.lsiding.com"));
    		inOutModel = new InOutModel(byteArrayInputStream);
    		System.out.println(getAddress(3, inOutModel));
    
    		byteArrayInputStream = new ByteArrayInputStream(getAddressBytes(1,
    				"192.168.0.103"));
    		inOutModel = new InOutModel(byteArrayInputStream);
    		System.out.println(getAddress(1, inOutModel));
    		System.out.println("2001:0DB8:0000:0023:1008:0800:200C:0001");
    		byteArrayInputStream = new ByteArrayInputStream(getAddressBytes(4,
    				"2001:0DB8:0000:0023:1008:0800:200C:0001"));
    		inOutModel = new InOutModel(byteArrayInputStream);
    		System.out.println(getAddress(4, inOutModel));
    	}
    }
    
    
    
    public RequestInfo readDatas() throws IOException {
    		RequestInfo requestInfo = new RequestInfo();
    		
    		requestInfo.setVer(read());
    		requestInfo.setCmd(read());
    		requestInfo.setRsv(read());
    		requestInfo.setAtyp(read());
    		String ip = UtilSocks5.getAddress(requestInfo.getAtyp(), this);
    		requestInfo.setDstAddr(ip);
    		requestInfo.setPort(UtilSocks5.getProt(this));
    		
    		return requestInfo;
    	}

    8. 서버 응답
    VER
    REP
      RSV 
    ATYP
    BND.ADDR
    BND.PORT
    1바이트
    1바이트
    X'00'
    1바이트
    Variable
    2바이트
  • o  VER    protocol version: X'05'
  • o REP Reply 상태 코드:
  • o  X'00' succeeded
  • o X'01'일반 SOCKS 서버 오류
  • o X'02'는 cmd2를 사용할 수 없습니다
  • o X'03'네트워크에 액세스할 수 없습니다
  • o X'04 호스트에 액세스할 수 없습니다
  • o X'05'연결이 거부되었습니다
  • o X'06'TTL이 만료되었습니다
  • o X'07'명령은 지원되지 않습니다
  • o X'08'은 주소 유형을 지원하지 않습니다
  • o X'09'X'09'에서 X'FF'로 미분배
  • o RSV 보존 필드는 0X00 을 입력해야 합니다
  • o ATYP 1ip4,3 도메인 이름 모드, 4ip6

  • 연결 모드
    BND.ADDR BND.PORT 및 DST.ADDR 및 DST.PORT 동일
    BIND 모드, 새로 열린 서버의 포트, IP
    UDP 모드, 지원 준비 없음, 자세히 보기https://www.ietf.org/rfc/rfc1928.txt

    좋은 웹페이지 즐겨찾기