Browse Source

bytebudy的agent

research/11.0
richie 7 years ago
parent
commit
d4faba1068
  1. 92
      fine-byte-buddy/src/com/fr/third/net/bytebuddy/agent/Attacher.java
  2. 1199
      fine-byte-buddy/src/com/fr/third/net/bytebuddy/agent/ByteBuddyAgent.java
  3. 71
      fine-byte-buddy/src/com/fr/third/net/bytebuddy/agent/Installer.java
  4. 336
      fine-byte-buddy/src/com/fr/third/net/bytebuddy/agent/VirtualMachine.java
  5. 4
      fine-byte-buddy/src/com/fr/third/net/bytebuddy/agent/package-info.java
  6. 141
      fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/AFUNIXServerSocket.java
  7. 131
      fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/AFUNIXSocket.java
  8. 76
      fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/AFUNIXSocketAddress.java
  9. 53
      fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/AFUNIXSocketException.java
  10. 411
      fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/AFUNIXSocketImpl.java
  11. 131
      fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/NativeUnixSocket.java
  12. 5
      fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/package-info.java

92
fine-byte-buddy/src/com/fr/third/net/bytebuddy/agent/Attacher.java

@ -0,0 +1,92 @@
package com.fr.third.net.bytebuddy.agent;
import java.lang.reflect.InvocationTargetException;
/**
* A Java program that attaches a Java agent to an external process.
*/
public class Attacher {
/**
* Base for access to a reflective member to make the code more readable.
*/
private static final Object STATIC_MEMBER = null;
/**
* The name of the {@code attach} method of the {@code VirtualMachine} class.
*/
private static final String ATTACH_METHOD_NAME = "attach";
/**
* The name of the {@code loadAgent} method of the {@code VirtualMachine} class.
*/
private static final String LOAD_AGENT_METHOD_NAME = "loadAgent";
/**
* The name of the {@code detach} method of the {@code VirtualMachine} class.
*/
private static final String DETACH_METHOD_NAME = "detach";
/**
* Runs the attacher as a Java application.
*
* @param args A list containing the fully qualified name of the virtual machine type,
* the process id, the fully qualified name of the Java agent jar followed by
* an empty string if the argument to the agent is {@code null} or any number
* of strings where the first argument is proceeded by any single character
* which is stripped off.
*/
public static void main(String[] args) {
try {
String argument;
if (args.length < 4 || args[3].isEmpty()) {
argument = null;
} else {
StringBuilder stringBuilder = new StringBuilder(args[3].substring(1));
for (int index = 4; index < args.length; index++) {
stringBuilder.append(' ').append(args[index]);
}
argument = stringBuilder.toString();
}
install(Class.forName(args[0]), args[1], args[2], argument);
} catch (Exception ignored) {
System.exit(1);
}
}
/**
* Installs a Java agent on a target VM.
*
* @param virtualMachineType The virtual machine type to use for the external attachment.
* @param processId The id of the process being target of the external attachment.
* @param agent The Java agent to attach.
* @param argument The argument to provide or {@code null} if no argument is provided.
* @throws NoSuchMethodException If the virtual machine type does not define an expected method.
* @throws InvocationTargetException If the virtual machine type raises an error.
* @throws IllegalAccessException If a method of the virtual machine type cannot be accessed.
*/
protected static void install(Class<?> virtualMachineType,
String processId,
String agent,
String argument) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Object virtualMachineInstance = virtualMachineType
.getMethod(ATTACH_METHOD_NAME, String.class)
.invoke(STATIC_MEMBER, processId);
try {
virtualMachineType
.getMethod(LOAD_AGENT_METHOD_NAME, String.class, String.class)
.invoke(virtualMachineInstance, agent, argument);
} finally {
virtualMachineType
.getMethod(DETACH_METHOD_NAME)
.invoke(virtualMachineInstance);
}
}
/**
* The attacher provides only {@code static} utility methods and should not be instantiated.
*/
private Attacher() {
throw new UnsupportedOperationException();
}
}

1199
fine-byte-buddy/src/com/fr/third/net/bytebuddy/agent/ByteBuddyAgent.java

File diff suppressed because it is too large Load Diff

71
fine-byte-buddy/src/com/fr/third/net/bytebuddy/agent/Installer.java

