From 2ac1ab544e43c3982ee6a02a4cb6425d0467b241 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 15 Nov 2018 18:48:13 -0500 Subject: [PATCH] http service: use jav_config for determining revision The handshakes are now ambigious due to having to support multiple client releases --- .../updatecheck/ClientConfigLoader.java | 84 ++++++++++++ .../http/service/updatecheck/RSConfig.java | 52 +++++++ .../updatecheck/UpdateCheckService.java | 129 ++++-------------- 3 files changed, 166 insertions(+), 99 deletions(-) create mode 100644 http-service/src/main/java/net/runelite/http/service/updatecheck/ClientConfigLoader.java create mode 100644 http-service/src/main/java/net/runelite/http/service/updatecheck/RSConfig.java diff --git a/http-service/src/main/java/net/runelite/http/service/updatecheck/ClientConfigLoader.java b/http-service/src/main/java/net/runelite/http/service/updatecheck/ClientConfigLoader.java new file mode 100644 index 0000000000..8df39598bd --- /dev/null +++ b/http-service/src/main/java/net/runelite/http/service/updatecheck/ClientConfigLoader.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018, Adam + * Copyright (c) 2018, Tomas Slusny + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.http.service.updatecheck; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import net.runelite.http.api.RuneLiteAPI; +import okhttp3.Request; +import okhttp3.Response; + +class ClientConfigLoader +{ + private static final String CONFIG_URL = "http://oldschool.runescape.com/jav_config.ws"; + + static RSConfig fetch() throws IOException + { + final Request request = new Request.Builder() + .url(CONFIG_URL) + .build(); + + final RSConfig config = new RSConfig(); + + try (final Response response = RuneLiteAPI.CLIENT.newCall(request).execute(); final BufferedReader in = new BufferedReader( + new InputStreamReader(response.body().byteStream()))) + { + String str; + + while ((str = in.readLine()) != null) + { + int idx = str.indexOf('='); + + if (idx == -1) + { + continue; + } + + String s = str.substring(0, idx); + + switch (s) + { + case "param": + str = str.substring(idx + 1); + idx = str.indexOf('='); + s = str.substring(0, idx); + + config.getAppletProperties().put(s, str.substring(idx + 1)); + break; + case "msg": + // ignore + break; + default: + config.getClassLoaderProperties().put(s, str.substring(idx + 1)); + break; + } + } + } + + return config; + } +} diff --git a/http-service/src/main/java/net/runelite/http/service/updatecheck/RSConfig.java b/http-service/src/main/java/net/runelite/http/service/updatecheck/RSConfig.java new file mode 100644 index 0000000000..def3919d7f --- /dev/null +++ b/http-service/src/main/java/net/runelite/http/service/updatecheck/RSConfig.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018, Adam + * Copyright (c) 2018, Tomas Slusny + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.http.service.updatecheck; + +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; + +@Getter +class RSConfig +{ + private final Map appletProperties = new HashMap<>(); + private final Map classLoaderProperties = new HashMap<>(); + + String getCodeBase() + { + return classLoaderProperties.get("codebase"); + } + + String getInitialJar() + { + return classLoaderProperties.get("initial_jar"); + } + + String getInitialClass() + { + return classLoaderProperties.get("initial_class").replace(".class", ""); + } +} diff --git a/http-service/src/main/java/net/runelite/http/service/updatecheck/UpdateCheckService.java b/http-service/src/main/java/net/runelite/http/service/updatecheck/UpdateCheckService.java index 883657b9fb..9af87a9a39 100644 --- a/http-service/src/main/java/net/runelite/http/service/updatecheck/UpdateCheckService.java +++ b/http-service/src/main/java/net/runelite/http/service/updatecheck/UpdateCheckService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Adam + * Copyright (c) 2018, Adam * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,52 +25,22 @@ package net.runelite.http.service.updatecheck; 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.UnknownHostException; -import java.nio.ByteBuffer; -import java.time.Duration; -import java.util.List; -import java.util.Random; +import lombok.extern.slf4j.Slf4j; import net.runelite.http.api.RuneLiteAPI; -import net.runelite.http.api.worlds.World; -import net.runelite.http.api.worlds.WorldResult; -import net.runelite.http.service.worlds.WorldsService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; +import okhttp3.HttpUrl; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/update-check") +@Slf4j public class UpdateCheckService { - private static final Logger logger = LoggerFactory.getLogger(UpdateCheckService.class); - - private static final Duration TIMEOUT = Duration.ofSeconds(5); - - private static final int PORT = 43594; - private static final byte HANDSHAKE_TYPE = 15; - - private static final int RESPONSE_OK = 0; - private static final int RESPONSE_OUTDATED = 6; - - private final WorldsService worldsService; private boolean updateAvailable; - @Autowired - public UpdateCheckService(WorldsService worldsService) - { - this.worldsService = worldsService; - } - @RequestMapping - public Boolean check() + public boolean check() { return updateAvailable; } @@ -81,84 +51,45 @@ public class UpdateCheckService updateAvailable = checkUpdate(); } - private int checkResponse(InetAddress address, int revision) throws IOException + private int getRevision() throws IOException { - try (Socket socket = new Socket()) + RSConfig config = ClientConfigLoader.fetch(); + + for (String value : config.getAppletProperties().values()) { - socket.setSoTimeout((int) TIMEOUT.toMillis()); - socket.connect(new InetSocketAddress(address, PORT), (int) TIMEOUT.toMillis()); - - ByteBuffer buffer = ByteBuffer.allocate(5); - buffer.put(HANDSHAKE_TYPE); - buffer.putInt(revision); - - InputStream is = socket.getInputStream(); - OutputStream os = socket.getOutputStream(); - os.write(buffer.array()); - - int reply = is.read(); - return reply; + // http://www.runescape.com/g=oldscape/slr.ws?order=LPWM&ep=176 + if (value.contains("slr.ws")) + { + HttpUrl url = HttpUrl.parse(value); + String revstr = url.queryParameter("ep"); + int rev = Integer.parseInt(revstr); + return rev; + } } + + return -1; } private boolean checkUpdate() { - World nextWorld = randomWorld(); - if (nextWorld == null) - { - return false; - } - - InetAddress address; + int rev; try { - String url = nextWorld.getAddress(); - address = InetAddress.getByName(url); + rev = getRevision(); } - catch (UnknownHostException ex) + catch (IOException e) { - logger.warn(null, ex); + log.warn("error checking revision", e); + return false; + } + + if (rev == -1) + { + log.warn("Unable to parse revision from config!"); return false; } - // Since mobile, the handshake server will handshake multiple revisions successfully, - // so we can't assume that just because it says our revision is okay doesn't mean that - // the client revision hasn't changed. int thisRevision = RuneLiteAPI.getRsVersion(); - int nextRevision = thisRevision + 1; - - try - { - int thisCode = checkResponse(address, thisRevision); - int nextCode = checkResponse(address, nextRevision); - - if (thisCode == RESPONSE_OK && nextCode == RESPONSE_OUTDATED) - { - return false; // This is most up-to-date - } - - return true; // Needs to be updated - } - catch (IOException ex) - { - logger.warn(null, ex); - return false; // assume not updated - } - } - - private World randomWorld() - { - try - { - WorldResult worldResult = worldsService.getWorlds(); - List worlds = worldResult.getWorlds(); - Random rand = new Random(); - return worlds.get(rand.nextInt(worlds.size())); - } - catch (IOException ex) - { - logger.warn(null, ex); - return null; - } + return rev != thisRevision; } }