// Path: mitm/src/main/java/uk/ac/cam/gpe21/droidssl/mitm/util/CLibrary.java
// public interface CLibrary extends Library {
// 	public final CLibrary INSTANCE = (CLibrary) Native.loadLibrary("c", CLibrary.class);
// 
// 	/* from /usr/include/bits/socket.h */
// 	public final int PF_INET = 2;
// 	public final int PF_INET6 = 10;
// 
// 	/* from /usr/include/bits/in.h */
// 	public final int SOL_IP = 0;
// 	public final int SOL_IPV6 = 41;
// 	public final int IP_TRANSPARENT = 19;
// 
// 	/* from /usr/include/linux/netfilter_ipv4.h */
// 	public final int SO_ORIGINAL_DST = 80;
// 
// 	/* from /usr/include/linux/netfilter_ipv6/ip6_tables.h */
// 	public final int IP6T_SO_ORIGINAL_DST = 80;
// 
// 	/* from /usr/include/linux/in.h */
// 	public final class sockaddr_in extends Structure {
// 		public short sin_family;
// 		public byte[] sin_port = new byte[2];
// 		public byte[] sin_addr = new byte[4];
// 		public byte[] sin_zero = new byte[8];
// 
// 		@Override
// 		protected List getFieldOrder() {
// 			return Arrays.asList("sin_family", "sin_port", "sin_addr", "sin_zero");
// 		}
// 	}
// 
// 	/* from /usr/include/linux/in6.h */
// 	public final class sockaddr_in6 extends Structure {
// 		public short  sin6_family;
// 		public byte[] sin6_port     = new byte[2];
// 		public byte[] sin6_flowinfo = new byte[4];
// 		public byte[] sin6_addr     = new byte[16];
// 		public int    sin6_scope_id;
// 
// 		@Override
// 		protected List getFieldOrder() {
// 			return Arrays.asList("sin6_family", "sin6_port", "sin6_flowinfo", "sin6_addr", "sin6_scope_id");
// 		}
// 	}
// 
// 	/* from /usr/include/sys/socket.h */
// 	public int getsockopt(int socket, int level, int option_name, Pointer option_value, IntByReference option_len) throws LastErrorException;
// 	public int setsockopt(int socket, int level, int option_name, Pointer option_value, int option_len) throws LastErrorException;
// 
// 	/* from /usr/include/string.h */
// 	public String strerror(int errnum);
// }


