Loading core/java/android/net/nsd/NsdServiceInfo.java +96 −2 Original line number Diff line number Diff line Loading @@ -16,8 +16,11 @@ package android.net.nsd; import android.annotation.NonNull; import android.os.Parcelable; import android.os.Parcel; import android.text.TextUtils; import android.util.Base64; import android.util.Log; import android.util.ArrayMap; Loading Loading @@ -95,8 +98,99 @@ public final class NsdServiceInfo implements Parcelable { mPort = p; } /** * Unpack txt information from a base-64 encoded byte array. * * @param rawRecords The raw base64 encoded records string read from netd. * * @hide */ public void setTxtRecords(@NonNull String rawRecords) { byte[] txtRecordsRawBytes = Base64.decode(rawRecords, Base64.DEFAULT); // There can be multiple TXT records after each other. Each record has to following format: // // byte type required meaning // ------------------- ------------------- -------- ---------------------------------- // 0 unsigned 8 bit yes size of record excluding this byte // 1 - n ASCII but not '=' yes key // n + 1 '=' optional separator of key and value // n + 2 - record size uninterpreted bytes optional value // // Example legal records: // [11, 'm', 'y', 'k', 'e', 'y', '=', 0x0, 0x4, 0x65, 0x7, 0xff] // [17, 'm', 'y', 'K', 'e', 'y', 'W', 'i', 't', 'h', 'N', 'o', 'V', 'a', 'l', 'u', 'e', '='] // [12, 'm', 'y', 'B', 'o', 'o', 'l', 'e', 'a', 'n', 'K', 'e', 'y'] // // Example corrupted records // [3, =, 1, 2] <- key is empty // [3, 0, =, 2] <- key contains non-ASCII character. We handle this by replacing the // invalid characters instead of skipping the record. // [30, 'a', =, 2] <- length exceeds total left over bytes in the TXT records array, we // handle this by reducing the length of the record as needed. int pos = 0; while (pos < txtRecordsRawBytes.length) { // recordLen is an unsigned 8 bit value int recordLen = txtRecordsRawBytes[pos] & 0xff; pos += 1; try { if (recordLen == 0) { throw new IllegalArgumentException("Zero sized txt record"); } else if (pos + recordLen > txtRecordsRawBytes.length) { Log.w(TAG, "Corrupt record length (pos = " + pos + "): " + recordLen); recordLen = txtRecordsRawBytes.length - pos; } // Decode key-value records String key = null; byte[] value = null; int valueLen = 0; for (int i = pos; i < pos + recordLen; i++) { if (key == null) { if (txtRecordsRawBytes[i] == '=') { key = new String(txtRecordsRawBytes, pos, i - pos, StandardCharsets.US_ASCII); } } else { if (value == null) { value = new byte[recordLen - key.length() - 1]; } value[valueLen] = txtRecordsRawBytes[i]; valueLen++; } } // If '=' was not found we have a boolean record if (key == null) { key = new String(txtRecordsRawBytes, pos, recordLen, StandardCharsets.US_ASCII); } if (TextUtils.isEmpty(key)) { // Empty keys are not allowed (RFC6763 6.4) throw new IllegalArgumentException("Invalid txt record (key is empty)"); } if (getAttributes().containsKey(key)) { // When we have a duplicate record, the later ones are ignored (RFC6763 6.4) throw new IllegalArgumentException("Invalid txt record (duplicate key \"" + key + "\")"); } setAttribute(key, value); } catch (IllegalArgumentException e) { Log.e(TAG, "While parsing txt records (pos = " + pos + "): " + e.getMessage()); } pos += recordLen; } } /** @hide */ public void setAttribute(String key, byte[] value) { if (TextUtils.isEmpty(key)) { throw new IllegalArgumentException("Key cannot be empty"); } // Key must be printable US-ASCII, excluding =. for (int i = 0; i < key.length(); ++i) { char character = key.charAt(i); Loading Loading @@ -177,10 +271,10 @@ public final class NsdServiceInfo implements Parcelable { } /** @hide */ public byte[] getTxtRecord() { public @NonNull byte[] getTxtRecord() { int txtRecordSize = getTxtRecordSize(); if (txtRecordSize == 0) { return null; return new byte[]{}; } byte[] txtRecord = new byte[txtRecordSize]; Loading services/core/java/com/android/server/NsdService.java +5 −17 Original line number Diff line number Diff line Loading @@ -30,16 +30,14 @@ import android.os.Message; import android.os.Messenger; import android.os.UserHandle; import android.provider.Settings; import android.util.Base64; import android.util.Slog; import android.util.SparseArray; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.concurrent.CountDownLatch; import com.android.internal.util.AsyncChannel; Loading Loading @@ -492,6 +490,7 @@ public class NsdService extends INsdManager.Stub { clientInfo.mResolvedService.setServiceName(name); clientInfo.mResolvedService.setServiceType(type); clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4])); clientInfo.mResolvedService.setTxtRecords(cooked[6]); stopResolveService(id); removeRequestMap(clientId, id, clientInfo); Loading Loading @@ -708,20 +707,9 @@ public class NsdService extends INsdManager.Stub { if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service); try { Command cmd = new Command("mdnssd", "register", regId, service.getServiceName(), service.getServiceType(), service.getPort()); // Add TXT records as additional arguments. Map<String, byte[]> txtRecords = service.getAttributes(); for (String key : txtRecords.keySet()) { try { // TODO: Send encoded TXT record as bytes once NDC/netd supports binary data. byte[] recordValue = txtRecords.get(key); cmd.appendArg(String.format(Locale.US, "%s=%s", key, recordValue != null ? new String(recordValue, "UTF_8") : "")); } catch (UnsupportedEncodingException e) { Slog.e(TAG, "Failed to encode txtRecord " + e); } } service.getServiceType(), service.getPort(), Base64.encodeToString(service.getTxtRecord(), Base64.DEFAULT) .replace("\n", "")); mNativeConnector.execute(cmd); } catch(NativeDaemonConnectorException e) { Loading Loading
core/java/android/net/nsd/NsdServiceInfo.java +96 −2 Original line number Diff line number Diff line Loading @@ -16,8 +16,11 @@ package android.net.nsd; import android.annotation.NonNull; import android.os.Parcelable; import android.os.Parcel; import android.text.TextUtils; import android.util.Base64; import android.util.Log; import android.util.ArrayMap; Loading Loading @@ -95,8 +98,99 @@ public final class NsdServiceInfo implements Parcelable { mPort = p; } /** * Unpack txt information from a base-64 encoded byte array. * * @param rawRecords The raw base64 encoded records string read from netd. * * @hide */ public void setTxtRecords(@NonNull String rawRecords) { byte[] txtRecordsRawBytes = Base64.decode(rawRecords, Base64.DEFAULT); // There can be multiple TXT records after each other. Each record has to following format: // // byte type required meaning // ------------------- ------------------- -------- ---------------------------------- // 0 unsigned 8 bit yes size of record excluding this byte // 1 - n ASCII but not '=' yes key // n + 1 '=' optional separator of key and value // n + 2 - record size uninterpreted bytes optional value // // Example legal records: // [11, 'm', 'y', 'k', 'e', 'y', '=', 0x0, 0x4, 0x65, 0x7, 0xff] // [17, 'm', 'y', 'K', 'e', 'y', 'W', 'i', 't', 'h', 'N', 'o', 'V', 'a', 'l', 'u', 'e', '='] // [12, 'm', 'y', 'B', 'o', 'o', 'l', 'e', 'a', 'n', 'K', 'e', 'y'] // // Example corrupted records // [3, =, 1, 2] <- key is empty // [3, 0, =, 2] <- key contains non-ASCII character. We handle this by replacing the // invalid characters instead of skipping the record. // [30, 'a', =, 2] <- length exceeds total left over bytes in the TXT records array, we // handle this by reducing the length of the record as needed. int pos = 0; while (pos < txtRecordsRawBytes.length) { // recordLen is an unsigned 8 bit value int recordLen = txtRecordsRawBytes[pos] & 0xff; pos += 1; try { if (recordLen == 0) { throw new IllegalArgumentException("Zero sized txt record"); } else if (pos + recordLen > txtRecordsRawBytes.length) { Log.w(TAG, "Corrupt record length (pos = " + pos + "): " + recordLen); recordLen = txtRecordsRawBytes.length - pos; } // Decode key-value records String key = null; byte[] value = null; int valueLen = 0; for (int i = pos; i < pos + recordLen; i++) { if (key == null) { if (txtRecordsRawBytes[i] == '=') { key = new String(txtRecordsRawBytes, pos, i - pos, StandardCharsets.US_ASCII); } } else { if (value == null) { value = new byte[recordLen - key.length() - 1]; } value[valueLen] = txtRecordsRawBytes[i]; valueLen++; } } // If '=' was not found we have a boolean record if (key == null) { key = new String(txtRecordsRawBytes, pos, recordLen, StandardCharsets.US_ASCII); } if (TextUtils.isEmpty(key)) { // Empty keys are not allowed (RFC6763 6.4) throw new IllegalArgumentException("Invalid txt record (key is empty)"); } if (getAttributes().containsKey(key)) { // When we have a duplicate record, the later ones are ignored (RFC6763 6.4) throw new IllegalArgumentException("Invalid txt record (duplicate key \"" + key + "\")"); } setAttribute(key, value); } catch (IllegalArgumentException e) { Log.e(TAG, "While parsing txt records (pos = " + pos + "): " + e.getMessage()); } pos += recordLen; } } /** @hide */ public void setAttribute(String key, byte[] value) { if (TextUtils.isEmpty(key)) { throw new IllegalArgumentException("Key cannot be empty"); } // Key must be printable US-ASCII, excluding =. for (int i = 0; i < key.length(); ++i) { char character = key.charAt(i); Loading Loading @@ -177,10 +271,10 @@ public final class NsdServiceInfo implements Parcelable { } /** @hide */ public byte[] getTxtRecord() { public @NonNull byte[] getTxtRecord() { int txtRecordSize = getTxtRecordSize(); if (txtRecordSize == 0) { return null; return new byte[]{}; } byte[] txtRecord = new byte[txtRecordSize]; Loading
services/core/java/com/android/server/NsdService.java +5 −17 Original line number Diff line number Diff line Loading @@ -30,16 +30,14 @@ import android.os.Message; import android.os.Messenger; import android.os.UserHandle; import android.provider.Settings; import android.util.Base64; import android.util.Slog; import android.util.SparseArray; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.concurrent.CountDownLatch; import com.android.internal.util.AsyncChannel; Loading Loading @@ -492,6 +490,7 @@ public class NsdService extends INsdManager.Stub { clientInfo.mResolvedService.setServiceName(name); clientInfo.mResolvedService.setServiceType(type); clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4])); clientInfo.mResolvedService.setTxtRecords(cooked[6]); stopResolveService(id); removeRequestMap(clientId, id, clientInfo); Loading Loading @@ -708,20 +707,9 @@ public class NsdService extends INsdManager.Stub { if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service); try { Command cmd = new Command("mdnssd", "register", regId, service.getServiceName(), service.getServiceType(), service.getPort()); // Add TXT records as additional arguments. Map<String, byte[]> txtRecords = service.getAttributes(); for (String key : txtRecords.keySet()) { try { // TODO: Send encoded TXT record as bytes once NDC/netd supports binary data. byte[] recordValue = txtRecords.get(key); cmd.appendArg(String.format(Locale.US, "%s=%s", key, recordValue != null ? new String(recordValue, "UTF_8") : "")); } catch (UnsupportedEncodingException e) { Slog.e(TAG, "Failed to encode txtRecord " + e); } } service.getServiceType(), service.getPort(), Base64.encodeToString(service.getTxtRecord(), Base64.DEFAULT) .replace("\n", "")); mNativeConnector.execute(cmd); } catch(NativeDaemonConnectorException e) { Loading