mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-20 11:33:06 +00:00
Add read timeouts to HTTP requests that should come back immediately if GFE is working properly
This commit is contained in:
parent
c9d003ca6d
commit
5f42ca66fe
@ -31,6 +31,7 @@ public class NvHTTP {
|
||||
|
||||
public static final int PORT = 47984;
|
||||
public static final int CONNECTION_TIMEOUT = 2000;
|
||||
public static final int READ_TIMEOUT = 2000;
|
||||
|
||||
private final boolean verbose = false;
|
||||
|
||||
@ -101,7 +102,7 @@ public class NvHTTP {
|
||||
}
|
||||
|
||||
public String getServerInfo(String uniqueId) throws MalformedURLException, IOException {
|
||||
return openHttpConnectionToString(baseUrl + "/serverinfo?uniqueid=" + uniqueId);
|
||||
return openHttpConnectionToString(baseUrl + "/serverinfo?uniqueid=" + uniqueId, true);
|
||||
}
|
||||
|
||||
public ComputerDetails getComputerDetails() throws MalformedURLException, IOException, XmlPullParserException {
|
||||
@ -146,19 +147,27 @@ public class NvHTTP {
|
||||
return details;
|
||||
}
|
||||
|
||||
private InputStream openHttpConnection(String url) throws IOException {
|
||||
// Read timeout should be enabled for any HTTP query that requires no outside action
|
||||
// on the GFE server. Examples of queries that DO require outside action are launch, resume, and quit.
|
||||
// The initial pair query does require outside action (user entering a PIN) but subsequent pairing
|
||||
// queries do not.
|
||||
private InputStream openHttpConnection(String url, boolean enableReadTimeout) throws IOException {
|
||||
URLConnection conn = new URL(url).openConnection();
|
||||
if (verbose) {
|
||||
System.out.println(url);
|
||||
}
|
||||
conn.setConnectTimeout(CONNECTION_TIMEOUT);
|
||||
if (enableReadTimeout) {
|
||||
conn.setReadTimeout(READ_TIMEOUT);
|
||||
}
|
||||
|
||||
conn.setUseCaches(false);
|
||||
conn.connect();
|
||||
return conn.getInputStream();
|
||||
}
|
||||
|
||||
String openHttpConnectionToString(String url) throws MalformedURLException, IOException {
|
||||
Scanner s = new Scanner(openHttpConnection(url));
|
||||
String openHttpConnectionToString(String url, boolean enableReadTimeout) throws MalformedURLException, IOException {
|
||||
Scanner s = new Scanner(openHttpConnection(url, enableReadTimeout));
|
||||
|
||||
String str = "";
|
||||
while (s.hasNext()) {
|
||||
@ -207,12 +216,14 @@ public class NvHTTP {
|
||||
}
|
||||
|
||||
public InputStream getBoxArtPng(NvApp app) throws IOException {
|
||||
// FIXME: Investigate whether this should be subject to the 2 second read timeout
|
||||
// or not.
|
||||
return openHttpConnection(baseUrl + "/applist?uniqueid="+uniqueId+"&appid="+
|
||||
app.getAppId()+"&AssetType=2&AssetIdx=0");
|
||||
app.getAppId()+"&AssetType=2&AssetIdx=0", false);
|
||||
}
|
||||
|
||||
public LinkedList<NvApp> getAppList() throws GfeHttpResponseException, IOException, XmlPullParserException {
|
||||
InputStream in = openHttpConnection(baseUrl + "/applist?uniqueid=" + uniqueId);
|
||||
InputStream in = openHttpConnection(baseUrl + "/applist?uniqueid=" + uniqueId, true);
|
||||
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
XmlPullParser xpp = factory.newPullParser();
|
||||
@ -253,7 +264,7 @@ public class NvHTTP {
|
||||
}
|
||||
|
||||
public void unpair() throws IOException {
|
||||
openHttpConnection(baseUrl + "/unpair?uniqueid=" + uniqueId);
|
||||
openHttpConnection(baseUrl + "/unpair?uniqueid=" + uniqueId, true);
|
||||
}
|
||||
|
||||
final private static char[] hexArray = "0123456789ABCDEF".toCharArray();
|
||||
@ -274,7 +285,7 @@ public class NvHTTP {
|
||||
"&mode=" + width + "x" + height + "x" + refreshRate +
|
||||
"&additionalStates=1&sops=" + (sops ? 1 : 0) +
|
||||
"&rikey="+bytesToHex(inputKey.getEncoded()) +
|
||||
"&rikeyid="+riKeyId);
|
||||
"&rikeyid="+riKeyId, false);
|
||||
String gameSession = getXmlString(in, "gamesession");
|
||||
return Integer.parseInt(gameSession);
|
||||
}
|
||||
@ -282,13 +293,13 @@ public class NvHTTP {
|
||||
public boolean resumeApp(SecretKey inputKey, int riKeyId) throws IOException, XmlPullParserException {
|
||||
InputStream in = openHttpConnection(baseUrl + "/resume?uniqueid=" + uniqueId +
|
||||
"&rikey="+bytesToHex(inputKey.getEncoded()) +
|
||||
"&rikeyid="+riKeyId);
|
||||
"&rikeyid="+riKeyId, false);
|
||||
String resume = getXmlString(in, "resume");
|
||||
return Integer.parseInt(resume) != 0;
|
||||
}
|
||||
|
||||
public boolean quitApp() throws IOException, XmlPullParserException {
|
||||
InputStream in = openHttpConnection(baseUrl + "/cancel?uniqueid=" + uniqueId);
|
||||
InputStream in = openHttpConnection(baseUrl + "/cancel?uniqueid=" + uniqueId, false);
|
||||
String cancel = getXmlString(in, "cancel");
|
||||
return Integer.parseInt(cancel) != 0;
|
||||
}
|
||||
|
@ -235,11 +235,14 @@ public class PairingManager {
|
||||
byte[] saltAndPin = saltPin(salt, pin);
|
||||
aesKey = generateAesKey(saltAndPin);
|
||||
|
||||
// Send the salt and get the server cert
|
||||
// Send the salt and get the server cert. This doesn't have a read timeout
|
||||
// because the user must enter the PIN before the server responds
|
||||
String getCert = http.openHttpConnectionToString(http.baseUrl +
|
||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&phrase=getservercert&salt="+bytesToHex(salt)+"&clientcert="+bytesToHex(pemCertBytes));
|
||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&phrase=getservercert&salt="+
|
||||
bytesToHex(salt)+"&clientcert="+bytesToHex(pemCertBytes),
|
||||
false);
|
||||
if (!NvHTTP.getXmlString(getCert, "paired").equals("1")) {
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId);
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
||||
return PairState.FAILED;
|
||||
}
|
||||
X509Certificate serverCert = extractPlainCert(getCert);
|
||||
@ -250,9 +253,10 @@ public class PairingManager {
|
||||
|
||||
// Send the encrypted challenge to the server
|
||||
String challengeResp = http.openHttpConnectionToString(http.baseUrl +
|
||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&clientchallenge="+bytesToHex(encryptedChallenge));
|
||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&clientchallenge="+bytesToHex(encryptedChallenge),
|
||||
true);
|
||||
if (!NvHTTP.getXmlString(challengeResp, "paired").equals("1")) {
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId);
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
||||
return PairState.FAILED;
|
||||
}
|
||||
|
||||
@ -268,9 +272,10 @@ public class PairingManager {
|
||||
byte[] challengeRespHash = toSHA1Bytes(concatBytes(concatBytes(serverChallenge, cert.getSignature()), clientSecret));
|
||||
byte[] challengeRespEncrypted = encryptAes(challengeRespHash, aesKey);
|
||||
String secretResp = http.openHttpConnectionToString(http.baseUrl +
|
||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&serverchallengeresp="+bytesToHex(challengeRespEncrypted));
|
||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&serverchallengeresp="+bytesToHex(challengeRespEncrypted),
|
||||
true);
|
||||
if (!NvHTTP.getXmlString(secretResp, "paired").equals("1")) {
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId);
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
||||
return PairState.FAILED;
|
||||
}
|
||||
|
||||
@ -282,7 +287,7 @@ public class PairingManager {
|
||||
// Ensure the authenticity of the data
|
||||
if (!verifySignature(serverSecret, serverSignature, serverCert)) {
|
||||
// Cancel the pairing process
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId);
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
||||
|
||||
// Looks like a MITM
|
||||
return PairState.FAILED;
|
||||
@ -292,7 +297,7 @@ public class PairingManager {
|
||||
byte[] serverChallengeRespHash = toSHA1Bytes(concatBytes(concatBytes(randomChallenge, serverCert.getSignature()), serverSecret));
|
||||
if (!Arrays.equals(serverChallengeRespHash, serverResponse)) {
|
||||
// Cancel the pairing process
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId);
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
||||
|
||||
// Probably got the wrong PIN
|
||||
return PairState.PIN_WRONG;
|
||||
@ -301,16 +306,18 @@ public class PairingManager {
|
||||
// Send the server our signed secret
|
||||
byte[] clientPairingSecret = concatBytes(clientSecret, signData(clientSecret, pk));
|
||||
String clientSecretResp = http.openHttpConnectionToString(http.baseUrl +
|
||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&clientpairingsecret="+bytesToHex(clientPairingSecret));
|
||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&clientpairingsecret="+bytesToHex(clientPairingSecret),
|
||||
true);
|
||||
if (!NvHTTP.getXmlString(clientSecretResp, "paired").equals("1")) {
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId);
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
||||
return PairState.FAILED;
|
||||
}
|
||||
|
||||
// Do the initial challenge (seems neccessary for us to show as paired)
|
||||
String pairChallenge = http.openHttpConnectionToString(http.baseUrl + "/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&phrase=pairchallenge");
|
||||
String pairChallenge = http.openHttpConnectionToString(http.baseUrl +
|
||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&phrase=pairchallenge", true);
|
||||
if (!NvHTTP.getXmlString(pairChallenge, "paired").equals("1")) {
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId);
|
||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
||||
return PairState.FAILED;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user