import com.sun.jna.LastErrorException;
import com.sun.jna.ptr.IntByReference;
import uk.ac.cam.gpe21.droidssl.mitm.util.CLibrary;
import javax.net.ssl.SSLSocket;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.*;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
/*
 * Copyright 2013-2014 Graham Edgecombe
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package uk.ac.cam.gpe21.droidssl.mitm.socket;



public final class SocketUtils {
	private static final Class<?> SERVER_SOCKET_CHANNEL_IMPL;
	private static final Field SERVER_SOCKET_CHANNEL_FD;

	private static final Class<?> SOCKET_CHANNEL_IMPL;
	private static final Field SOCKET_CHANNEL_FD;

	private static final Class<?> SSL_SOCKET_IMPL;
	private static final Field SSL_SOCKET_INPUT;

	private static final Field FD;

	static {
		try {
			SERVER_SOCKET_CHANNEL_IMPL = Class.forName("sun.nio.ch.ServerSocketChannelImpl");

			SERVER_SOCKET_CHANNEL_FD = SERVER_SOCKET_CHANNEL_IMPL.getDeclaredField("fd");
			SERVER_SOCKET_CHANNEL_FD.setAccessible(true);

			SOCKET_CHANNEL_IMPL = Class.forName("sun.nio.ch.SocketChannelImpl");

			SOCKET_CHANNEL_FD = SOCKET_CHANNEL_IMPL.getDeclaredField("fd");
			SOCKET_CHANNEL_FD.setAccessible(true);

			SSL_SOCKET_IMPL = Class.forName("sun.security.ssl.SSLSocketImpl");

			SSL_SOCKET_INPUT = SSL_SOCKET_IMPL.getDeclaredField("sockInput");
			SSL_SOCKET_INPUT.setAccessible(true);

			FD = FileDescriptor.class.getDeclaredField("fd");
			FD.setAccessible(true);
		} catch (NoSuchFieldException | ClassNotFoundException ex) {
			throw new ExceptionInInitializerError(ex);
		}
	}

	/**
	 * Opens an unbound {@link ServerSocket} with the {@code IP_TRANSPARENT}
	 * option enabled.
	 * @return The {@link ServerSocket}.
	 * @throws IOException if an I/O error occurs opening the socket or if the
	 *                     {@code IP_TRANSPARENT} option could not be set.
	 */
	public static ServerSocket openTproxyServerSocket() throws IOException {
		/*
		 * The old IO ServerSocket class obtains an FD and binds to the socket
		 * in one go in a single native call, making it impossible to set the
		 * IP_TRANSPARENT option prior to binding.
		 *
		 * However, the NIO ServerSocketChannel class obtains an FD upon
		 * creation and allows the bind to be delayed, allowing us to insert
		 * the setsockopt() call between these two events.
		 *
		 * It also has a method to return an object which appears like a
		 * ServerSocket but actually translates all method calls into
		 * operations on the underlying ServerSocketChannel instead. This is
		 * useful so we don't have to convert the entire MITM server to use
		 * NIO. (Notably, SSL code is trickier in NIO as you have to implement
		 * it yourself with the SSLEngine class.)
		 */
		ServerSocketChannel ch = ServerSocketChannel.open();
		int fd = getFileDescriptor(ch);

		IntByReference yes = new IntByReference(1); /* sizeof(int) = 4 */

		try {
// Path: mitm/src/main/java/uk/ac/cam/gpe21/droidssl/mitm/util/CLibrary.java
// public interface CLibrary extends Library {
// 	public final CLibrary INSTANCE = (CLibrary) Native.loadLibrary("c", CLibrary.class);
// 
// 	/* from /usr/include/bits/socket.h */
// 	public final int PF_INET = 2;
// 	public final int PF_INET6 = 10;
// 
// 	/* from /usr/include/bits/in.h */
// 	public final int SOL_IP = 0;
// 	public final int SOL_IPV6 = 41;
// 	public final int IP_TRANSPARENT = 19;
// 
// 	/* from /usr/include/linux/netfilter_ipv4.h */
// 	public final int SO_ORIGINAL_DST = 80;
// 
// 	/* from /usr/include/linux/netfilter_ipv6/ip6_tables.h */
// 	public final int IP6T_SO_ORIGINAL_DST = 80;
// 
// 	/* from /usr/include/linux/in.h */
// 	public final class sockaddr_in extends Structure {
// 		public short sin_family;
// 		public byte[] sin_port = new byte[2];
// 		public byte[] sin_addr = new byte[4];
// 		public byte[] sin_zero = new byte[8];
// 
// 		@Override
// 		protected List getFieldOrder() {
// 			return Arrays.asList("sin_family", "sin_port", "sin_addr", "sin_zero");
// 		}
// 	}
// 
// 	/* from /usr/include/linux/in6.h */
// 	public final class sockaddr_in6 extends Structure {
// 		public short  sin6_family;
// 		public byte[] sin6_port     = new byte[2];
// 		public byte[] sin6_flowinfo = new byte[4];
// 		public byte[] sin6_addr     = new byte[16];
// 		public int    sin6_scope_id;
// 
// 		@Override
// 		protected List getFieldOrder() {
// 			return Arrays.asList("sin6_family", "sin6_port", "sin6_flowinfo", "sin6_addr", "sin6_scope_id");
// 		}
// 	}
// 
// 	/* from /usr/include/sys/socket.h */
// 	public int getsockopt(int socket, int level, int option_name, Pointer option_value, IntByReference option_len) throws LastErrorException;
// 	public int setsockopt(int socket, int level, int option_name, Pointer option_value, int option_len) throws LastErrorException;
// 
// 	/* from /usr/include/string.h */
// 	public String strerror(int errnum);
// }


// Path: mitm/src/main/java/uk/ac/cam/gpe21/droidssl/mitm/socket/SocketUtils.java
import com.sun.jna.LastErrorException;
import com.sun.jna.ptr.IntByReference;
import uk.ac.cam.gpe21.droidssl.mitm.util.CLibrary;
import javax.net.ssl.SSLSocket;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.*;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/*
 * Copyright 2013-2014 Graham Edgecombe
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package uk.ac.cam.gpe21.droidssl.mitm.socket;



public final class SocketUtils {
	private static final Class<?> SERVER_SOCKET_CHANNEL_IMPL;
	private static final Field SERVER_SOCKET_CHANNEL_FD;

	private static final Class<?> SOCKET_CHANNEL_IMPL;
	private static final Field SOCKET_CHANNEL_FD;

	private static final Class<?> SSL_SOCKET_IMPL;
	private static final Field SSL_SOCKET_INPUT;

	private static final Field FD;

	static {
		try {
			SERVER_SOCKET_CHANNEL_IMPL = Class.forName("sun.nio.ch.ServerSocketChannelImpl");

			SERVER_SOCKET_CHANNEL_FD = SERVER_SOCKET_CHANNEL_IMPL.getDeclaredField("fd");
			SERVER_SOCKET_CHANNEL_FD.setAccessible(true);

			SOCKET_CHANNEL_IMPL = Class.forName("sun.nio.ch.SocketChannelImpl");

			SOCKET_CHANNEL_FD = SOCKET_CHANNEL_IMPL.getDeclaredField("fd");
			SOCKET_CHANNEL_FD.setAccessible(true);

			SSL_SOCKET_IMPL = Class.forName("sun.security.ssl.SSLSocketImpl");

			SSL_SOCKET_INPUT = SSL_SOCKET_IMPL.getDeclaredField("sockInput");
			SSL_SOCKET_INPUT.setAccessible(true);

			FD = FileDescriptor.class.getDeclaredField("fd");
			FD.setAccessible(true);
		} catch (NoSuchFieldException | ClassNotFoundException ex) {
			throw new ExceptionInInitializerError(ex);
		}
	}

	/**
	 * Opens an unbound {@link ServerSocket} with the {@code IP_TRANSPARENT}
	 * option enabled.
	 * @return The {@link ServerSocket}.
	 * @throws IOException if an I/O error occurs opening the socket or if the
	 *                     {@code IP_TRANSPARENT} option could not be set.
	 */
	public static ServerSocket openTproxyServerSocket() throws IOException {
		/*
		 * The old IO ServerSocket class obtains an FD and binds to the socket
		 * in one go in a single native call, making it impossible to set the
		 * IP_TRANSPARENT option prior to binding.
		 *
		 * However, the NIO ServerSocketChannel class obtains an FD upon
		 * creation and allows the bind to be delayed, allowing us to insert
		 * the setsockopt() call between these two events.
		 *
		 * It also has a method to return an object which appears like a
		 * ServerSocket but actually translates all method calls into
		 * operations on the underlying ServerSocketChannel instead. This is
		 * useful so we don't have to convert the entire MITM server to use
		 * NIO. (Notably, SSL code is trickier in NIO as you have to implement
		 * it yourself with the SSLEngine class.)
		 */
		ServerSocketChannel ch = ServerSocketChannel.open();
		int fd = getFileDescriptor(ch);

		IntByReference yes = new IntByReference(1); /* sizeof(int) = 4 */

		try {
			CLibrary.INSTANCE.setsockopt(fd, CLibrary.SOL_IP, CLibrary.IP_TRANSPARENT, yes.getPointer(), 4);