Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/krb5/KrbServiceLocator.java
38830 views
/*1* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.krb5;2627import sun.security.krb5.internal.Krb5;2829import java.security.AccessController;30import java.security.PrivilegedActionException;31import java.security.PrivilegedExceptionAction;32import java.util.Arrays;33import java.util.Hashtable;34import java.util.Random;35import java.util.StringTokenizer;3637import javax.naming.*;38import javax.naming.directory.*;39import javax.naming.spi.NamingManager;4041/**42* This class discovers the location of Kerberos services by querying DNS,43* as defined in RFC 4120.44*45* @author Seema Malkani46* @since 1.747*/4849class KrbServiceLocator {5051private static final String SRV_RR = "SRV";52private static final String[] SRV_RR_ATTR = new String[] {SRV_RR};5354private static final String SRV_TXT = "TXT";55private static final String[] SRV_TXT_ATTR = new String[] {SRV_TXT};5657private static final Random random = new Random();5859private static final boolean DEBUG = Krb5.DEBUG;6061private KrbServiceLocator() {62}6364/**65* Locates the KERBEROS service for a given domain.66* Queries DNS for a list of KERBEROS Service Text Records (TXT) for a67* given domain name.68* Information on the mapping of DNS hostnames and domain names69* to Kerberos realms is stored using DNS TXT records70*71* @param realmName A string realm name.72* @return An ordered list of hostports for the Kerberos service or null if73* the service has not been located.74*/75static String[] getKerberosService(String realmName) {7677// search realm in SRV TXT records78String dnsUrl = "dns:///_kerberos." + realmName;79String[] records = null;80try {81// Create the DNS context using NamingManager rather than using82// the initial context constructor. This avoids having the initial83// context constructor call itself (when processing the URL84// argument in the getAttributes call).85Context ctx = NamingManager.getURLContext("dns", new Hashtable<>(0));86if (!(ctx instanceof DirContext)) {87return null; // cannot create a DNS context88}89Attributes attrs = null;90try {91// both connect and accept are needed since DNS is thru UDP92attrs = AccessController.doPrivileged(93(PrivilegedExceptionAction<Attributes>)94() -> ((DirContext)ctx).getAttributes(95dnsUrl, SRV_TXT_ATTR),96null,97new java.net.SocketPermission("*", "connect,accept"));98} catch (PrivilegedActionException e) {99throw (NamingException)e.getCause();100}101Attribute attr;102103if (attrs != null && ((attr = attrs.get(SRV_TXT)) != null)) {104int numValues = attr.size();105int numRecords = 0;106String[] txtRecords = new String[numValues];107108// gather the text records109int i = 0;110int j = 0;111while (i < numValues) {112try {113txtRecords[j] = (String)attr.get(i);114j++;115} catch (Exception e) {116// ignore bad value117}118i++;119}120numRecords = j;121122// trim123if (numRecords < numValues) {124String[] trimmed = new String[numRecords];125System.arraycopy(txtRecords, 0, trimmed, 0, numRecords);126records = trimmed;127} else {128records = txtRecords;129}130}131} catch (NamingException e) {132// ignore133}134return records;135}136137/**138* Locates the KERBEROS service for a given domain.139* Queries DNS for a list of KERBEROS Service Location Records (SRV) for a140* given domain name.141*142* @param realmName A string realm name.143* @param protocol the protocol string, can be "_udp" or "_tcp"144* @return An ordered list of hostports for the Kerberos service or null if145* the service has not been located.146*/147static String[] getKerberosService(String realmName, String protocol) {148149String dnsUrl = "dns:///_kerberos." + protocol + "." + realmName;150String[] hostports = null;151152try {153// Create the DNS context using NamingManager rather than using154// the initial context constructor. This avoids having the initial155// context constructor call itself (when processing the URL156// argument in the getAttributes call).157Context ctx = NamingManager.getURLContext("dns", new Hashtable<>(0));158if (!(ctx instanceof DirContext)) {159return null; // cannot create a DNS context160}161162Attributes attrs = null;163try {164// both connect and accept are needed since DNS is thru UDP165attrs = AccessController.doPrivileged(166(PrivilegedExceptionAction<Attributes>)167() -> ((DirContext)ctx).getAttributes(168dnsUrl, SRV_RR_ATTR),169null,170new java.net.SocketPermission("*", "connect,accept"));171} catch (PrivilegedActionException e) {172throw (NamingException)e.getCause();173}174175Attribute attr;176177if (attrs != null && ((attr = attrs.get(SRV_RR)) != null)) {178int numValues = attr.size();179int numRecords = 0;180SrvRecord[] srvRecords = new SrvRecord[numValues];181182// create the service records183int i = 0;184int j = 0;185while (i < numValues) {186try {187srvRecords[j] = new SrvRecord((String) attr.get(i));188j++;189} catch (Exception e) {190// ignore bad value191}192i++;193}194numRecords = j;195196// trim197if (numRecords < numValues) {198SrvRecord[] trimmed = new SrvRecord[numRecords];199System.arraycopy(srvRecords, 0, trimmed, 0, numRecords);200srvRecords = trimmed;201}202203// Sort the service records in ascending order of their204// priority value. For records with equal priority, move205// those with weight 0 to the top of the list.206if (numRecords > 1) {207Arrays.sort(srvRecords);208}209210// extract the host and port number from each service record211hostports = extractHostports(srvRecords);212}213} catch (NamingException e) {214// e.printStackTrace();215// ignore216}217return hostports;218}219220/**221* Extract hosts and port numbers from a list of SRV records.222* An array of hostports is returned or null if none were found.223*/224private static String[] extractHostports(SrvRecord[] srvRecords) {225String[] hostports = null;226227int head = 0;228int tail = 0;229int sublistLength = 0;230int k = 0;231for (int i = 0; i < srvRecords.length; i++) {232if (hostports == null) {233hostports = new String[srvRecords.length];234}235// find the head and tail of the list of records having the same236// priority value.237head = i;238while (i < srvRecords.length - 1 &&239srvRecords[i].priority == srvRecords[i + 1].priority) {240i++;241}242tail = i;243244// select hostports from the sublist245sublistLength = (tail - head) + 1;246for (int j = 0; j < sublistLength; j++) {247hostports[k++] = selectHostport(srvRecords, head, tail);248}249}250return hostports;251}252253/*254* Randomly select a service record in the range [head, tail] and return255* its hostport value. Follows the algorithm in RFC 2782.256*/257private static String selectHostport(SrvRecord[] srvRecords, int head,258int tail) {259if (head == tail) {260return srvRecords[head].hostport;261}262263// compute the running sum for records between head and tail264int sum = 0;265for (int i = head; i <= tail; i++) {266if (srvRecords[i] != null) {267sum += srvRecords[i].weight;268srvRecords[i].sum = sum;269}270}271String hostport = null;272273// If all records have zero weight, select first available one;274// otherwise, randomly select a record according to its weight275int target = (sum == 0 ? 0 : random.nextInt(sum + 1));276for (int i = head; i <= tail; i++) {277if (srvRecords[i] != null && srvRecords[i].sum >= target) {278hostport = srvRecords[i].hostport;279srvRecords[i] = null; // make this record unavailable280break;281}282}283return hostport;284}285286/**287* This class holds a DNS service (SRV) record.288* See http://www.ietf.org/rfc/rfc2782.txt289*/290291static class SrvRecord implements Comparable<SrvRecord> {292293int priority;294int weight;295int sum;296String hostport;297298/**299* Creates a service record object from a string record.300* DNS supplies the string record in the following format:301* <pre>302* <Priority> " " <Weight> " " <Port> " " <Host>303* </pre>304*/305SrvRecord(String srvRecord) throws Exception {306StringTokenizer tokenizer = new StringTokenizer(srvRecord, " ");307String port;308309if (tokenizer.countTokens() == 4) {310priority = Integer.parseInt(tokenizer.nextToken());311weight = Integer.parseInt(tokenizer.nextToken());312port = tokenizer.nextToken();313hostport = tokenizer.nextToken() + ":" + port;314} else {315throw new IllegalArgumentException();316}317}318319/*320* Sort records in ascending order of priority value. For records with321* equal priority move those with weight 0 to the top of the list.322*/323public int compareTo(SrvRecord that) {324if (priority > that.priority) {325return 1; // this > that326} else if (priority < that.priority) {327return -1; // this < that328} else if (weight == 0 && that.weight != 0) {329return -1; // this < that330} else if (weight != 0 && that.weight == 0) {331return 1; // this > that332} else {333return 0; // this == that334}335}336}337}338339340