@ -0,0 +1,71 @@
package com.fr.third.net.bytebuddy.agent;
import java.lang.instrument.Instrumentation;
/**
* An installer class which defined the hook-in methods that are required by the Java agent specification.
*/
public class Installer {
/**
* A field for carrying the {@link java.lang.instrument.Instrumentation} that was loaded by the Byte Buddy
* agent. Note that this field must never be accessed directly as the agent is injected into the VM's
* system class loader. This way, the field of this class might be {@code null} even after the installation
* of the Byte Buddy agent as this class might be loaded by a different class loader than the system class
* loader.
*/
@SuppressWarnings("unused")
private static volatile Instrumentation instrumentation;
/**
* The installer provides only {@code static} hook-in methods and should not be instantiated.
*/
private Installer() {
throw new UnsupportedOperationException();
}
/**
* <p>
* Returns the instrumentation that was loaded by the Byte Buddy agent. When a security manager is active,
* the {@link RuntimePermission} for {@code getInstrumentation} is required by the caller.
* </p>
* <p>
* <b>Important</b>: This method must only be invoked via the {@link ClassLoader#getSystemClassLoader()} where any
* Java agent is loaded. It is possible that two versions of this class exist for different class loaders.
* </p>
*
* @return The instrumentation instance of the Byte Buddy agent.
*/
public static Instrumentation getInstrumentation() {
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
securityManager.checkPermission(new RuntimePermission("getInstrumentation"));
}
Instrumentation instrumentation = Installer.instrumentation;
if (instrumentation == null) {
throw new IllegalStateException("The Byte Buddy agent is not loaded or this method is not called via the system class loader");
}
return instrumentation;
}
/**
* Allows the installation of this agent via a command line argument.
*
* @param agentArguments The unused agent arguments.
* @param instrumentation The instrumentation instance.
*/
public static void premain(String agentArguments, Instrumentation instrumentation) {
Installer.instrumentation = instrumentation;
}
/**
* Allows the installation of this agent via the Attach API.
*
* @param agentArguments The unused agent arguments.
* @param instrumentation The instrumentation instance.
*/
@SuppressWarnings("unused")
public static void agentmain(String agentArguments, Instrumentation instrumentation) {
Installer.instrumentation = instrumentation;
}
}

336
fine-byte-buddy/src/com/fr/third/net/bytebuddy/agent/VirtualMachine.java

