From 0365d9b3619117c54e1e1db6f3e44a0c3ebc1595 Mon Sep 17 00:00:00 2001 From: FongMi Date: Thu, 8 Dec 2022 17:25:53 +0800 Subject: [PATCH] Support drpy - part 2 --- drpy/build.gradle | 1 + drpy/src/main/java/com/hiker/drpy/Loader.java | 97 +++++++++++++++ .../main/java/com/hiker/drpy/net/OkHttp.java | 82 +++++++++++++ .../drpy/net/SSLSocketFactoryCompat.java | 114 ++++++++++++++++++ 4 files changed, 294 insertions(+) create mode 100644 drpy/src/main/java/com/hiker/drpy/net/OkHttp.java create mode 100644 drpy/src/main/java/com/hiker/drpy/net/SSLSocketFactoryCompat.java diff --git a/drpy/build.gradle b/drpy/build.gradle index 755008e5c..b0e7cf855 100644 --- a/drpy/build.gradle +++ b/drpy/build.gradle @@ -15,4 +15,5 @@ android { dependencies { implementation project(':catvod') implementation 'wang.harlon.quickjs:wrapper-android:0.6.0' + implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10' } \ No newline at end of file diff --git a/drpy/src/main/java/com/hiker/drpy/Loader.java b/drpy/src/main/java/com/hiker/drpy/Loader.java index 643584628..30bd958a7 100644 --- a/drpy/src/main/java/com/hiker/drpy/Loader.java +++ b/drpy/src/main/java/com/hiker/drpy/Loader.java @@ -1,10 +1,107 @@ package com.hiker.drpy; import android.content.Context; +import android.content.SharedPreferences; +import android.util.Base64; +import android.util.Log; + +import com.hiker.drpy.net.OkHttp; +import com.whl.quickjs.wrapper.JSArray; +import com.whl.quickjs.wrapper.JSObject; +import com.whl.quickjs.wrapper.QuickJSContext; + +import org.json.JSONObject; + +import java.io.IOException; + +import okhttp3.Response; public class Loader { + private final String TAG = Loader.class.getSimpleName(); + private QuickJSContext jsContext; + private Context context; + + private void init(Context context) { + System.loadLibrary("quickjs-android-wrapper"); + this.jsContext = QuickJSContext.create(); + this.context = context; + initConsole(); + initLocal(); + initReq(); + } + public Spider spider(Context context, String name, String ext) { + if (jsContext == null) init(context); return new Spider(); } + + private void initConsole() { + jsContext.evaluate("var console = {};"); + JSObject console = (JSObject) jsContext.getGlobalObject().getProperty("console"); + console.setProperty("log", args -> { + StringBuilder b = new StringBuilder(); + for (Object o : args) b.append(o == null ? "null" : o.toString()); + Log.e(TAG, b.toString()); + return null; + }); + } + + private void initLocal() { + jsContext.evaluate("var local = {};"); + JSObject local = (JSObject) jsContext.getGlobalObject().getProperty("local"); + local.setProperty("get", args -> { + SharedPreferences pref = context.getSharedPreferences("js_engine_" + args[0].toString(), Context.MODE_PRIVATE); + return pref.getString(args[1].toString(), ""); + }); + local.setProperty("set", args -> { + SharedPreferences pref = context.getSharedPreferences("js_engine_" + args[0].toString(), Context.MODE_PRIVATE); + pref.edit().putString(args[1].toString(), args[2].toString()).apply(); + return null; + }); + local.setProperty("delete", args -> { + SharedPreferences pref = context.getSharedPreferences("js_engine_" + args[0].toString(), Context.MODE_PRIVATE); + pref.edit().remove(args[1].toString()).apply(); + return null; + }); + } + + private void initReq() { + jsContext.getGlobalObject().setProperty("req", args -> { + JSObject jsObject = jsContext.createNewJSObject(); + JSObject jsHeader = jsContext.createNewJSObject(); + setReqObject(jsObject, jsHeader, args); + return jsObject; + }); + } + + private void setReqObject(JSObject jsObject, JSObject jsHeader, Object... args) { + try { + JSONObject obj = new JSONObject(jsContext.stringify((JSObject) args[1])); + Response response = OkHttp.get().newCall(args[0].toString(), obj).execute(); + for (String name : response.headers().names()) jsHeader.setProperty(name, response.header(name)); + jsObject.setProperty("headers", jsHeader); + setReqContent(jsObject, obj, response); + } catch (Throwable e) { + jsObject.setProperty("headers", jsHeader); + jsObject.setProperty("content", ""); + } + } + + private void setReqContent(JSObject jsObject, JSONObject obj, Response response) throws IOException { + switch (obj.optInt("buffer")) { + case 1: + byte[] bytes = response.body().bytes(); + JSArray array = jsContext.createNewJSArray(); + for (int i = 0; i < bytes.length; i++) array.set(bytes[i], i); + jsObject.setProperty("content", array); + break; + case 2: + jsObject.setProperty("content", Base64.encodeToString(response.body().bytes(), Base64.DEFAULT)); + break; + default: + jsObject.setProperty("content", response.body().string()); + break; + } + } } diff --git a/drpy/src/main/java/com/hiker/drpy/net/OkHttp.java b/drpy/src/main/java/com/hiker/drpy/net/OkHttp.java new file mode 100644 index 000000000..45dacfaa0 --- /dev/null +++ b/drpy/src/main/java/com/hiker/drpy/net/OkHttp.java @@ -0,0 +1,82 @@ +package com.hiker.drpy.net; + +import org.json.JSONObject; + +import java.util.Iterator; +import java.util.concurrent.TimeUnit; + +import okhttp3.Call; +import okhttp3.Headers; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; + +public class OkHttp { + + private final OkHttpClient client; + private final OkHttpClient noRedirect; + + private static class Loader { + static volatile OkHttp INSTANCE = new OkHttp(); + } + + public static OkHttp get() { + return Loader.INSTANCE; + } + + public OkHttp() { + client = getBuilder().build(); + noRedirect = client.newBuilder().followRedirects(false).followSslRedirects(false).build(); + } + + private OkHttpClient.Builder getBuilder() { + return new OkHttpClient.Builder().sslSocketFactory(new SSLSocketFactoryCompat(), SSLSocketFactoryCompat.trustAllCert); + } + + private OkHttpClient client() { + return client; + } + + private OkHttpClient noRedirect() { + return noRedirect; + } + + public Call newCall(String url, JSONObject object) { + int redirect = object.optInt("redirect", 1); + int timeout = object.optInt("timeout", 10000); + OkHttpClient client = redirect == 1 ? client() : noRedirect(); + client.newBuilder().callTimeout(timeout, TimeUnit.MILLISECONDS).readTimeout(timeout, TimeUnit.MILLISECONDS).writeTimeout(timeout, TimeUnit.MILLISECONDS).connectTimeout(timeout, TimeUnit.MILLISECONDS); + return client.newCall(getRequest(url, object)); + } + + private Request getRequest(String url, JSONObject object) { + String method = object.optString("method", "get"); + Headers headers = getHeader(object.optJSONObject("headers")); + if (method.equalsIgnoreCase("post")) { + return new Request.Builder().url(url).headers(headers).post(getPostBody(object, headers.get("Content-Type"))).build(); + } else if (method.equalsIgnoreCase("header")) { + return new Request.Builder().url(url).headers(headers).head().build(); + } else { + return new Request.Builder().url(url).headers(headers).get().build(); + } + } + + private Headers getHeader(JSONObject object) { + Headers.Builder builder = new Headers.Builder(); + if (object == null) return builder.build(); + for (Iterator iterator = object.keys(); iterator.hasNext(); ) { + String key = iterator.next(); + builder.add(key, object.optString(key)); + } + return builder.build(); + } + + private RequestBody getPostBody(JSONObject object, String contentType) { + String body = object.optString("body").trim(); + String data = object.optString("data").trim(); + if (data.length() > 0) return RequestBody.create(data, MediaType.get("application/json")); + if (body.length() > 0 && contentType != null) return RequestBody.create(body, MediaType.get(contentType)); + return RequestBody.create("", null); + } +} diff --git a/drpy/src/main/java/com/hiker/drpy/net/SSLSocketFactoryCompat.java b/drpy/src/main/java/com/hiker/drpy/net/SSLSocketFactoryCompat.java new file mode 100644 index 000000000..c74c5a0f5 --- /dev/null +++ b/drpy/src/main/java/com/hiker/drpy/net/SSLSocketFactoryCompat.java @@ -0,0 +1,114 @@ +package com.hiker.drpy.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.security.GeneralSecurityException; +import java.security.cert.X509Certificate; +import java.util.LinkedList; +import java.util.List; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; + +public class SSLSocketFactoryCompat extends SSLSocketFactory { + + public static final X509TrustManager trustAllCert = new X509TrustManager() { + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + }; + + static String[] protocols = null; + static String[] cipherSuites = null; + + static { + try { + SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); + if (socket != null) { + List protocols = new LinkedList<>(); + for (String protocol : socket.getSupportedProtocols()) if (!protocol.toUpperCase().contains("SSL")) protocols.add(protocol); + SSLSocketFactoryCompat.protocols = protocols.toArray(new String[protocols.size()]); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private final SSLSocketFactory defaultFactory; + + public SSLSocketFactoryCompat() { + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new X509TrustManager[]{SSLSocketFactoryCompat.trustAllCert}, null); + defaultFactory = sslContext.getSocketFactory(); + HttpsURLConnection.setDefaultSSLSocketFactory(defaultFactory); + } catch (GeneralSecurityException e) { + throw new AssertionError(); // The system has no TLS. Just give up. + } + } + + private void upgradeTLS(SSLSocket ssl) { + if (protocols != null) { + ssl.setEnabledProtocols(protocols); + } + } + + @Override + public String[] getDefaultCipherSuites() { + return cipherSuites; + } + + @Override + public String[] getSupportedCipherSuites() { + return cipherSuites; + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + Socket ssl = defaultFactory.createSocket(s, host, port, autoClose); + if (ssl instanceof SSLSocket) upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + Socket ssl = defaultFactory.createSocket(host, port); + if (ssl instanceof SSLSocket) upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { + Socket ssl = defaultFactory.createSocket(host, port, localHost, localPort); + if (ssl instanceof SSLSocket) upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + Socket ssl = defaultFactory.createSocket(host, port); + if (ssl instanceof SSLSocket) upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + Socket ssl = defaultFactory.createSocket(address, port, localAddress, localPort); + if (ssl instanceof SSLSocket) upgradeTLS((SSLSocket) ssl); + return ssl; + } +}