mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-18 18:42:46 +00:00
Finished the rewrite of mDNS
This commit is contained in:
parent
7038384d36
commit
82aa5730bd
@ -5,7 +5,7 @@ import java.util.Locale;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class NvComputer {
|
public class NvComputer {
|
||||||
private String mDNSResponse;
|
private String hostname;
|
||||||
private InetAddress ipAddress;
|
private InetAddress ipAddress;
|
||||||
private String ipAddressString;
|
private String ipAddressString;
|
||||||
private int state;
|
private int state;
|
||||||
@ -14,15 +14,15 @@ public class NvComputer {
|
|||||||
private String mac;
|
private String mac;
|
||||||
private UUID uniqueID;
|
private UUID uniqueID;
|
||||||
|
|
||||||
|
|
||||||
private int sessionID;
|
private int sessionID;
|
||||||
private boolean paired;
|
private boolean paired;
|
||||||
private boolean isBusy;
|
private boolean isBusy;
|
||||||
|
|
||||||
|
public NvComputer(String hostname, InetAddress ipAddress, int state, int numOfApps, String gpuType, String mac, UUID uniqueID) {
|
||||||
public NvComputer(String mDNSResponse, InetAddress ipAddress, String ipAddressString, int state, int numOfApps, String gpuType, String mac, UUID uniqueID) {
|
this.hostname = hostname;
|
||||||
this.mDNSResponse = mDNSResponse;
|
|
||||||
this.ipAddress = ipAddress;
|
this.ipAddress = ipAddress;
|
||||||
this.ipAddressString = ipAddressString;
|
this.ipAddressString = this.ipAddress.getHostAddress();
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.numOfApps = numOfApps;
|
this.numOfApps = numOfApps;
|
||||||
this.gpuType = gpuType;
|
this.gpuType = gpuType;
|
||||||
@ -30,8 +30,8 @@ public class NvComputer {
|
|||||||
this.uniqueID = uniqueID;
|
this.uniqueID = uniqueID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getmDNSResponse() {
|
public String getHostname() {
|
||||||
return this.mDNSResponse;
|
return this.hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InetAddress getIpAddress() {
|
public InetAddress getIpAddress() {
|
||||||
@ -42,10 +42,6 @@ public class NvComputer {
|
|||||||
return this.ipAddressString;
|
return this.ipAddressString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getIPAddressString() {
|
|
||||||
return this.ipAddress.getCanonicalHostName().toLowerCase(Locale.getDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getState() {
|
public int getState() {
|
||||||
return this.state;
|
return this.state;
|
||||||
}
|
}
|
||||||
@ -97,8 +93,8 @@ public class NvComputer {
|
|||||||
StringBuilder returnStringBuilder = new StringBuilder();
|
StringBuilder returnStringBuilder = new StringBuilder();
|
||||||
returnStringBuilder.append("NvComputer 0x");
|
returnStringBuilder.append("NvComputer 0x");
|
||||||
returnStringBuilder.append(Integer.toHexString(this.hashCode()).toUpperCase(Locale.getDefault()));
|
returnStringBuilder.append(Integer.toHexString(this.hashCode()).toUpperCase(Locale.getDefault()));
|
||||||
returnStringBuilder.append("\n|- mDNS Hostname: ");
|
returnStringBuilder.append("\n|- Hostname: ");
|
||||||
returnStringBuilder.append(this.mDNSResponse);
|
returnStringBuilder.append(this.hostname);
|
||||||
returnStringBuilder.append("\n|- IP Address: ");
|
returnStringBuilder.append("\n|- IP Address: ");
|
||||||
returnStringBuilder.append(this.ipAddress.toString());
|
returnStringBuilder.append(this.ipAddress.toString());
|
||||||
returnStringBuilder.append("\n|- Computer State: ");
|
returnStringBuilder.append("\n|- Computer State: ");
|
||||||
|
@ -33,8 +33,8 @@ public class NvmDNS extends AsyncTask<Void, Integer, Void> {
|
|||||||
public static InetAddress MDNS_MULTICAST_ADDRESS;
|
public static InetAddress MDNS_MULTICAST_ADDRESS;
|
||||||
public static final short MDNS_PORT = 5353;
|
public static final short MDNS_PORT = 5353;
|
||||||
|
|
||||||
public static final int WAIT_MS = 5000;
|
public static final int WAIT_MS = 100;
|
||||||
|
|
||||||
private HashSet<NvComputer> responses;
|
private HashSet<NvComputer> responses;
|
||||||
private MulticastSocket socket;
|
private MulticastSocket socket;
|
||||||
|
|
||||||
@ -45,10 +45,10 @@ public class NvmDNS extends AsyncTask<Void, Integer, Void> {
|
|||||||
MDNS_MULTICAST_ADDRESS = null;
|
MDNS_MULTICAST_ADDRESS = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public NvmDNS() {
|
public NvmDNS() {
|
||||||
this.responses = new HashSet<NvComputer>();
|
this.responses = new HashSet<NvComputer>();
|
||||||
|
|
||||||
// Create our Socket Connection
|
// Create our Socket Connection
|
||||||
try {
|
try {
|
||||||
this.socket = new MulticastSocket(NvmDNS.MDNS_PORT);
|
this.socket = new MulticastSocket(NvmDNS.MDNS_PORT);
|
||||||
@ -64,10 +64,10 @@ public class NvmDNS extends AsyncTask<Void, Integer, Void> {
|
|||||||
public Set<NvComputer> getComputers() {
|
public Set<NvComputer> getComputers() {
|
||||||
return Collections.unmodifiableSet(this.responses);
|
return Collections.unmodifiableSet(this.responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendQuery() {
|
private void sendQuery() {
|
||||||
Header queryHeader = new Header();
|
Header queryHeader = new Header();
|
||||||
|
|
||||||
// If we set the RA (Recursion Available) flag and our message ID to 0
|
// If we set the RA (Recursion Available) flag and our message ID to 0
|
||||||
// then the packet matches the real mDNS query packet as displayed in Wireshark
|
// then the packet matches the real mDNS query packet as displayed in Wireshark
|
||||||
queryHeader.setFlag(org.xbill.DNS.Flags.RA);
|
queryHeader.setFlag(org.xbill.DNS.Flags.RA);
|
||||||
@ -90,8 +90,8 @@ public class NvmDNS extends AsyncTask<Void, Integer, Void> {
|
|||||||
|
|
||||||
// Convert the message into Network Byte Order
|
// Convert the message into Network Byte Order
|
||||||
byte[] wireQuery = query.toWire();
|
byte[] wireQuery = query.toWire();
|
||||||
Log.v("NvmDNS Query", query.toString());
|
Log.i("NvmDNS Query", query.toString());
|
||||||
|
|
||||||
// Convert our byte array into a Packet
|
// Convert our byte array into a Packet
|
||||||
DatagramPacket transmitPacket = new DatagramPacket(wireQuery, wireQuery.length);
|
DatagramPacket transmitPacket = new DatagramPacket(wireQuery, wireQuery.length);
|
||||||
transmitPacket.setAddress(NvmDNS.MDNS_MULTICAST_ADDRESS);
|
transmitPacket.setAddress(NvmDNS.MDNS_MULTICAST_ADDRESS);
|
||||||
@ -99,9 +99,9 @@ public class NvmDNS extends AsyncTask<Void, Integer, Void> {
|
|||||||
|
|
||||||
// And (attempt) to send the packet
|
// And (attempt) to send the packet
|
||||||
try {
|
try {
|
||||||
Log.v("NvmDNS Query", "Blocking on this.nvstream_socket.send(transmitPacket)");
|
Log.d("NvmDNS Query", "Blocking on this.nvstream_socket.send(transmitPacket)");
|
||||||
this.socket.send(transmitPacket);
|
this.socket.send(transmitPacket);
|
||||||
Log.v("NvmDNS Query", "Passed this.nvstream_socket.send(transmitPacket)");
|
Log.d("NvmDNS Query", "Passed this.nvstream_socket.send(transmitPacket)");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e("NvmDNS Query", "There was an error sending the DNS query.");
|
Log.e("NvmDNS Query", "There was an error sending the DNS query.");
|
||||||
Log.e("NvmDNS Query", e.getMessage());
|
Log.e("NvmDNS Query", e.getMessage());
|
||||||
@ -110,11 +110,11 @@ public class NvmDNS extends AsyncTask<Void, Integer, Void> {
|
|||||||
|
|
||||||
public void waitForResponses() {
|
public void waitForResponses() {
|
||||||
Log.v("NvmDNS Response", "mDNS Loop Started");
|
Log.v("NvmDNS Response", "mDNS Loop Started");
|
||||||
|
|
||||||
// We support up to 1500 byte packets
|
// We support up to 1500 byte packets
|
||||||
byte[] data = new byte[1500];
|
byte[] data = new byte[1500];
|
||||||
DatagramPacket packet = new DatagramPacket(data, data.length);
|
DatagramPacket packet = new DatagramPacket(data, data.length);
|
||||||
|
|
||||||
Message message = null;
|
Message message = null;
|
||||||
|
|
||||||
while (!this.socket.isClosed()) {
|
while (!this.socket.isClosed()) {
|
||||||
@ -124,6 +124,7 @@ public class NvmDNS extends AsyncTask<Void, Integer, Void> {
|
|||||||
this.socket.receive(packet);
|
this.socket.receive(packet);
|
||||||
Log.d("NvmDNS Response", "Blocking passed on this.nvstream_query_socket.recieve()");
|
Log.d("NvmDNS Response", "Blocking passed on this.nvstream_query_socket.recieve()");
|
||||||
message = new Message(packet.getData());
|
message = new Message(packet.getData());
|
||||||
|
this.parseRecord(message, packet.getAddress());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (this.socket.isClosed()) {
|
if (this.socket.isClosed()) {
|
||||||
Log.e("NvmDNS Response", "The socket was closed on us. The timer must have been reached.");
|
Log.e("NvmDNS Response", "The socket was closed on us. The timer must have been reached.");
|
||||||
@ -134,78 +135,115 @@ public class NvmDNS extends AsyncTask<Void, Integer, Void> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// We really only care about the ADDITIONAL section (specifically the text records)
|
}
|
||||||
Record[] responses = message.getSectionArray(Section.ADDITIONAL);
|
|
||||||
|
private void parseRecord(Message message, InetAddress address) {
|
||||||
// We only want to process records that actually have a length, have an ANSWER
|
// We really only care about the ADDITIONAL section (specifically the text records)
|
||||||
// section that has stuff in it and that the ANSWER to our query is what we sent
|
Record[] responses = message.getSectionArray(Section.ADDITIONAL);
|
||||||
if (responses.length != 0 &&
|
// We only want to process records that actually have a length, have an ANSWER
|
||||||
|
// section that has stuff in it and that the ANSWER to our query is what we sent
|
||||||
|
if (responses.length != 0 &&
|
||||||
message.getSectionArray(Section.ANSWER).length != 0 &&
|
message.getSectionArray(Section.ANSWER).length != 0 &&
|
||||||
message.getSectionArray(Section.ANSWER)[0].getName().toString().equals(NvmDNS.MDNS_QUERY)) {
|
message.getSectionArray(Section.ANSWER)[0].getName().toString().equals(NvmDNS.MDNS_QUERY)) {
|
||||||
|
|
||||||
Log.v("NvmDNS Response", "Got a packet from " + packet.getAddress().getCanonicalHostName());
|
|
||||||
Log.v("NvmDNS Response", "Question: " + message.getSectionArray(Section.ANSWER)[0].getName().toString());
|
|
||||||
Log.v("NvmDNS Response", "Response: " + responses[0].getName().toString());
|
|
||||||
|
|
||||||
// The DNS library we are using does not use inferred generics :(
|
Log.v("NvmDNS Response", "Got a packet from " + address.getCanonicalHostName());
|
||||||
|
Log.v("NvmDNS Response", "Question: " + message.getSectionArray(Section.ANSWER)[0].getName().toString());
|
||||||
TXTRecord txtRecord = null;
|
Log.v("NvmDNS Response", "Response: " + responses[0].getName().toString());
|
||||||
|
|
||||||
|
|
||||||
for (Record record : responses) {
|
|
||||||
Log.v("NvmDNS Response", "We recieved a DNS repsonse with a " + record.getClass().getName() + " record.");
|
|
||||||
if (record instanceof TXTRecord) {
|
|
||||||
txtRecord = (TXTRecord)record;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (txtRecord == null) {
|
|
||||||
Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse with no TXTRecord");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
ArrayList<?> txtRecordStringList = new ArrayList<Object>(txtRecord.getStrings());
|
|
||||||
|
|
||||||
if (txtRecordStringList.size() != 5) {
|
|
||||||
Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse with the improper amount of TXTRecord Entries.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The general format of the text records is:
|
|
||||||
// SERVICE_STATE=1
|
|
||||||
// SERVICE_NUMOFAPPS=5
|
|
||||||
// SERVICE_GPUTYPE=GeForce GTX 760 x2
|
|
||||||
// SERVICE_MAC=DE:AD:BE:EF:CA:FE
|
|
||||||
// SERVICE_UNIQUEID={A Wild UUID Appeared!}
|
|
||||||
// Every single record I've seen so far has been in this format
|
|
||||||
try {
|
|
||||||
int serviceState = Integer.parseInt(txtRecordStringList.get(0).toString().split("=")[1]);
|
|
||||||
int numberOfApps = Integer.parseInt(txtRecordStringList.get(1).toString().split("=")[1]);
|
|
||||||
String gpuType = txtRecordStringList.get(2).toString().split("=")[1];
|
|
||||||
String mac = txtRecordStringList.get(3).toString().split("=")[1];
|
|
||||||
UUID uniqueID = UUID.fromString(txtRecordStringList.get(4).toString().split("=")[1]);
|
|
||||||
|
|
||||||
// We need to resolve the hostname in this thread so that we can use it in the GUI
|
// TODO: The DNS entry we get is "XENITH._nvstream._tcp.local."
|
||||||
packet.getAddress().getCanonicalHostName();
|
// And the .'s in there are not actually periods. Or something.
|
||||||
|
String hostname = responses[0].getName().toString();
|
||||||
NvComputer computer = new NvComputer(responses[0].getName().toString(), packet.getAddress(), packet.getAddress().getCanonicalHostName(), serviceState, numberOfApps, gpuType, mac, uniqueID);
|
|
||||||
this.responses.add(computer);
|
// The records can be returned in any order, so we need to figure out which one is the TXTRecord
|
||||||
} catch (ArrayIndexOutOfBoundsException e) {
|
// We get three records back: A TXTRecord, a SRVRecord and an ARecord
|
||||||
Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse.");
|
TXTRecord txtRecord = null;
|
||||||
|
|
||||||
|
for (Record record : responses) {
|
||||||
|
Log.v("NvmDNS Response", "We recieved a DNS repsonse with a " + record.getClass().getName() + " record.");
|
||||||
|
if (record instanceof TXTRecord) {
|
||||||
|
txtRecord = (TXTRecord)record;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (txtRecord == null) {
|
||||||
|
Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse with no TXTRecord");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parseTXTRecord(txtRecord, address, hostname);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void parseTXTRecord(TXTRecord txtRecord, InetAddress address, String hostname) {
|
||||||
|
// The DNS library we are using does not use inferred generics :(
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
ArrayList<String> txtRecordStringList = new ArrayList<String>(txtRecord.getStrings());
|
||||||
|
|
||||||
|
if (txtRecordStringList.size() != 5) {
|
||||||
|
Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse with the improper amount of TXTRecord Entries.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The general format of the text records is:
|
||||||
|
// SERVICE_STATE=1
|
||||||
|
// SERVICE_NUMOFAPPS=5
|
||||||
|
// SERVICE_GPUTYPE=GeForce GTX 760 x2
|
||||||
|
// SERVICE_MAC=DE:AD:BE:EF:CA:FE
|
||||||
|
// SERVICE_UNIQUEID={A Wild UUID Appeared!}
|
||||||
|
// Every single record I've seen so far has been in this format
|
||||||
|
try {
|
||||||
|
int serviceState = Integer.parseInt(this.parseTXTRecordField(txtRecordStringList.get(0), "SERVICE_STATE"));
|
||||||
|
int numberOfApps = Integer.parseInt(this.parseTXTRecordField(txtRecordStringList.get(1), "SERVICE_NUMOFAPPS"));
|
||||||
|
String gpuType = this.parseTXTRecordField(txtRecordStringList.get(2), "SERVICE_GPUTYPE");
|
||||||
|
String mac = this.parseTXTRecordField(txtRecordStringList.get(3), "SERVICE_MAC");
|
||||||
|
UUID uniqueID = UUID.fromString(this.parseTXTRecordField(txtRecordStringList.get(4), "SERVICE_UNIQUEID"));
|
||||||
|
|
||||||
|
// We need to resolve the hostname in this thread so that we can use it in the GUI
|
||||||
|
address.getCanonicalHostName();
|
||||||
|
|
||||||
|
NvComputer computer = new NvComputer(hostname, address, serviceState, numberOfApps, gpuType, mac, uniqueID);
|
||||||
|
this.responses.add(computer);
|
||||||
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
|
Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String parseTXTRecordField(String field, String key) {
|
||||||
|
// Make sure that our key=value pair actually has our key in it
|
||||||
|
if (!field.contains(key)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that our key=value pair only has one "=" in it.
|
||||||
|
if (field.indexOf('=') != field.lastIndexOf('=')) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] split = field.split("=");
|
||||||
|
|
||||||
|
if (split.length != 2) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return split[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// What follows is an implementation of Android's AsyncTask.
|
||||||
|
// The first step is to send our query, then we start our
|
||||||
|
// RX thread to parse responses. However we only want to accept
|
||||||
|
// responses for a limited amount of time so we start a new thread
|
||||||
|
// to kill the socket after a set amount of time
|
||||||
|
// Then we return control to the foreground thread
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... thisParameterIsUseless) {
|
protected Void doInBackground(Void... thisParameterIsUseless) {
|
||||||
Log.v("NvmDNS ASync", "doInBackground entered");
|
Log.v("NvmDNS ASync", "doInBackground entered");
|
||||||
|
|
||||||
this.sendQuery();
|
this.sendQuery();
|
||||||
|
|
||||||
|
|
||||||
// We want to run our wait thread for an amount of time then close the socket.
|
// We want to run our wait thread for an amount of time then close the socket.
|
||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -222,25 +260,23 @@ public class NvmDNS extends AsyncTask<Void, Integer, Void> {
|
|||||||
Log.v("NvmDNS Wait", "Socket Closed");
|
Log.v("NvmDNS Wait", "Socket Closed");
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.waitForResponses();
|
this.waitForResponses();
|
||||||
|
|
||||||
Log.v("NvmDNS ASync", "doInBackground exit");
|
Log.v("NvmDNS ASync", "doInBackground exit");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onProgressUpdate(Integer... progress) {
|
|
||||||
Log.v("NvmDNS ASync", "onProgressUpdate");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void moreUselessParameters) {
|
protected void onProgressUpdate(Integer... progress) {
|
||||||
Log.v("NvmDNS ASync", "onPostExecute");
|
Log.v("NvmDNS ASync", "onProgressUpdate");
|
||||||
for (NvComputer computer : this.responses) {
|
}
|
||||||
Log.i("NvmDNS NvComputer", computer.toString());
|
|
||||||
}
|
@Override
|
||||||
}
|
protected void onPostExecute(Void moreUselessParameters) {
|
||||||
|
Log.v("NvmDNS ASync", "onPostExecute");
|
||||||
|
for (NvComputer computer : this.responses) {
|
||||||
|
Log.i("NvmDNS NvComputer", computer.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user