@ -0,0 +1,336 @@
package com.fr.third.net.bytebuddy.agent;
import com.fr.third.org.newsclub.net.unix.AFUNIXSocket;
import com.fr.third.org.newsclub.net.unix.AFUNIXSocketAddress;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
/**
* <p>
* An implementation for attachment on a virtual machine. This interface mimics the tooling API's virtual
* machine interface to allow for similar usage by {@link ByteBuddyAgent} where all calls are made via
* reflection such that this structural typing suffices for interoperability.
* </p>
* <p>
* <b>Note</b>: Implementations are required to declare a static method {@code attach(String)} returning an
* instance of a class that declares the methods defined by {@link VirtualMachine}.
* </p>
*/
public interface VirtualMachine {
/**
* Loads an agent into the represented virtual machine.
*
* @param jarFile The jar file to attach.
* @param argument The argument to provide or {@code null} if no argument should be provided.
* @throws IOException If an I/O exception occurs.
*/
@SuppressWarnings("unused")
void loadAgent(String jarFile, String argument) throws IOException;
/**
* Detaches this virtual machine representation.
*
* @throws IOException If an I/O exception occurs.
*/
@SuppressWarnings("unused")
void detach() throws IOException;
/**
* A virtual machine implementation for a HotSpot VM or any compatible VM.
*/
abstract class ForHotSpot implements VirtualMachine {
/**
* The UTF-8 charset.
*/
private static final Charset UTF_8 = Charset.forName("UTF-8");
/**
* The protocol version to use for communication.
*/
private static final String PROTOCOL_VERSION = "1";
/**
* The {@code load} command.
*/
private static final String LOAD_COMMAND = "load";
/**
* The {@code instrument} command.
*/
private static final String INSTRUMENT_COMMAND = "instrument";
/**
* A delimiter to be used for attachment.
*/
private static final String ARGUMENT_DELIMITER = "=";
/**
* A blank line argument.
*/
private static final byte[] BLANK = new byte[]{0};
/**
* The target process's id.
*/
protected final String processId;
/**
* Creates a new HotSpot-compatible VM implementation.
*
* @param processId The target process's id.
*/
protected ForHotSpot(String processId) {
this.processId = processId;
}
@Override
public void loadAgent(String jarFile, String argument) throws IOException {
connect();
write(PROTOCOL_VERSION.getBytes(UTF_8));
write(BLANK);
write(LOAD_COMMAND.getBytes(UTF_8));
write(BLANK);
write(INSTRUMENT_COMMAND.getBytes(UTF_8));
write(BLANK);
write(Boolean.FALSE.toString().getBytes(UTF_8));
write(BLANK);
write((argument == null
? jarFile
: jarFile + ARGUMENT_DELIMITER + argument).getBytes(UTF_8));
write(BLANK);
byte[] buffer = new byte[1];
StringBuilder stringBuilder = new StringBuilder();
int length;
while ((length = read(buffer)) != -1) {
if (length > 0) {
if (buffer[0] == 10) {
break;
}
stringBuilder.append((char) buffer[0]);
}
}
switch (Integer.parseInt(stringBuilder.toString())) {
case 0:
return;
case 101:
throw new IOException("Protocol mismatch with target VM");
default:
buffer = new byte[1024];
stringBuilder = new StringBuilder();
while ((length = read(buffer)) != -1) {
stringBuilder.append(new String(buffer, 0, length, UTF_8));
}
throw new IllegalStateException(stringBuilder.toString());
}
}
/**
* Connects to the target VM.
*
* @throws IOException If an I/O exception occurs.
*/
protected abstract void connect() throws IOException;
/**
* Reads from the communication channel.
*
* @param buffer The buffer to read into.
* @return The amount of bytes read.
* @throws IOException If an I/O exception occurs.
*/
protected abstract int read(byte[] buffer) throws IOException;
/**
* Writes to the communication channel.
*
* @param buffer The buffer to write from.
* @throws IOException If an I/O exception occurs.
*/
protected abstract void write(byte[] buffer) throws IOException;
/**
* A virtual machine implementation for a HotSpot VM running on Unix.
*/
public static class OnUnix extends ForHotSpot {
/**
* The default amount of attempts to connect.
*/
private static final int DEFAULT_ATTEMPTS = 10;
/**
* The default pause between two attempts.
*/
private static final long DEFAULT_PAUSE = 200;
/**
* The default socket timeout.
*/
private static final long DEFAULT_TIMEOUT = 5000;
/**
* The temporary directory on Unix systems.
*/
private static final String TEMPORARY_DIRECTORY = "/tmp";
/**
* The name prefix for a socket.
*/
private static final String SOCKET_FILE_PREFIX = ".java_pid";
/**
* The name prefix for an attachment file indicator.
*/
private static final String ATTACH_FILE_PREFIX = ".attach_pid";
/**
* The Unix socket to use for communication. The containing object is supposed to be an instance
* of {@link AFUNIXSocket} which is however not set to avoid eager loading
*/
private final Object socket;
/**
* The number of attempts to connect.
*/
private final int attempts;
/**
* The time to pause between attempts.
*/
private final long pause;
/**
* The socket timeout.
*/
private final long timeout;
/**
* The time unit of the pause time.
*/
private final TimeUnit timeUnit;
/**
* Creates a new VM implementation for a HotSpot VM running on Unix.
*
* @param processId The process id of the target VM.
* @param socket The Unix socket to use for communication.
* @param attempts The number of attempts to connect.
* @param pause The pause time between two VMs.
* @param timeout The socket timeout.
* @param timeUnit The time unit of the pause time.
*/
public OnUnix(String processId, Object socket, int attempts, long pause, long timeout, TimeUnit timeUnit) {
super(processId);
this.socket = socket;
this.attempts = attempts;
this.pause = pause;
this.timeout = timeout;
this.timeUnit = timeUnit;
}
/**
* Asserts the availability of this virtual machine implementation. If the Unix socket library is missing or
* if this VM does not support Unix socket communication, a {@link Throwable} is thrown.
*
* @return This virtual machine type.
* @throws Throwable If this VM does not support POSIX sockets or is not running on a HotSpot VM.
*/
public static Class<?> assertAvailability() throws Throwable {
if (!AFUNIXSocket.isSupported()) {
throw new IllegalStateException("POSIX sockets are not supported on the current system");
} else if (!System.getProperty("java.vm.name").toLowerCase(Locale.US).contains("hotspot")) {
throw new IllegalStateException("Cannot apply attachment on non-Hotspot compatible VM");
} else {
return OnUnix.class;
}
}
/**
* Attaches to the supplied VM process.
*
* @param processId The process id of the target VM.
* @return An appropriate virtual machine implementation.
* @throws IOException If an I/O exception occurs.
*/
public static VirtualMachine attach(String processId) throws IOException {
return new OnUnix(processId, AFUNIXSocket.newInstance(), DEFAULT_ATTEMPTS, DEFAULT_PAUSE, DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
}
@Override
protected void connect() throws IOException {
File socketFile = new File(TEMPORARY_DIRECTORY, SOCKET_FILE_PREFIX + processId);
if (!socketFile.exists()) {
String target = ATTACH_FILE_PREFIX + processId, path = "/proc/" + processId + "/cwd/" + target;
File attachFile = new File(path);
try {
if (!attachFile.createNewFile() && !attachFile.isFile()) {
throw new IllegalStateException("Could not create attach file: " + attachFile);
}
} catch (IOException ignored) {
attachFile = new File(TEMPORARY_DIRECTORY, target);
if (!attachFile.createNewFile() && !attachFile.isFile()) {
throw new IllegalStateException("Could not create attach file: " + attachFile);
}
}
try {
// The HotSpot attachment API attempts to send the signal to all children of a process
Process process = Runtime.getRuntime().exec("kill -3 " + processId);
int attempts = this.attempts;
boolean killed = false;
do {
try {
if (process.exitValue() != 0) {
throw new IllegalStateException("Error while sending signal to target VM: " + processId);
}
killed = true;
break;
} catch (IllegalThreadStateException ignored) {
attempts -= 1;
Thread.sleep(timeUnit.toMillis(pause));
}
} while (attempts > 0);
if (!killed) {
throw new IllegalStateException("Target VM did not respond to signal: " + processId);
}
attempts = this.attempts;
while (attempts-- > 0 && !socketFile.exists()) {
Thread.sleep(timeUnit.toMillis(pause));
}
if (!socketFile.exists()) {
throw new IllegalStateException("Target VM did not respond: " + processId);
}
} catch (InterruptedException exception) {
throw new IllegalStateException("Interrupted during wait for process", exception);
} finally {
if (!attachFile.delete()) {
attachFile.deleteOnExit();
}
}
}
((AFUNIXSocket) socket).setSoTimeout((int) timeUnit.toMillis(timeout));
((AFUNIXSocket) socket).connect(new AFUNIXSocketAddress(socketFile));
}
@Override
public int read(byte[] buffer) throws IOException {
return ((AFUNIXSocket) this.socket).getInputStream().read(buffer);
}
@Override
public void write(byte[] buffer) throws IOException {
((AFUNIXSocket) this.socket).getOutputStream().write(buffer);
}
@Override
public void detach() throws IOException {
((AFUNIXSocket) this.socket).close();
}
}
}
}

4
fine-byte-buddy/src/com/fr/third/net/bytebuddy/agent/package-info.java

@ -0,0 +1,4 @@
/**
* The Byte Buddy agent allows the redefinition of classes at runtime.
*/
package com.fr.third.net.bytebuddy.agent;

141
fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/AFUNIXServerSocket.java

@ -0,0 +1,141 @@
/**
* junixsocket
*
* Copyright (c) 2009,2014 Christian Kohlschütter
*
* The author licenses this file to You 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 com.fr.third.org.newsclub.net.unix;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
/**
* The server part of an AF_UNIX domain socket.
*
* @author Christian Kohlschütter
*/
public class AFUNIXServerSocket extends ServerSocket {
private final AFUNIXSocketImpl implementation;
private AFUNIXSocketAddress boundEndpoint = null;
private final Thread shutdownThread = new Thread() {
@Override
public void run() {
try {
if (boundEndpoint != null) {
NativeUnixSocket.unlink(boundEndpoint.getSocketFile());
}
} catch (IOException e) {
// ignore
}
}
};
protected AFUNIXServerSocket() throws IOException {
super();
this.implementation = new AFUNIXSocketImpl();
NativeUnixSocket.initServerImpl(this, implementation);
Runtime.getRuntime().addShutdownHook(shutdownThread);
NativeUnixSocket.setCreatedServer(this);
}
/**
* Returns a new, unbound AF_UNIX {@link ServerSocket}.
*
* @return The new, unbound {@link AFUNIXServerSocket}.
*/
public static AFUNIXServerSocket newInstance() throws IOException {
AFUNIXServerSocket instance = new AFUNIXServerSocket();
return instance;
}
/**
* Returns a new AF_UNIX {@link ServerSocket} that is bound to the given
* {@link AFUNIXSocketAddress}.
*
* @return The new, unbound {@link AFUNIXServerSocket}.
*/
public static AFUNIXServerSocket bindOn(final AFUNIXSocketAddress addr) throws IOException {
AFUNIXServerSocket socket = newInstance();
socket.bind(addr);
return socket;
}
@Override
public void bind(SocketAddress endpoint, int backlog) throws IOException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
if (isBound()) {
throw new SocketException("Already bound");
}
if (!(endpoint instanceof AFUNIXSocketAddress)) {
throw new IOException("Can only bind to endpoints of type "
+ AFUNIXSocketAddress.class.getName());
}
implementation.bind(backlog, endpoint);
boundEndpoint = (AFUNIXSocketAddress) endpoint;
}
@Override
public boolean isBound() {
return boundEndpoint != null;
}
@Override
public Socket accept() throws IOException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
AFUNIXSocket as = AFUNIXSocket.newInstance();
implementation.accept(as.impl);
as.addr = boundEndpoint;
NativeUnixSocket.setConnected(as);
return as;
}
@Override
public String toString() {
if (!isBound()) {
return "AFUNIXServerSocket[unbound]";
}
return "AFUNIXServerSocket[" + boundEndpoint.getSocketFile() + "]";
}
@Override
public void close() throws IOException {
if (isClosed()) {
return;
}
super.close();
implementation.close();
if (boundEndpoint != null) {
NativeUnixSocket.unlink(boundEndpoint.getSocketFile());
}
try {
Runtime.getRuntime().removeShutdownHook(shutdownThread);
} catch (IllegalStateException e) {
// ignore
}
}
public static boolean isSupported() {
return NativeUnixSocket.isLoaded();
}
}

