mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-19 19:13:03 +00:00
Update for GFE 2.4.5.54 support. The HTTPS /serverinfo query is now only available to paired clients. As a result, we catch the cert validation error and failover to HTTP. It's ugly but I don't see another way to do it.
This commit is contained in:
parent
7d150e7e89
commit
13ec16c606
@ -47,13 +47,15 @@ public class NvHTTP {
|
|||||||
private PairingManager pm;
|
private PairingManager pm;
|
||||||
private InetAddress address;
|
private InetAddress address;
|
||||||
|
|
||||||
public static final int PORT = 47984;
|
public static final int HTTPS_PORT = 47984;
|
||||||
|
public static final int HTTP_PORT = 47989;
|
||||||
public static final int CONNECTION_TIMEOUT = 3000;
|
public static final int CONNECTION_TIMEOUT = 3000;
|
||||||
public static final int READ_TIMEOUT = 5000;
|
public static final int READ_TIMEOUT = 5000;
|
||||||
|
|
||||||
private static boolean verbose = false;
|
private static boolean verbose = false;
|
||||||
|
|
||||||
public String baseUrl;
|
public String baseUrlHttps;
|
||||||
|
public String baseUrlHttp;
|
||||||
|
|
||||||
private OkHttpClient httpClient = new OkHttpClient();
|
private OkHttpClient httpClient = new OkHttpClient();
|
||||||
private OkHttpClient httpClientWithReadTimeout;
|
private OkHttpClient httpClientWithReadTimeout;
|
||||||
@ -115,7 +117,8 @@ public class NvHTTP {
|
|||||||
|
|
||||||
initializeHttpState(cryptoProvider);
|
initializeHttpState(cryptoProvider);
|
||||||
|
|
||||||
this.baseUrl = "https://" + safeAddress + ":" + PORT;
|
this.baseUrlHttps = "https://" + safeAddress + ":" + HTTPS_PORT;
|
||||||
|
this.baseUrlHttp = "http://" + safeAddress + ":" + HTTP_PORT;
|
||||||
this.pm = new PairingManager(this, cryptoProvider);
|
this.pm = new PairingManager(this, cryptoProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,8 +169,25 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getServerInfo(String uniqueId) throws MalformedURLException, IOException {
|
public String getServerInfo(String uniqueId) throws MalformedURLException, IOException, XmlPullParserException {
|
||||||
return openHttpConnectionToString(baseUrl + "/serverinfo?uniqueid=" + uniqueId, true);
|
String resp;
|
||||||
|
try {
|
||||||
|
resp = openHttpConnectionToString(baseUrlHttps + "/serverinfo?uniqueid=" + uniqueId, true);
|
||||||
|
|
||||||
|
// This will throw an exception if the request came back with a failure status.
|
||||||
|
// We want this because it will throw us into the HTTP case if the client is unpaired.
|
||||||
|
getServerVersion(resp);
|
||||||
|
}
|
||||||
|
catch (GfeHttpResponseException e) {
|
||||||
|
if (e.getErrorCode() == 401) {
|
||||||
|
// Cert validation error - fall back to HTTP
|
||||||
|
return openHttpConnectionToString(baseUrlHttp + "/serverinfo", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's not a cert validation error, throw it
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ComputerDetails getComputerDetails() throws MalformedURLException, IOException, XmlPullParserException {
|
public ComputerDetails getComputerDetails() throws MalformedURLException, IOException, XmlPullParserException {
|
||||||
@ -411,7 +431,7 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getAppListRaw() throws MalformedURLException, IOException {
|
public String getAppListRaw() throws MalformedURLException, IOException {
|
||||||
return openHttpConnectionToString(baseUrl + "/applist?uniqueid=" + uniqueId, true);
|
return openHttpConnectionToString(baseUrlHttps + "/applist?uniqueid=" + uniqueId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LinkedList<NvApp> getAppList() throws GfeHttpResponseException, IOException, XmlPullParserException {
|
public LinkedList<NvApp> getAppList() throws GfeHttpResponseException, IOException, XmlPullParserException {
|
||||||
@ -420,7 +440,7 @@ public class NvHTTP {
|
|||||||
return getAppListByReader(new StringReader(getAppListRaw()));
|
return getAppListByReader(new StringReader(getAppListRaw()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ResponseBody resp = openHttpConnection(baseUrl + "/applist?uniqueid=" + uniqueId, true);
|
ResponseBody resp = openHttpConnection(baseUrlHttps + "/applist?uniqueid=" + uniqueId, true);
|
||||||
LinkedList<NvApp> appList = getAppListByReader(new InputStreamReader(resp.byteStream()));
|
LinkedList<NvApp> appList = getAppListByReader(new InputStreamReader(resp.byteStream()));
|
||||||
resp.close();
|
resp.close();
|
||||||
return appList;
|
return appList;
|
||||||
@ -428,11 +448,11 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void unpair() throws IOException {
|
public void unpair() throws IOException {
|
||||||
openHttpConnectionToString(baseUrl + "/unpair?uniqueid=" + uniqueId, true);
|
openHttpConnectionToString(baseUrlHttps + "/unpair?uniqueid=" + uniqueId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getBoxArt(NvApp app) throws IOException {
|
public InputStream getBoxArt(NvApp app) throws IOException {
|
||||||
ResponseBody resp = openHttpConnection(baseUrl + "/appasset?uniqueid=" + uniqueId +
|
ResponseBody resp = openHttpConnection(baseUrlHttps + "/appasset?uniqueid=" + uniqueId +
|
||||||
"&appid=" + app.getAppId() + "&AssetType=2&AssetIdx=0", true);
|
"&appid=" + app.getAppId() + "&AssetType=2&AssetIdx=0", true);
|
||||||
return resp.byteStream();
|
return resp.byteStream();
|
||||||
}
|
}
|
||||||
@ -449,7 +469,7 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int launchApp(ConnectionContext context, int appId) throws IOException, XmlPullParserException {
|
public int launchApp(ConnectionContext context, int appId) throws IOException, XmlPullParserException {
|
||||||
String xmlStr = openHttpConnectionToString(baseUrl +
|
String xmlStr = openHttpConnectionToString(baseUrlHttps +
|
||||||
"/launch?uniqueid=" + uniqueId +
|
"/launch?uniqueid=" + uniqueId +
|
||||||
"&appid=" + appId +
|
"&appid=" + appId +
|
||||||
"&mode=" + context.streamConfig.getWidth() + "x" + context.streamConfig.getHeight() + "x" + context.streamConfig.getRefreshRate() +
|
"&mode=" + context.streamConfig.getWidth() + "x" + context.streamConfig.getHeight() + "x" + context.streamConfig.getRefreshRate() +
|
||||||
@ -462,7 +482,7 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean resumeApp(ConnectionContext context) throws IOException, XmlPullParserException {
|
public boolean resumeApp(ConnectionContext context) throws IOException, XmlPullParserException {
|
||||||
String xmlStr = openHttpConnectionToString(baseUrl + "/resume?uniqueid=" + uniqueId +
|
String xmlStr = openHttpConnectionToString(baseUrlHttps + "/resume?uniqueid=" + uniqueId +
|
||||||
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
|
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
|
||||||
"&rikeyid="+context.riKeyId, false);
|
"&rikeyid="+context.riKeyId, false);
|
||||||
String resume = getXmlString(xmlStr, "resume");
|
String resume = getXmlString(xmlStr, "resume");
|
||||||
@ -470,7 +490,7 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean quitApp() throws IOException, XmlPullParserException {
|
public boolean quitApp() throws IOException, XmlPullParserException {
|
||||||
String xmlStr = openHttpConnectionToString(baseUrl + "/cancel?uniqueid=" + uniqueId, false);
|
String xmlStr = openHttpConnectionToString(baseUrlHttps + "/cancel?uniqueid=" + uniqueId, false);
|
||||||
String cancel = getXmlString(xmlStr, "cancel");
|
String cancel = getXmlString(xmlStr, "cancel");
|
||||||
return Integer.parseInt(cancel) != 0;
|
return Integer.parseInt(cancel) != 0;
|
||||||
}
|
}
|
||||||
|
@ -176,12 +176,12 @@ public class PairingManager {
|
|||||||
|
|
||||||
// Send the salt and get the server cert. This doesn't have a read timeout
|
// 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
|
// because the user must enter the PIN before the server responds
|
||||||
String getCert = http.openHttpConnectionToString(http.baseUrl +
|
String getCert = http.openHttpConnectionToString(http.baseUrlHttps +
|
||||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&phrase=getservercert&salt="+
|
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&phrase=getservercert&salt="+
|
||||||
bytesToHex(salt)+"&clientcert="+bytesToHex(pemCertBytes),
|
bytesToHex(salt)+"&clientcert="+bytesToHex(pemCertBytes),
|
||||||
false);
|
false);
|
||||||
if (!NvHTTP.getXmlString(getCert, "paired").equals("1")) {
|
if (!NvHTTP.getXmlString(getCert, "paired").equals("1")) {
|
||||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
http.openHttpConnectionToString(http.baseUrlHttps + "/unpair?uniqueid="+uniqueId, true);
|
||||||
return PairState.FAILED;
|
return PairState.FAILED;
|
||||||
}
|
}
|
||||||
X509Certificate serverCert = extractPlainCert(getCert);
|
X509Certificate serverCert = extractPlainCert(getCert);
|
||||||
@ -191,11 +191,11 @@ public class PairingManager {
|
|||||||
byte[] encryptedChallenge = encryptAes(randomChallenge, aesKey);
|
byte[] encryptedChallenge = encryptAes(randomChallenge, aesKey);
|
||||||
|
|
||||||
// Send the encrypted challenge to the server
|
// Send the encrypted challenge to the server
|
||||||
String challengeResp = http.openHttpConnectionToString(http.baseUrl +
|
String challengeResp = http.openHttpConnectionToString(http.baseUrlHttps +
|
||||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&clientchallenge="+bytesToHex(encryptedChallenge),
|
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&clientchallenge="+bytesToHex(encryptedChallenge),
|
||||||
true);
|
true);
|
||||||
if (!NvHTTP.getXmlString(challengeResp, "paired").equals("1")) {
|
if (!NvHTTP.getXmlString(challengeResp, "paired").equals("1")) {
|
||||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
http.openHttpConnectionToString(http.baseUrlHttps + "/unpair?uniqueid="+uniqueId, true);
|
||||||
return PairState.FAILED;
|
return PairState.FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,11 +210,11 @@ public class PairingManager {
|
|||||||
byte[] clientSecret = generateRandomBytes(16);
|
byte[] clientSecret = generateRandomBytes(16);
|
||||||
byte[] challengeRespHash = toSHA1Bytes(concatBytes(concatBytes(serverChallenge, cert.getSignature()), clientSecret));
|
byte[] challengeRespHash = toSHA1Bytes(concatBytes(concatBytes(serverChallenge, cert.getSignature()), clientSecret));
|
||||||
byte[] challengeRespEncrypted = encryptAes(challengeRespHash, aesKey);
|
byte[] challengeRespEncrypted = encryptAes(challengeRespHash, aesKey);
|
||||||
String secretResp = http.openHttpConnectionToString(http.baseUrl +
|
String secretResp = http.openHttpConnectionToString(http.baseUrlHttps +
|
||||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&serverchallengeresp="+bytesToHex(challengeRespEncrypted),
|
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&serverchallengeresp="+bytesToHex(challengeRespEncrypted),
|
||||||
true);
|
true);
|
||||||
if (!NvHTTP.getXmlString(secretResp, "paired").equals("1")) {
|
if (!NvHTTP.getXmlString(secretResp, "paired").equals("1")) {
|
||||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
http.openHttpConnectionToString(http.baseUrlHttps + "/unpair?uniqueid="+uniqueId, true);
|
||||||
return PairState.FAILED;
|
return PairState.FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +226,7 @@ public class PairingManager {
|
|||||||
// Ensure the authenticity of the data
|
// Ensure the authenticity of the data
|
||||||
if (!verifySignature(serverSecret, serverSignature, serverCert)) {
|
if (!verifySignature(serverSecret, serverSignature, serverCert)) {
|
||||||
// Cancel the pairing process
|
// Cancel the pairing process
|
||||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
http.openHttpConnectionToString(http.baseUrlHttps + "/unpair?uniqueid="+uniqueId, true);
|
||||||
|
|
||||||
// Looks like a MITM
|
// Looks like a MITM
|
||||||
return PairState.FAILED;
|
return PairState.FAILED;
|
||||||
@ -236,7 +236,7 @@ public class PairingManager {
|
|||||||
byte[] serverChallengeRespHash = toSHA1Bytes(concatBytes(concatBytes(randomChallenge, serverCert.getSignature()), serverSecret));
|
byte[] serverChallengeRespHash = toSHA1Bytes(concatBytes(concatBytes(randomChallenge, serverCert.getSignature()), serverSecret));
|
||||||
if (!Arrays.equals(serverChallengeRespHash, serverResponse)) {
|
if (!Arrays.equals(serverChallengeRespHash, serverResponse)) {
|
||||||
// Cancel the pairing process
|
// Cancel the pairing process
|
||||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
http.openHttpConnectionToString(http.baseUrlHttps + "/unpair?uniqueid="+uniqueId, true);
|
||||||
|
|
||||||
// Probably got the wrong PIN
|
// Probably got the wrong PIN
|
||||||
return PairState.PIN_WRONG;
|
return PairState.PIN_WRONG;
|
||||||
@ -244,19 +244,19 @@ public class PairingManager {
|
|||||||
|
|
||||||
// Send the server our signed secret
|
// Send the server our signed secret
|
||||||
byte[] clientPairingSecret = concatBytes(clientSecret, signData(clientSecret, pk));
|
byte[] clientPairingSecret = concatBytes(clientSecret, signData(clientSecret, pk));
|
||||||
String clientSecretResp = http.openHttpConnectionToString(http.baseUrl +
|
String clientSecretResp = http.openHttpConnectionToString(http.baseUrlHttps +
|
||||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&clientpairingsecret="+bytesToHex(clientPairingSecret),
|
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&clientpairingsecret="+bytesToHex(clientPairingSecret),
|
||||||
true);
|
true);
|
||||||
if (!NvHTTP.getXmlString(clientSecretResp, "paired").equals("1")) {
|
if (!NvHTTP.getXmlString(clientSecretResp, "paired").equals("1")) {
|
||||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
http.openHttpConnectionToString(http.baseUrlHttps + "/unpair?uniqueid="+uniqueId, true);
|
||||||
return PairState.FAILED;
|
return PairState.FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the initial challenge (seems neccessary for us to show as paired)
|
// Do the initial challenge (seems neccessary for us to show as paired)
|
||||||
String pairChallenge = http.openHttpConnectionToString(http.baseUrl +
|
String pairChallenge = http.openHttpConnectionToString(http.baseUrlHttps +
|
||||||
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&phrase=pairchallenge", true);
|
"/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&phrase=pairchallenge", true);
|
||||||
if (!NvHTTP.getXmlString(pairChallenge, "paired").equals("1")) {
|
if (!NvHTTP.getXmlString(pairChallenge, "paired").equals("1")) {
|
||||||
http.openHttpConnectionToString(http.baseUrl + "/unpair?uniqueid="+uniqueId, true);
|
http.openHttpConnectionToString(http.baseUrlHttps + "/unpair?uniqueid="+uniqueId, true);
|
||||||
return PairState.FAILED;
|
return PairState.FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user