1package com.android.server.wifi.anqp;
2
3import com.android.server.wifi.anqp.eap.EAPMethod;
4import com.android.server.wifi.hotspot2.AuthMatch;
5import com.android.server.wifi.hotspot2.Utils;
6import com.android.server.wifi.hotspot2.pps.Credential;
7import com.android.server.wifi.hotspot2.pps.DomainMatcher;
8
9import java.net.ProtocolException;
10import java.nio.ByteBuffer;
11import java.nio.charset.StandardCharsets;
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.List;
15
16/**
17 * The NAI Realm Data ANQP sub-element, IEEE802.11-2012 section 8.4.4.10 figure 8-418
18 */
19public class NAIRealmData {
20    private final List<String> mRealms;
21    private final List<EAPMethod> mEAPMethods;
22
23    public NAIRealmData(ByteBuffer payload) throws ProtocolException {
24        if (payload.remaining() < 5) {
25            throw new ProtocolException("Runt payload: " + payload.remaining());
26        }
27
28        int length = payload.getShort() & Constants.SHORT_MASK;
29        if (length > payload.remaining()) {
30            throw new ProtocolException("Invalid data length: " + length);
31        }
32        boolean utf8 = (payload.get() & 1) == Constants.UTF8_INDICATOR;
33
34        String realm = Constants.getPrefixedString(payload, 1, utf8 ?
35                StandardCharsets.UTF_8 :
36                StandardCharsets.US_ASCII);
37        String[] realms = realm.split(";");
38        mRealms = new ArrayList<>();
39        for (String realmElement : realms) {
40            if (realmElement.length() > 0) {
41                mRealms.add(realmElement);
42            }
43        }
44
45        int methodCount = payload.get() & Constants.BYTE_MASK;
46        mEAPMethods = new ArrayList<>(methodCount);
47        while (methodCount > 0) {
48            mEAPMethods.add(new EAPMethod(payload));
49            methodCount--;
50        }
51    }
52
53    public List<String> getRealms() {
54        return Collections.unmodifiableList(mRealms);
55    }
56
57    public List<EAPMethod> getEAPMethods() {
58        return Collections.unmodifiableList(mEAPMethods);
59    }
60
61    public int match(List<String> credLabels, Credential credential) {
62        int realmMatch = AuthMatch.None;
63        if (!mRealms.isEmpty()) {
64            for (String realm : mRealms) {
65                List<String> labels = Utils.splitDomain(realm);
66                if (DomainMatcher.arg2SubdomainOfArg1(credLabels, labels)) {
67                    realmMatch = AuthMatch.Realm;
68                    break;
69                }
70            }
71            if (realmMatch == AuthMatch.None || mEAPMethods.isEmpty()) {
72                return realmMatch;
73            }
74            // else there is a realm match and one or more EAP methods - check them.
75        }
76        else if (mEAPMethods.isEmpty()) {
77            return AuthMatch.Indeterminate;
78        }
79
80        int best = AuthMatch.None;
81        for (EAPMethod eapMethod : mEAPMethods) {
82            int match = eapMethod.match(credential) | realmMatch;
83            if (match > best) {
84                best = match;
85                if (best == AuthMatch.Exact) {
86                    return best;
87                }
88            }
89        }
90        return best;
91    }
92
93    @Override
94    public String toString() {
95        StringBuilder sb = new StringBuilder();
96
97        sb.append("  NAI Realm(s)");
98        for (String realm : mRealms) {
99            sb.append(' ').append(realm);
100        }
101        sb.append('\n');
102
103        for (EAPMethod eapMethod : mEAPMethods) {
104            sb.append( "    " ).append(eapMethod.toString());
105        }
106        return sb.toString();
107    }
108}
109