131
fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/AFUNIXSocket.java

@ -0,0 +1,131 @@
/**
* junixsocket
*
* Copyright (c) 2009,2014 Christian Kohlschütter
*
* The author licenses this file to You 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 com.fr.third.org.newsclub.net.unix;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketAddress;
/**
* Implementation of an AF_UNIX domain socket.
*
* @author Christian Kohlschütter
*/
public class AFUNIXSocket extends Socket {
protected AFUNIXSocketImpl impl;
AFUNIXSocketAddress addr;
private AFUNIXSocket(final AFUNIXSocketImpl impl) throws IOException {
super(impl);
try {
NativeUnixSocket.setCreated(this);
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
}
}
/**
* Creates a new, unbound {@link AFUNIXSocket}.
*
* This "default" implementation is a bit "lenient" with respect to the specification.
*
* In particular, we ignore calls to {@link Socket#getTcpNoDelay()} and
* {@link Socket#setTcpNoDelay(boolean)}.
*
* @return A new, unbound socket.
*/
public static AFUNIXSocket newInstance() throws IOException {
final AFUNIXSocketImpl impl = new AFUNIXSocketImpl.Lenient();
AFUNIXSocket instance = new AFUNIXSocket(impl);
instance.impl = impl;
return instance;
}
/**
* Creates a new, unbound, "strict" {@link AFUNIXSocket}.
*
* This call uses an implementation that tries to be closer to the specification than
* {@link #newInstance()}, at least for some cases.
*
* @return A new, unbound socket.
*/
public static AFUNIXSocket newStrictInstance() throws IOException {
final AFUNIXSocketImpl impl = new AFUNIXSocketImpl();
AFUNIXSocket instance = new AFUNIXSocket(impl);
instance.impl = impl;
return instance;
}
/**
* Creates a new {@link AFUNIXSocket} and connects it to the given {@link AFUNIXSocketAddress}.
*
* @param addr The address to connect to.
* @return A new, connected socket.
*/
public static AFUNIXSocket connectTo(AFUNIXSocketAddress addr) throws IOException {
AFUNIXSocket socket = newInstance();
socket.connect(addr);
return socket;
}
/**
* Binds this {@link AFUNIXSocket} to the given bindpoint. Only bindpoints of the type
* {@link AFUNIXSocketAddress} are supported.
*/
@Override
public void bind(SocketAddress bindpoint) throws IOException {
super.bind(bindpoint);
this.addr = (AFUNIXSocketAddress) bindpoint;
}
@Override
public void connect(SocketAddress endpoint) throws IOException {
connect(endpoint, 0);
}
@Override
public void connect(SocketAddress endpoint, int timeout) throws IOException {
if (!(endpoint instanceof AFUNIXSocketAddress)) {
throw new IOException("Can only connect to endpoints of type "
+ AFUNIXSocketAddress.class.getName());
}
impl.connect(endpoint, timeout);
this.addr = (AFUNIXSocketAddress) endpoint;
NativeUnixSocket.setConnected(this);
}
@Override
public String toString() {
if (isConnected()) {
return "AFUNIXSocket[fd=" + impl.getFD() + ";path=" + addr.getSocketFile() + "]";
}
return "AFUNIXSocket[unconnected]";
}
/**
* Returns <code>true</code> iff {@link AFUNIXSocket}s are supported by the current Java VM.
*
* To support {@link AFUNIXSocket}s, a custom JNI library must be loaded that is supplied with
* <em>junixsocket</em>.
*
* @return {@code true} iff supported.
*/
public static boolean isSupported() {
return NativeUnixSocket.isLoaded();
}
}

