javaregexuriguavadiameter-protocol

Way to validate a Diameter URI in Java?


I'm wondering if there are any nice simple ways to validate a Diameter URI (description below) using Java?

Note, a Diameter URI must have one of the forms:

aaa://FQDN[:PORT][;transport=TRANS][;protocol=PROT]
aaas://FQDN[:PORT][;transport=TRANS][;protocol=PROT]

The FQDN (mandatory) has to be replaced with the fully qualified host name (or IP), the PORT (optional, default is 3868) with the port number, TRANS (optional) with the transport protocol (can be TCP or SCTP) and PROT (optional) with diameter.

Some examples of the acceptable forms are:

aaa://server.com
aaa://127.0.0.1
aaa://server.com:1234
aaas://server.com:1234;transport=tcp
aaas://[::1]
aaas://[::1]:1234
aaas://[::1]:1234;transport=tcp;protocol=diameter

Note, as shown above, if using an IPv6 address, the address must be placed in box brackets, whereas the port number (if specified), with its colon separator, should be outside of the brackets.

I think doing this using regular expressions would be quite messy and difficult to understand, and other examples I have seen which don't use regex are just as awkward looking (such as https://code.google.com/p/cipango/source/browse/trunk/cipango-diameter/src/main/java/org/cipango/diameter/util/AAAUri.java?r=763).

So was wondering if there were maybe a nicer way to do this, e.g. something like a URI validator library, which takes some rules (such as those for the Diameter URI above) and then applies them to some input to validate it?

I've had a look at the Google Guava libraries as well to see if there was anything that could help but I couldn't see anything offhand.

Many thanks!


Solution

  • Since the URI class is not sufficient, and in fact will create exceptions for valid Diameter URI's, this is not such a trivial task.

    I think reg.ex. is the way to go here, but due to the complexities, you might be better off if you place it in a helper class. I agree that the code you linked to did not look very good -- you can do better! :)

    Take a look at the following code example, where I've broken down a regEx into its individual parts as a way to "document" what's happening.

    It is not in any ways complete, it was created to conform with your examples. Especially the IP6 type addresses needs work. In addition, you might want to give more information in the validation; like why it failed.

    But at least it's a beginning, and I think it is quite a bit better than the code you linked to. It might seem like an awful lot of code, but most of it is actually print statements and tests... :) In addition, since each part is broken down and kept as field variables, you can create simple getters to access each part (if that is of importance to you).

    import java.net.URISyntaxException;
    import java.util.Arrays;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class DiameterUri {
    
        private String diameterUri;
        private String protocol;
        private String host;
        private String port;
        private String[] params;
    
        public DiameterUri(String diameterUri) throws URISyntaxException {
            this.diameterUri = diameterUri;
            validate();
            System.out.println(diameterUri);
            System.out.println("  protocol=" + protocol);
            System.out.println("  host=" + host);
            System.out.println("  port=" + port);
            System.out.println("  params=" + Arrays.toString(params));
        }
    
        private void validate() throws URISyntaxException {
            String protocol = "(aaa|aaas)://";              // protocol- required
            String ip4 = "[A-Za-z0-9.]+";                   // ip4 address - part of "host"
            String ip6 = "\\[::1\\]";                       // ip6 address - part of "host"
            String host = "(" + ip4 + "|" + ip6 + ")";      // host - required
            String port = "(:\\d+)?";                       // port - optional (one occurrence)
            String params = "((;[a-zA-Z0-9]+=[a-zA-Z0-9]+)*)"; // params - optional (multiple occurrences)
            String regEx = protocol + host + port + params;
            Pattern pattern = Pattern.compile(regEx);
            Matcher matcher = pattern.matcher(diameterUri);
            if (matcher.matches()) {
                this.protocol = matcher.group(1);
                this.host = matcher.group(2);
                this.port = matcher.group(3) == null ? null : matcher.group(3).substring(1);
                String paramsFromUri = matcher.group(4);
                if (paramsFromUri != null && paramsFromUri.length() > 0) {
                    this.params = paramsFromUri.substring(1).split(";");
                } else {
                    this.params = new String[0];
                }
            } else {
                throw new URISyntaxException(diameterUri, "invalid");
            }
        }
    
        public static void main(String[] args) throws URISyntaxException {
            new DiameterUri("aaa://server.com");
            new DiameterUri("aaa://127.0.0.1");
            new DiameterUri("aaa://server.com:1234");
            new DiameterUri("aaas://server.com:1234;transport=tcp");
            new DiameterUri("aaas://[::1]");
            new DiameterUri("aaas://[::1]:1234");
            new DiameterUri("aaas://[::1]:1234;transport=tcp;protocol=diameter");
            try {
                new DiameterUri("127.0.0.1");
                throw new RuntimeException("Expected URISyntaxException");
            } catch (URISyntaxException ignore) {}
        }
    
    }