1 /* 2 * Copyright (C) 2021 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.connectivity.mdns; 18 19 import android.annotation.Nullable; 20 21 import com.android.net.module.util.DnsUtils; 22 23 import java.io.IOException; 24 import java.util.Arrays; 25 import java.util.Locale; 26 import java.util.Objects; 27 28 /** An mDNS "SRV" record, which contains service information. */ 29 public class MdnsServiceRecord extends MdnsRecord { 30 public static final int PROTO_NONE = 0; 31 public static final int PROTO_TCP = 1; 32 public static final int PROTO_UDP = 2; 33 private static final String PROTO_TOKEN_TCP = "_tcp"; 34 private static final String PROTO_TOKEN_UDP = "_udp"; 35 private int servicePriority; 36 private int serviceWeight; 37 private int servicePort; 38 private String[] serviceHost; 39 MdnsServiceRecord(String[] name, MdnsPacketReader reader)40 public MdnsServiceRecord(String[] name, MdnsPacketReader reader) throws IOException { 41 this(name, reader, false); 42 } 43 MdnsServiceRecord(String[] name, MdnsPacketReader reader, boolean isQuestion)44 public MdnsServiceRecord(String[] name, MdnsPacketReader reader, boolean isQuestion) 45 throws IOException { 46 super(name, TYPE_SRV, reader, isQuestion); 47 } 48 MdnsServiceRecord(String[] name, boolean isUnicast)49 public MdnsServiceRecord(String[] name, boolean isUnicast) { 50 super(name, TYPE_SRV, 51 MdnsConstants.QCLASS_INTERNET | (isUnicast ? MdnsConstants.QCLASS_UNICAST : 0), 52 0L /* receiptTimeMillis */, false /* cacheFlush */, 0L /* ttlMillis */); 53 } 54 MdnsServiceRecord(String[] name, long receiptTimeMillis, boolean cacheFlush, long ttlMillis, int servicePriority, int serviceWeight, int servicePort, String[] serviceHost)55 public MdnsServiceRecord(String[] name, long receiptTimeMillis, boolean cacheFlush, 56 long ttlMillis, int servicePriority, int serviceWeight, int servicePort, 57 String[] serviceHost) { 58 super(name, TYPE_SRV, MdnsConstants.QCLASS_INTERNET, receiptTimeMillis, cacheFlush, 59 ttlMillis); 60 this.servicePriority = servicePriority; 61 this.serviceWeight = serviceWeight; 62 this.servicePort = servicePort; 63 this.serviceHost = serviceHost; 64 } 65 66 /** Returns the service's port number. */ getServicePort()67 public int getServicePort() { 68 return servicePort; 69 } 70 71 /** Returns the service's host name. */ getServiceHost()72 public String[] getServiceHost() { 73 return serviceHost; 74 } 75 76 /** Returns the service's priority. */ getServicePriority()77 public int getServicePriority() { 78 return servicePriority; 79 } 80 81 /** Returns the service's weight. */ getServiceWeight()82 public int getServiceWeight() { 83 return serviceWeight; 84 } 85 86 // Format of name is <instance-name>.<service-name>.<protocol>.<domain> 87 88 /** Returns the service's instance name, which uniquely identifies the service instance. */ getServiceInstanceName()89 public String getServiceInstanceName() { 90 if (name.length < 1) { 91 return null; 92 } 93 return name[0]; 94 } 95 96 /** Returns the service's name. */ getServiceName()97 public String getServiceName() { 98 if (name.length < 2) { 99 return null; 100 } 101 return name[1]; 102 } 103 104 /** Returns the service's protocol. */ getServiceProtocol()105 public int getServiceProtocol() { 106 if (name.length < 3) { 107 return PROTO_NONE; 108 } 109 110 String protocol = name[2]; 111 if (protocol.equals(PROTO_TOKEN_TCP)) { 112 return PROTO_TCP; 113 } 114 if (protocol.equals(PROTO_TOKEN_UDP)) { 115 return PROTO_UDP; 116 } 117 return PROTO_NONE; 118 } 119 120 @Override readData(MdnsPacketReader reader)121 protected void readData(MdnsPacketReader reader) throws IOException { 122 servicePriority = reader.readUInt16(); 123 serviceWeight = reader.readUInt16(); 124 servicePort = reader.readUInt16(); 125 serviceHost = reader.readLabels(); 126 } 127 128 @Override writeData(MdnsPacketWriter writer)129 protected void writeData(MdnsPacketWriter writer) throws IOException { 130 writer.writeUInt16(servicePriority); 131 writer.writeUInt16(serviceWeight); 132 writer.writeUInt16(servicePort); 133 writer.writeLabels(serviceHost); 134 } 135 136 @Override toString()137 public String toString() { 138 return String.format( 139 Locale.ROOT, 140 "SRV: %s:%d (prio=%d, weight=%d)", 141 labelsToString(serviceHost), 142 servicePort, 143 servicePriority, 144 serviceWeight); 145 } 146 147 @Override hashCode()148 public int hashCode() { 149 return (super.hashCode() * 31) 150 + Objects.hash(servicePriority, serviceWeight, 151 Arrays.hashCode(DnsUtils.toDnsLabelsUpperCase(serviceHost)), 152 servicePort); 153 } 154 155 @Override equals(@ullable Object other)156 public boolean equals(@Nullable Object other) { 157 if (this == other) { 158 return true; 159 } 160 if (!(other instanceof MdnsServiceRecord)) { 161 return false; 162 } 163 MdnsServiceRecord otherRecord = (MdnsServiceRecord) other; 164 165 return super.equals(other) 166 && (servicePriority == otherRecord.servicePriority) 167 && (serviceWeight == otherRecord.serviceWeight) 168 && DnsUtils.equalsDnsLabelIgnoreDnsCase(serviceHost, otherRecord.serviceHost) 169 && (servicePort == otherRecord.servicePort); 170 } 171 } 172