76
fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/AFUNIXSocketAddress.java

@ -0,0 +1,76 @@
/**
* junixsocket
*
* Copyright (c) 2009,2014 Christian Kohlschütter
*
* The author licenses this file to You 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 com.fr.third.org.newsclub.net.unix;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
/**
* Describes an {@link InetSocketAddress} that actually uses AF_UNIX sockets instead of AF_INET.
*
* The ability to specify a port number is not specified by AF_UNIX sockets, but we need it
* sometimes, for example for RMI-over-AF_UNIX.
*
* @author Christian Kohlschütter
*/
public class AFUNIXSocketAddress extends InetSocketAddress {
private static final long serialVersionUID = 1L;
private final String socketFile;
/**
* Creates a new {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the
* given file.
*
* @param socketFile The socket to connect to.
*/
public AFUNIXSocketAddress(final File socketFile) throws IOException {
this(socketFile, 0);
}
/**
* Creates a new {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the
* given file, assigning the given port to it.
*
* @param socketFile The socket to connect to.
* @param port The port associated with this socket, or {@code 0} when no port should be assigned.
*/
public AFUNIXSocketAddress(final File socketFile, int port) throws IOException {
super(0);
if (port != 0) {
NativeUnixSocket.setPort1(this, port);
}
this.socketFile = socketFile.getCanonicalPath();
}
/**
* Returns the (canonical) file path for this {@link AFUNIXSocketAddress}.
*
* @return The file path.
*/
public String getSocketFile() {
return socketFile;
}
@Override
public String toString() {
return getClass().getName() + "[host=" + getHostName() + ";port=" + getPort() + ";file="
+ socketFile + "]";
}
}

53
fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/AFUNIXSocketException.java

