blob: ce60c55b0118a0688d15843669484252ebf765ce [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi.hotspot2;
import android.text.TextUtils;
import com.android.server.wifi.hotspot2.Utils;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Utility class for matching domain names.
*/
public class DomainMatcher {
public static final int MATCH_NONE = 0;
public static final int MATCH_PRIMARY = 1;
public static final int MATCH_SECONDARY = 2;
/**
* The root of the Label tree.
*/
private final Label mRoot;
/**
* Label tree representation for the domain name. Labels are delimited by "." in the domain
* name.
*
* For example, the tree representation of "android.google.com" as a primary domain:
* [com, None] -> [google, None] -> [android, Primary]
*
*/
private static class Label {
private final Map<String, Label> mSubDomains;
private int mMatch;
Label(int match) {
mMatch = match;
mSubDomains = new HashMap<String, Label>();
}
/**
* Add sub-domains to this label.
*
* @param labels The iterator of domain label strings
* @param match The match status of the domain
*/
public void addDomain(Iterator<String> labels, int match) {
String labelName = labels.next();
// Create the Label object if it doesn't exist yet.
Label subLabel = mSubDomains.get(labelName);
if (subLabel == null) {
subLabel = new Label(MATCH_NONE);
mSubDomains.put(labelName, subLabel);
}
if (labels.hasNext()) {
// Adding sub-domain.
subLabel.addDomain(labels, match);
} else {
// End of the domain, update the match status.
subLabel.mMatch = match;
}
}
/**
* Return the Label for the give label string.
* @param labelString The label string to look for
* @return {@link Label}
*/
public Label getSubLabel(String labelString) {
return mSubDomains.get(labelString);
}
/**
* Return the match status
*
* @return The match status
*/
public int getMatch() {
return mMatch;
}
private void toString(StringBuilder sb) {
if (mSubDomains != null) {
sb.append(".{");
for (Map.Entry<String, Label> entry : mSubDomains.entrySet()) {
sb.append(entry.getKey());
entry.getValue().toString(sb);
}
sb.append('}');
} else {
sb.append('=').append(mMatch);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
toString(sb);
return sb.toString();
}
}
public DomainMatcher(String primaryDomain, List<String> secondaryDomains) {
// Create the root label.
mRoot = new Label(MATCH_NONE);
// Add secondary domains.
if (secondaryDomains != null) {
for (String domain : secondaryDomains) {
if (!TextUtils.isEmpty(domain)) {
List<String> secondaryLabel = Utils.splitDomain(domain);
mRoot.addDomain(secondaryLabel.iterator(), MATCH_SECONDARY);
}
}
}
// Add primary domain, primary overwrites secondary.
if (!TextUtils.isEmpty(primaryDomain)) {
List<String> primaryLabel = Utils.splitDomain(primaryDomain);
mRoot.addDomain(primaryLabel.iterator(), MATCH_PRIMARY);
}
}
/**
* Check if domain is either the same or a sub-domain of any of the domains in the
* domain tree in this matcher, i.e. all or a sub-set of the labels in domain matches
* a path in the tree.
*
* This will have precedence for matching primary domain over secondary domain if both
* are found.
*
* For example, with primary domain set to "test.google.com" and secondary domain set to
* "google.com":
* "test2.test.google.com" -> Match.Primary
* "test1.google.com" -> Match.Secondary
*
* @param domainName Domain name to be checked.
* @return The match status
*/
public int isSubDomain(String domainName) {
if (TextUtils.isEmpty(domainName)) {
return MATCH_NONE;
}
List<String> domainLabels = Utils.splitDomain(domainName);
Label label = mRoot;
int match = MATCH_NONE;
for (String labelString : domainLabels) {
label = label.getSubLabel(labelString);
if (label == null) {
break;
} else if (label.getMatch() != MATCH_NONE) {
match = label.getMatch();
if (match == MATCH_PRIMARY) {
break;
}
}
}
return match;
}
/**
* Check if domain2 is a sub-domain of domain1.
*
* @param domain1 The string of the first domain
* @param domain2 The string of the second domain
* @return true if the second domain is the sub-domain of the first
*/
public static boolean arg2SubdomainOfArg1(String domain1, String domain2) {
if (TextUtils.isEmpty(domain1) || TextUtils.isEmpty(domain2)) {
return false;
}
List<String> labels1 = Utils.splitDomain(domain1);
List<String> labels2 = Utils.splitDomain(domain2);
// domain2 must be the same or longer than domain1 in order to be a sub-domain.
if (labels2.size() < labels1.size()) {
return false;
}
Iterator<String> l1 = labels1.iterator();
Iterator<String> l2 = labels2.iterator();
while(l1.hasNext()) {
if (!TextUtils.equals(l1.next(), l2.next())) {
return false;
}
}
return true;
}
@Override
public String toString() {
return "Domain matcher " + mRoot;
}
}