Menu

Parsing TXT bytes

2010-05-27
2013-01-24
  • trepidacious

    trepidacious - 2010-05-27

    Hi,
    Firstly, thanks for a very useful library!
    Secondly, I noticed when adding TXT records to my service in avahi, that the ServiceInfo.getTextString() doesn't handle the TXT data properly. It assumes everything in the text bytes is a string - actually according to the spec it is a list of strings, with each string having a one byte length. I might just be missing another method for doing this, but I put together some code to parse this format (And also take it a step further to parse out the key/value pairs in the strings). Just in case it's helpful I'll attach below. Please let me know if you would like to use it:

    /**
     * Utility methods for zeroconf
     */
    public class ZeroconfUtils {
    
        private static Charset charset = Charset.forName("US-ASCII");
    
        /**
         * Parse the raw bytes of text data into a map of keys and values
         * @param textBytes     Raw text bytes from {@link ServiceInfo#getTextBytes()}
         * @param strict        If true, invalid strings cause an exception, if false
         *                      they are just ignored.
         * @return              {@link SortedMap} of String to String, sorted by key.
         *                      Note that in the case of duplicate keys, only the one that
         *                      comes last in the text will be present.
         * @throws TextByteDecodeException      If data is not a valid DNS text record containing
         *                                      DNS-SD format key/value pairs
         */
        public static SortedMap<String, String> textBytesToMap(byte[] textBytes, boolean strict) throws TextByteDecodeException {
            List<String> strings = textBytesToStrings(textBytes);
            SortedMap<String, String> map = new TreeMap<String, String>();
            for (String s : strings) {
                int i = s.indexOf("="); 
                if (i >= 0) {
                    String key = s.substring(0, i);
                    String value = "";
                    if (i + 1 < s.length()) {
                        value = s.substring(i+1, s.length());
                    }
                    map.put(key, value);
                } else {
                    if (strict) {
                        throw new TextByteDecodeException("String is not a key/value pair '" + s + "'");
                    }
                }
            }
            return map;
        }
    
        /**
         * Parse the raw bytes of text data into a list of strings
         * @param textBytes     Raw text bytes from {@link ServiceInfo#getTextBytes()}
         * @return              List of strings
         * @throws TextByteDecodeException      If data is not a valid DNS text record
         */
        public static List<String> textBytesToStrings(byte[] textBytes) throws TextByteDecodeException {
            //Format is defined at 
            //http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
            //sections 6.1 and 6.2
    
            CharsetDecoder decoder = charset.newDecoder();
    
            ByteBuffer buffer = ByteBuffer.wrap(textBytes);
            List<String> strings = new ArrayList<String>();
    
            //While we have bytes left, try to read a string length, then string
            while (buffer.hasRemaining()) {
                int count = buffer.get();
                if (buffer.remaining() >= count) {
                    //Read only specified count
                    buffer.limit(buffer.position() + count);
    
                    try {
                        CharBuffer cbuf = decoder.decode(buffer);
                        //Restore limit so we use the rest of the buffer
                        buffer.limit(buffer.capacity());
                        strings.add(cbuf.toString()); 
                    } catch (MalformedInputException mie) {
                        throw new TextByteDecodeException("Can't decode a string", mie);
                    } catch (UnmappableCharacterException uce) {
                        throw new TextByteDecodeException("Can't decode a string", uce);                    
                    } catch (CharacterCodingException cce) {
                        throw new TextByteDecodeException("Can't decode a string", cce);                    
                    }
    
                } else {
                    throw new TextByteDecodeException("String length is " + count + " but only " + buffer.remaining() + " bytes left.");
                }
            }
    
            return strings;
    
        }
    
    }
    
     
  • Pierre Frisch

    Pierre Frisch - 2010-06-10

    The method ServiceInfo.getTextString() has now been deprecated. There is no way to make this method return a correct result. To access TXT info either use getTextByte() that will return the raw information of use the properties. You can enumerate on the property names and get each value either as a string or byte array.

     

Log in to post a comment.