@ -0,0 +1,53 @@
/**
* junixsocket
*
* Copyright (c) 2009,2014 Christian Kohlschütter
*
* The author licenses this file to You 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 com.fr.third.org.newsclub.net.unix;
import java.net.SocketException;
/**
* Something went wrong with the communication to a Unix socket.
*
* @author Christian Kohlschütter
*/
public class AFUNIXSocketException extends SocketException {
private static final long serialVersionUID = 1L;
private final String socketFile;
public AFUNIXSocketException(String reason) {
this(reason, (String) null);
}
public AFUNIXSocketException(String reason, final Throwable cause) {
this(reason, (String) null);
initCause(cause);
}
public AFUNIXSocketException(String reason, final String socketFile) {
super(reason);
this.socketFile = socketFile;
}
@Override
public String toString() {
if (socketFile == null) {
return super.toString();
} else {
return super.toString() + " (socket: " + socketFile + ")";
}
}
}

411
fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/AFUNIXSocketImpl.java

@ -0,0 +1,411 @@
/**
* junixsocket
*
* Copyright (c) 2009,2014 Christian Kohlschütter
*
* The author licenses this file to You 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 com.fr.third.org.newsclub.net.unix;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketImpl;
import java.net.SocketOptions;
/**
* The Java-part of the {@link AFUNIXSocket} implementation.
*
* @author Christian Kohlschütter
*/
class AFUNIXSocketImpl extends SocketImpl {
private static final int SHUT_RD = 0;
private static final int SHUT_WR = 1;
private static final int SHUT_RD_WR = 2;
private String socketFile;
private boolean closed = false;
private boolean bound = false;
private boolean connected = false;
private boolean closedInputStream = false;
private boolean closedOutputStream = false;
private final AFUNIXInputStream in = new AFUNIXInputStream();
private final AFUNIXOutputStream out = new AFUNIXOutputStream();
AFUNIXSocketImpl() {
super();
this.fd = new FileDescriptor();
}
FileDescriptor getFD() {
return fd;
}
@Override
protected void accept(SocketImpl socket) throws IOException {
final AFUNIXSocketImpl si = (AFUNIXSocketImpl) socket;
NativeUnixSocket.accept(socketFile, fd, si.fd);
si.socketFile = socketFile;
si.connected = true;
}
@Override
protected int available() throws IOException {
return NativeUnixSocket.available(fd);
}
protected void bind(SocketAddress addr) throws IOException {
bind(0, addr);
}
protected void bind(int backlog, SocketAddress addr) throws IOException {
if (!(addr instanceof AFUNIXSocketAddress)) {
throw new SocketException("Cannot bind to this type of address: " + addr.getClass());
}
final AFUNIXSocketAddress socketAddress = (AFUNIXSocketAddress) addr;
socketFile = socketAddress.getSocketFile();
NativeUnixSocket.bind(socketFile, fd, backlog);
bound = true;
this.localport = socketAddress.getPort();
}
@Override
@SuppressWarnings("hiding")
protected void bind(InetAddress host, int port) throws IOException {
throw new SocketException("Cannot bind to this type of address: " + InetAddress.class);
}
private void checkClose() throws IOException {
if (closedInputStream && closedOutputStream) {
// close();
}
}
@Override
protected synchronized void close() throws IOException {
if (closed) {
return;
}
closed = true;
if (fd.valid()) {
NativeUnixSocket.shutdown(fd, SHUT_RD_WR);
NativeUnixSocket.close(fd);
}
if (bound) {
NativeUnixSocket.unlink(socketFile);
}
connected = false;
}
@Override
@SuppressWarnings("hiding")
protected void connect(String host, int port) throws IOException {
throw new SocketException("Cannot bind to this type of address: " + InetAddress.class);
}
@Override
@SuppressWarnings("hiding")
protected void connect(InetAddress address, int port) throws IOException {
throw new SocketException("Cannot bind to this type of address: " + InetAddress.class);
}
@Override
protected void connect(SocketAddress addr, int timeout) throws IOException {
if (!(addr instanceof AFUNIXSocketAddress)) {
throw new SocketException("Cannot bind to this type of address: " + addr.getClass());
}
final AFUNIXSocketAddress socketAddress = (AFUNIXSocketAddress) addr;
socketFile = socketAddress.getSocketFile();
NativeUnixSocket.connect(socketFile, fd);
this.address = socketAddress.getAddress();
this.port = socketAddress.getPort();
this.localport = 0;
this.connected = true;
}
@Override
protected void create(boolean stream) throws IOException {
}
@Override
protected InputStream getInputStream() throws IOException {
if (!connected && !bound) {
throw new IOException("Not connected/not bound");
}
return in;
}
@Override
protected OutputStream getOutputStream() throws IOException {
if (!connected && !bound) {
throw new IOException("Not connected/not bound");
}
return out;
}
@Override
protected void listen(int backlog) throws IOException {
NativeUnixSocket.listen(fd, backlog);
}
@Override
protected void sendUrgentData(int data) throws IOException {
NativeUnixSocket.write(fd, new byte[] {(byte) (data & 0xFF)}, 0, 1);
}
private final class AFUNIXInputStream extends InputStream {
private boolean streamClosed = false;
@Override
public int read(byte[] buf, int off, int len) throws IOException {
if (streamClosed) {
throw new IOException("This InputStream has already been closed.");
}
if (len == 0) {
return 0;
}
int maxRead = buf.length - off;
if (len > maxRead) {
len = maxRead;
}
try {
return NativeUnixSocket.read(fd, buf, off, len);
} catch (final IOException e) {
throw (IOException) new IOException(e.getMessage() + " at "
+ AFUNIXSocketImpl.this.toString()).initCause(e);
}
}
@Override
public int read() throws IOException {
final byte[] buf1 = new byte[1];
final int numRead = read(buf1, 0, 1);
if (numRead <= 0) {
return -1;
} else {
return buf1[0] & 0xFF;
}
}
@Override
public void close() throws IOException {
if (streamClosed) {
return;
}
streamClosed = true;
if (fd.valid()) {
NativeUnixSocket.shutdown(fd, SHUT_RD);
}
closedInputStream = true;
checkClose();
}
@Override
public int available() throws IOException {
final int av = NativeUnixSocket.available(fd);
return av;
}
}
private final class AFUNIXOutputStream extends OutputStream {
private boolean streamClosed = false;
@Override
public void write(int oneByte) throws IOException {
final byte[] buf1 = new byte[] {(byte) oneByte};
write(buf1, 0, 1);
}
@Override
public void write(byte[] buf, int off, int len) throws IOException {
if (streamClosed) {
throw new AFUNIXSocketException("This OutputStream has already been closed.");
}
if (len > buf.length - off) {
throw new IndexOutOfBoundsException();
}
try {
while (len > 0 && !Thread.interrupted()) {
final int written = NativeUnixSocket.write(fd, buf, off, len);
if (written == -1) {
throw new IOException("Unspecific error while writing");
}
len -= written;
off += written;
}
} catch (final IOException e) {
throw (IOException) new IOException(e.getMessage() + " at "
+ AFUNIXSocketImpl.this.toString()).initCause(e);
}
}
@Override
public void close() throws IOException {
if (streamClosed) {
return;
}
streamClosed = true;
if (fd.valid()) {
NativeUnixSocket.shutdown(fd, SHUT_WR);
}
closedOutputStream = true;
checkClose();
}
}
@Override
public String toString() {
return super.toString() + "[fd=" + fd + "; file=" + this.socketFile + "; connected="
+ connected + "; bound=" + bound + "]";
}
private static int expectInteger(Object value) throws SocketException {
try {
return (Integer) value;
} catch (final ClassCastException e) {
throw new AFUNIXSocketException("Unsupported value: " + value, e);
} catch (final NullPointerException e) {
throw new AFUNIXSocketException("Value must not be null", e);
}
}
private static int expectBoolean(Object value) throws SocketException {
try {
return ((Boolean) value).booleanValue() ? 1 : 0;
} catch (final ClassCastException e) {
throw new AFUNIXSocketException("Unsupported value: " + value, e);
} catch (final NullPointerException e) {
throw new AFUNIXSocketException("Value must not be null", e);
}
}
@Override
public Object getOption(int optID) throws SocketException {
try {
switch (optID) {
case SocketOptions.SO_KEEPALIVE:
case SocketOptions.TCP_NODELAY:
return NativeUnixSocket.getSocketOptionInt(fd, optID) != 0 ? true : false;
case SocketOptions.SO_LINGER:
case SocketOptions.SO_TIMEOUT:
case SocketOptions.SO_RCVBUF:
case SocketOptions.SO_SNDBUF:
return NativeUnixSocket.getSocketOptionInt(fd, optID);
default:
throw new AFUNIXSocketException("Unsupported option: " + optID);
}
} catch (final AFUNIXSocketException e) {
throw e;
} catch (final Exception e) {
throw new AFUNIXSocketException("Error while getting option", e);
}
}
@Override
public void setOption(int optID, Object value) throws SocketException {
try {
switch (optID) {
case SocketOptions.SO_LINGER:
if (value instanceof Boolean) {
final boolean b = (Boolean) value;
if (b) {
throw new SocketException("Only accepting Boolean.FALSE here");
}
NativeUnixSocket.setSocketOptionInt(fd, optID, -1);
return;
}
NativeUnixSocket.setSocketOptionInt(fd, optID, expectInteger(value));
return;
case SocketOptions.SO_RCVBUF:
case SocketOptions.SO_SNDBUF:
case SocketOptions.SO_TIMEOUT:
NativeUnixSocket.setSocketOptionInt(fd, optID, expectInteger(value));
return;
case SocketOptions.SO_KEEPALIVE:
case SocketOptions.TCP_NODELAY:
NativeUnixSocket.setSocketOptionInt(fd, optID, expectBoolean(value));
return;
default:
throw new AFUNIXSocketException("Unsupported option: " + optID);
}
} catch (final AFUNIXSocketException e) {
throw e;
} catch (final Exception e) {
throw new AFUNIXSocketException("Error while setting option", e);
}
}
@Override
protected void shutdownInput() throws IOException {
if (!closed && fd.valid()) {
NativeUnixSocket.shutdown(fd, SHUT_RD);
}
}
@Override
protected void shutdownOutput() throws IOException {
if (!closed && fd.valid()) {
NativeUnixSocket.shutdown(fd, SHUT_WR);
}
}
/**
* Changes the behavior to be somewhat lenient with respect to the specification.
*
* In particular, we ignore calls to {@link Socket#getTcpNoDelay()} and
* {@link Socket#setTcpNoDelay(boolean)}.
*/
static class Lenient extends AFUNIXSocketImpl {
Lenient() {
super();
}
@Override
public void setOption(int optID, Object value) throws SocketException {
try {
super.setOption(optID, value);
} catch (SocketException e) {
switch (optID) {
case SocketOptions.TCP_NODELAY:
return;
default:
throw e;
}
}
}
@Override
public Object getOption(int optID) throws SocketException {
try {
return super.getOption(optID);
} catch (SocketException e) {
switch (optID) {
case SocketOptions.TCP_NODELAY:
case SocketOptions.SO_KEEPALIVE:
return false;
default:
throw e;
}
}
}
}
}

