0

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!

  • 2
    Have you tried simply using the `java.net.URI` constructor, which will throw a URISyntaxException if the URI is not valid? – Steinar Mar 24 '13 at 18:34
  • Yeah, java.net.URI has slightly different rules than my use case. For example, new URI("127.0.0.1") is accepted, but that's not a valid Diameter URI (no scheme part at start) – user1977749 Mar 24 '13 at 19:20
  • Why not parse out the FQDN with a regular expression like: `#aaas?://([^;]+)(;transport=\w+)?(;protocol=\w+)?#` then use subgroup 1 in a new URI constructor to validate it as a FQDN? – FrankieTheKneeMan Mar 24 '13 at 20:21
  • 1
    `java.net.URI` could still help: You just need to check that the scheme is non-null. And that's convenient, since you already want to check that the scheme is "aaa" or "aaas." That said, you'll probably need to fall back to regular expressions eventually: The Diameter URL syntax is an odd one that doesn't fit any of the *four* URL specs I'm aware of, so a standard URL class is unlikely to be able to split it up the way you need. – Chris Povirk Mar 25 '13 at 14:43

1 Answers1

3

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) {}
    }

}
Steinar
  • 5,860
  • 1
  • 25
  • 23
  • Many thanks Steinar, I think something like this might be the best approach (and you are right, it is much cleaner than the code I linked to!) – user1977749 Mar 24 '13 at 21:57