diff options
Diffstat (limited to 'tunnel/src/main')
4 files changed, 442 insertions, 1 deletions
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java index 00a528f2..f10ddc52 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java @@ -7,14 +7,22 @@ package com.wireguard.android.backend; import android.content.Context; import android.content.Intent; +import android.net.LocalSocketAddress; import android.net.ProxyInfo; import android.os.Build; import android.os.ParcelFileDescriptor; import android.system.OsConstants; import android.util.Log; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Empty; + import com.wireguard.android.backend.BackendException.Reason; import com.wireguard.android.backend.Tunnel.State; +import com.wireguard.android.backend.gen.LibwgGrpc; +import com.wireguard.android.backend.gen.VersionRequest; +import com.wireguard.android.backend.gen.VersionResponse; import com.wireguard.android.util.SharedLibraryLoader; import com.wireguard.config.Config; import com.wireguard.config.InetEndpoint; @@ -24,6 +32,11 @@ import com.wireguard.crypto.Key; import com.wireguard.crypto.KeyFormatException; import com.wireguard.util.NonNullForAll; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.okhttp.OkHttpChannelBuilder; + +import java.io.File; import java.net.InetAddress; import java.net.URL; import java.util.Collections; @@ -34,6 +47,8 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import javax.net.SocketFactory; + import androidx.annotation.Nullable; import androidx.collection.ArraySet; @@ -51,6 +66,7 @@ public final class GoBackend implements Backend { @Nullable private Config currentConfig; @Nullable private Tunnel currentTunnel; private int currentTunnelHandle = -1; + private ManagedChannel channel; /** * Public constructor for GoBackend. @@ -60,6 +76,15 @@ public final class GoBackend implements Backend { public GoBackend(final Context context) { SharedLibraryLoader.loadSharedLibrary(context, "wg-go"); this.context = context; + + File socketFile = new File(context.getCacheDir(), "libwg.sock"); + String socketName = socketFile.getAbsolutePath(); + Log.i(TAG, "wgStartGrpc: " + wgStartGrpc(socketName)); + ManagedChannelBuilder<?> channelBuilder = ManagedChannelBuilder.forAddress("localhost", 10000).usePlaintext(); + LocalSocketAddress address = new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.FILESYSTEM); + SocketFactory socketFactory = new UnixDomainSocketFactory(address); + ((OkHttpChannelBuilder) channelBuilder).socketFactory(socketFactory); + channel = channelBuilder.build(); } /** @@ -84,6 +109,8 @@ public final class GoBackend implements Backend { private static native String wgVersion(); + private static native int wgStartGrpc(String sockName); + /** * Method to get the names of running tunnels. * @@ -168,7 +195,10 @@ public final class GoBackend implements Backend { */ @Override public String getVersion() { - return wgVersion(); + LibwgGrpc.LibwgBlockingStub stub = LibwgGrpc.newBlockingStub(channel); + VersionRequest request = VersionRequest.newBuilder().build(); + VersionResponse resp = stub.version(request); + return resp.getVersion(); } /** diff --git a/tunnel/src/main/java/com/wireguard/android/backend/LocalSocketAdapter.java b/tunnel/src/main/java/com/wireguard/android/backend/LocalSocketAdapter.java new file mode 100644 index 00000000..bf027ae1 --- /dev/null +++ b/tunnel/src/main/java/com/wireguard/android/backend/LocalSocketAdapter.java @@ -0,0 +1,325 @@ +/* + */ +package com.wireguard.android.backend; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketImplFactory; +import java.net.SocketOptions; +import java.nio.channels.SocketChannel; +import java.util.Vector; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.util.Log; + +/** + * Adaptor allows using a LocalSocket as a Socket. + */ +final class LocalSocketAdapter extends Socket { + private final LocalSocketAddress address; + private final LocalSocket unix; + private final SocketAddress localAddress; + private InetSocketAddress inetSocketAddress; + private InputStream is; + private OutputStream os; + + LocalSocketAdapter(LocalSocketAddress address) { + this.address = address; + this.localAddress = new InetSocketAddress(0); + unix = new LocalSocket(); + } + + LocalSocketAdapter(LocalSocketAddress address, InetSocketAddress inetAddress) { + this(address); + this.inetSocketAddress = inetAddress; + } + + private void throwUnsupportedOperationException() { + Log.i("helloworld", "Unsupported: " + Log.getStackTraceString(new Exception())); + throw new UnsupportedOperationException(); + } + + @Override + public void bind (SocketAddress bindpoint) { + throwUnsupportedOperationException(); + } + + @Override + public void close() throws IOException { + unix.close(); + } + + @Override public void connect(SocketAddress endpoint) throws IOException { + this.inetSocketAddress = (InetSocketAddress) endpoint; + try { + unix.connect(address); + } catch (IOException e) { + Log.i("helloworld", "Error: " + e.toString()); + throw e; + } + } + + @Override + public void connect(SocketAddress endpoint, int timeout) throws IOException { + this.inetSocketAddress = (InetSocketAddress) endpoint; + unix.connect(address, timeout); + } + + @Override + public SocketChannel getChannel() { + throwUnsupportedOperationException(); + return null; + } + + @Override public InetAddress getInetAddress() { + return inetSocketAddress.getAddress(); + } + + @Override + public InputStream getInputStream() throws IOException { + is = unix.getInputStream(); + return is; + } + + @Override + public boolean getKeepAlive() { + throwUnsupportedOperationException(); + return false; + } + + @Override + public InetAddress getLocalAddress() { + throwUnsupportedOperationException(); + return null; + } + + @Override + public int getLocalPort() { + throwUnsupportedOperationException(); + return 0; + } + + @Override + public SocketAddress getLocalSocketAddress() { + //throwUnsupportedOperationException(); + return localAddress; + } + + @Override + public boolean getOOBInline() { + throwUnsupportedOperationException(); + return false; + } + + @Override + public OutputStream getOutputStream() throws IOException { + if (os != null) + return os; + + OutputStream unixOs = unix.getOutputStream(); + os = new OutputStream() { + @Override + public void close() throws IOException { + // LocalSocket's default implementation closes the socket, + // which leaves readers of thes InputStream hanging. + // Instead shutdown input (and output) to release readers. + LocalSocketAdapter.this.shutdownInput(); + LocalSocketAdapter.this.shutdownOutput(); + } + + @Override + public void write (byte[] b) throws IOException { + unixOs.write(b); + } + + @Override + public void write (byte[] b, int off, int len) throws IOException { + unixOs.write(b, off, len); + } + + @Override + public void write (int b) throws IOException { + unixOs.write(b); + } + }; + return os; + } + + @Override + public int getPort() { + return inetSocketAddress.getPort(); + } + + @Override + public int getReceiveBufferSize() throws SocketException { + try { + return unix.getReceiveBufferSize(); + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + + @Override + public SocketAddress getRemoteSocketAddress() { + return inetSocketAddress; + } + + @Override + public boolean getReuseAddress() { + throwUnsupportedOperationException(); + return false; + } + + @Override + public int getSendBufferSize() throws SocketException { + try { + return unix.getSendBufferSize(); + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + + @Override + public int getSoLinger() { + throwUnsupportedOperationException(); + return 0; + } + + @Override + public int getSoTimeout() throws SocketException { + try { + return unix.getSoTimeout(); + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + + @Override + public boolean getTcpNoDelay() { + throwUnsupportedOperationException(); + return false; + } + + @Override + public int getTrafficClass() { + throwUnsupportedOperationException(); + return 0; + } + + @Override + public boolean isBound() { + return unix.isBound(); + } + + @Override + public boolean isClosed() { + return unix.isClosed(); + } + + @Override + public boolean isConnected() { + return unix.isConnected(); + } + + @Override + public boolean isInputShutdown() { + return unix.isInputShutdown(); + } + + @Override + public boolean isOutputShutdown() { + return unix.isOutputShutdown(); + } + + @Override + public void sendUrgentData (int data) { + throwUnsupportedOperationException(); + } + + @Override + public void setKeepAlive (boolean on) { + throwUnsupportedOperationException(); + } + + @Override + public void setOOBInline (boolean on) { + throwUnsupportedOperationException(); + } + + @Override + public void setPerformancePreferences (int connectionTime, + int latency, + int bandwidth) { + throwUnsupportedOperationException(); + } + + @Override + public void setReceiveBufferSize(int size) throws SocketException { + try { + unix.setReceiveBufferSize(size); + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + + @Override + public void setReuseAddress (boolean on) { + throwUnsupportedOperationException(); + } + + @Override + public void setSendBufferSize(int size) throws SocketException { + try { + unix.setSendBufferSize(size); + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + + @Override + public void setSoLinger (boolean on, + int linger) { + throwUnsupportedOperationException(); + } + + @Override + public void setSoTimeout(int timeout) throws SocketException { + try { + unix.setSoTimeout(timeout); + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + + @Override + public void setTcpNoDelay (boolean on) { + // Not relevant for local sockets. + } + + @Override + public void setTrafficClass (int tc) { + throwUnsupportedOperationException(); + } + + @Override + public void shutdownInput() throws IOException { + unix.shutdownInput(); + } + + @Override + public void shutdownOutput() throws IOException { + unix.shutdownOutput(); + } + + @Override + public String toString() { + return unix.toString(); + } +} diff --git a/tunnel/src/main/java/com/wireguard/android/backend/UnixDomainSocketFactory.java b/tunnel/src/main/java/com/wireguard/android/backend/UnixDomainSocketFactory.java new file mode 100644 index 00000000..427a19f1 --- /dev/null +++ b/tunnel/src/main/java/com/wireguard/android/backend/UnixDomainSocketFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 Square, Inc. + * + * 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 com.wireguard.android.backend; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import javax.net.SocketFactory; +import android.net.LocalSocketAddress; + +/** Impersonate TCP-style SocketFactory over UNIX domain sockets. */ +public final class UnixDomainSocketFactory extends SocketFactory { + private final LocalSocketAddress address; + + public UnixDomainSocketFactory(LocalSocketAddress address) { + this.address = address; + } + + @Override public Socket createSocket() throws IOException { + return new LocalSocketAdapter(address); + } + + @Override public Socket createSocket(String host, int port) throws IOException { + Socket result = createSocket(); + result.connect(new InetSocketAddress(host, port)); + return result; + } + + @Override public Socket createSocket( + String host, int port, InetAddress localHost, int localPort) throws IOException { + return createSocket(host, port); + } + + @Override public Socket createSocket(InetAddress host, int port) throws IOException { + Socket result = createSocket(); + result.connect(new InetSocketAddress(host, port)); + return result; + } + + @Override public Socket createSocket( + InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException { + return createSocket(host, port); + } +} diff --git a/tunnel/src/main/proto/libwg.proto b/tunnel/src/main/proto/libwg.proto new file mode 100644 index 00000000..2d964897 --- /dev/null +++ b/tunnel/src/main/proto/libwg.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = 'com.wireguard.android.backend.gen'; +option java_outer_classname = "LibwgProto"; +option java_generic_services = true; +option go_package = 'golang.zx2c4.com/wireguard/android/gen'; + +package api; + +service Libwg { + rpc StopGrpc(StopGrpcRequest) returns (StopGrpcResponse); + rpc Version(VersionRequest) returns (VersionResponse); +} + +message StopGrpcRequest { +} + +message StopGrpcResponse { +} + +message VersionRequest { +} + +message VersionResponse { + string version = 1; +} |