131
fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/NativeUnixSocket.java

@ -0,0 +1,131 @@
/**
* junixsocket
*
* Copyright (c) 2009,2014 Christian Kohlschütter
*
* The author licenses this file to You 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 com.fr.third.org.newsclub.net.unix;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
/**
* JNI connector to native JNI C code.
*
* @author Christian Kohlschütter
*/
final class NativeUnixSocket {
private static boolean loaded = false;
static {
try {
Class.forName("org.newsclub.net.unix.NarSystem").getMethod("loadLibrary").invoke(null);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(
"Could not find NarSystem class.\n\n*** ECLIPSE USERS ***\nIf you're running from "
+ "within Eclipse, please try closing the \"junixsocket-native-common\" "
+ "project\n", e);
} catch (Exception e) {
throw new IllegalStateException(e);
}
loaded = true;
}
static boolean isLoaded() {
return loaded;
}
static void checkSupported() {
}
static native void bind(final String socketFile, final FileDescriptor fd, final int backlog)
throws IOException;
static native void listen(final FileDescriptor fd, final int backlog) throws IOException;
static native void accept(final String socketFile, final FileDescriptor fdServer,
final FileDescriptor fd) throws IOException;
static native void connect(final String socketFile, final FileDescriptor fd) throws IOException;
static native int read(final FileDescriptor fd, byte[] buf, int off, int len) throws IOException;
static native int write(final FileDescriptor fd, byte[] buf, int off, int len) throws IOException;
static native void close(final FileDescriptor fd) throws IOException;
static native void shutdown(final FileDescriptor fd, int mode) throws IOException;
static native int getSocketOptionInt(final FileDescriptor fd, int optionId) throws IOException;
static native void setSocketOptionInt(final FileDescriptor fd, int optionId, int value)
throws IOException;
static native void unlink(final String socketFile) throws IOException;
static native int available(final FileDescriptor fd) throws IOException;
static native void initServerImpl(final AFUNIXServerSocket serverSocket,
final AFUNIXSocketImpl impl);
static native void setCreated(final AFUNIXSocket socket);
static native void setConnected(final AFUNIXSocket socket);
static native void setBound(final AFUNIXSocket socket);
static native void setCreatedServer(final AFUNIXServerSocket socket);
static native void setBoundServer(final AFUNIXServerSocket socket);
static native void setPort(final AFUNIXSocketAddress addr, int port);
static void setPort1(AFUNIXSocketAddress addr, int port) throws AFUNIXSocketException {
if (port < 0) {
throw new IllegalArgumentException("port out of range:" + port);
}
boolean setOk = false;
try {
final Field holderField = InetSocketAddress.class.getDeclaredField("holder");
if (holderField != null) {
holderField.setAccessible(true);
final Object holder = holderField.get(addr);
if (holder != null) {
final Field portField = holder.getClass().getDeclaredField("port");
if (portField != null) {
portField.setAccessible(true);
portField.set(holder, port);
setOk = true;
}
}
} else {
setPort(addr, port);
}
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
if (e instanceof AFUNIXSocketException) {
throw (AFUNIXSocketException) e;
}
throw new AFUNIXSocketException("Could not set port", e);
}
if (!setOk) {
throw new AFUNIXSocketException("Could not set port");
}
}
}

5
fine-byte-buddy/src/com/fr/third/org/newsclub/net/unix/package-info.java

@ -0,0 +1,5 @@
/**
* The actual AF_UNIX Socket implementation.
*/
package com.fr.third.org.newsclub.net.unix;
Loading…
Cancel
Save