Home | History | Annotate | Download | only in hotspot2
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.wifi.hotspot2;
     18 
     19 import android.text.TextUtils;
     20 
     21 import com.android.server.wifi.hotspot2.Utils;
     22 
     23 import java.util.HashMap;
     24 import java.util.Iterator;
     25 import java.util.List;
     26 import java.util.Map;
     27 
     28 /**
     29  * Utility class for matching domain names.
     30  */
     31 public class DomainMatcher {
     32     public static final int MATCH_NONE = 0;
     33     public static final int MATCH_PRIMARY = 1;
     34     public static final int MATCH_SECONDARY = 2;
     35 
     36     /**
     37      * The root of the Label tree.
     38      */
     39     private final Label mRoot;
     40 
     41     /**
     42      * Label tree representation for the domain name.  Labels are delimited by "." in the domain
     43      * name.
     44      *
     45      * For example, the tree representation of "android.google.com" as a primary domain:
     46      * [com, None] -> [google, None] -> [android, Primary]
     47      *
     48      */
     49     private static class Label {
     50         private final Map<String, Label> mSubDomains;
     51         private int mMatch;
     52 
     53         Label(int match) {
     54             mMatch = match;
     55             mSubDomains = new HashMap<String, Label>();
     56         }
     57 
     58         /**
     59          * Add sub-domains to this label.
     60          *
     61          * @param labels The iterator of domain label strings
     62          * @param match The match status of the domain
     63          */
     64         public void addDomain(Iterator<String> labels, int match) {
     65             String labelName = labels.next();
     66             // Create the Label object if it doesn't exist yet.
     67             Label subLabel = mSubDomains.get(labelName);
     68             if (subLabel == null) {
     69                 subLabel = new Label(MATCH_NONE);
     70                 mSubDomains.put(labelName, subLabel);
     71             }
     72 
     73             if (labels.hasNext()) {
     74                 // Adding sub-domain.
     75                 subLabel.addDomain(labels, match);
     76             } else {
     77                 // End of the domain, update the match status.
     78                 subLabel.mMatch = match;
     79             }
     80         }
     81 
     82         /**
     83          * Return the Label for the give label string.
     84          * @param labelString The label string to look for
     85          * @return {@link Label}
     86          */
     87         public Label getSubLabel(String labelString) {
     88             return mSubDomains.get(labelString);
     89         }
     90 
     91         /**
     92          * Return the match status
     93          *
     94          * @return The match status
     95          */
     96         public int getMatch() {
     97             return mMatch;
     98         }
     99 
    100         private void toString(StringBuilder sb) {
    101             if (mSubDomains != null) {
    102                 sb.append(".{");
    103                 for (Map.Entry<String, Label> entry : mSubDomains.entrySet()) {
    104                     sb.append(entry.getKey());
    105                     entry.getValue().toString(sb);
    106                 }
    107                 sb.append('}');
    108             } else {
    109                 sb.append('=').append(mMatch);
    110             }
    111         }
    112 
    113         @Override
    114         public String toString() {
    115             StringBuilder sb = new StringBuilder();
    116             toString(sb);
    117             return sb.toString();
    118         }
    119     }
    120 
    121     public DomainMatcher(String primaryDomain, List<String> secondaryDomains) {
    122         // Create the root label.
    123         mRoot = new Label(MATCH_NONE);
    124 
    125         // Add secondary domains.
    126         if (secondaryDomains != null) {
    127             for (String domain : secondaryDomains) {
    128                 if (!TextUtils.isEmpty(domain)) {
    129                     List<String> secondaryLabel = Utils.splitDomain(domain);
    130                     mRoot.addDomain(secondaryLabel.iterator(), MATCH_SECONDARY);
    131                 }
    132             }
    133         }
    134 
    135         // Add primary domain, primary overwrites secondary.
    136         if (!TextUtils.isEmpty(primaryDomain)) {
    137             List<String> primaryLabel = Utils.splitDomain(primaryDomain);
    138             mRoot.addDomain(primaryLabel.iterator(), MATCH_PRIMARY);
    139         }
    140     }
    141 
    142     /**
    143      * Check if domain is either the same or a sub-domain of any of the domains in the
    144      * domain tree in this matcher, i.e. all or a sub-set of the labels in domain matches
    145      * a path in the tree.
    146      *
    147      * This will have precedence for matching primary domain over secondary domain if both
    148      * are found.
    149      *
    150      * For example, with primary domain set to "test.google.com" and secondary domain set to
    151      * "google.com":
    152      * "test2.test.google.com" -> Match.Primary
    153      * "test1.google.com" -> Match.Secondary
    154      *
    155      * @param domainName Domain name to be checked.
    156      * @return The match status
    157      */
    158     public int isSubDomain(String domainName) {
    159         if (TextUtils.isEmpty(domainName)) {
    160             return MATCH_NONE;
    161         }
    162         List<String> domainLabels = Utils.splitDomain(domainName);
    163 
    164         Label label = mRoot;
    165         int match = MATCH_NONE;
    166         for (String labelString : domainLabels) {
    167             label = label.getSubLabel(labelString);
    168             if (label == null) {
    169                 break;
    170             } else if (label.getMatch() != MATCH_NONE) {
    171                 match = label.getMatch();
    172                 if (match == MATCH_PRIMARY) {
    173                     break;
    174                 }
    175             }
    176         }
    177         return match;
    178     }
    179 
    180     /**
    181      * Check if domain2 is a sub-domain of domain1.
    182      *
    183      * @param domain1 The string of the first domain
    184      * @param domain2 The string of the second domain
    185      * @return true if the second domain is the sub-domain of the first
    186      */
    187     public static boolean arg2SubdomainOfArg1(String domain1, String domain2) {
    188         if (TextUtils.isEmpty(domain1) || TextUtils.isEmpty(domain2)) {
    189             return false;
    190         }
    191 
    192         List<String> labels1 = Utils.splitDomain(domain1);
    193         List<String> labels2 = Utils.splitDomain(domain2);
    194 
    195         // domain2 must be the same or longer than domain1 in order to be a sub-domain.
    196         if (labels2.size() < labels1.size()) {
    197             return false;
    198         }
    199 
    200         Iterator<String> l1 = labels1.iterator();
    201         Iterator<String> l2 = labels2.iterator();
    202 
    203         while(l1.hasNext()) {
    204             if (!TextUtils.equals(l1.next(), l2.next())) {
    205                 return false;
    206             }
    207         }
    208         return true;
    209     }
    210 
    211     @Override
    212     public String toString() {
    213         return "Domain matcher " + mRoot;
    214     }
    215 }
    216