Home | History | Annotate | Download | only in nsd
      1 /*
      2  * Copyright (C) 2012 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 android.net.wifi.p2p.nsd;
     18 
     19 import android.net.wifi.p2p.WifiP2pDevice;
     20 
     21 import java.io.ByteArrayInputStream;
     22 import java.io.DataInputStream;
     23 import java.io.IOException;
     24 import java.util.HashMap;
     25 import java.util.Map;
     26 
     27 /**
     28  * A class for a response of bonjour service discovery.
     29  *
     30  * @hide
     31  */
     32 public class WifiP2pDnsSdServiceResponse extends WifiP2pServiceResponse {
     33 
     34     /**
     35      * DNS query name.
     36      * e.g)
     37      * for PTR
     38      * "_ipp._tcp.local."
     39      * for TXT
     40      * "MyPrinter._ipp._tcp.local."
     41      */
     42     private String mDnsQueryName;
     43 
     44     /**
     45      * Service instance name.
     46      * e.g) "MyPrinter"
     47      * This field is only used when the dns type equals to
     48      * {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_PTR}.
     49      */
     50     private String mInstanceName;
     51 
     52     /**
     53      * DNS Type.
     54      * Should be {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_PTR} or
     55      * {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_TXT}.
     56      */
     57     private int mDnsType;
     58 
     59     /**
     60      * DnsSd version number.
     61      * Should be {@link WifiP2pDnsSdServiceInfo#VERSION_1}.
     62      */
     63     private int mVersion;
     64 
     65     /**
     66      * Txt record.
     67      * This field is only used when the dns type equals to
     68      * {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_TXT}.
     69      */
     70     private final HashMap<String, String> mTxtRecord = new HashMap<String, String>();
     71 
     72     /**
     73      * Virtual memory packet.
     74      * see E.3 of the Wi-Fi Direct technical specification for the detail.<br>
     75      * The spec can be obtained from wi-fi.org
     76      * Key: pointer Value: domain name.<br>
     77      */
     78     private final static Map<Integer, String> sVmpack;
     79 
     80     static {
     81         sVmpack = new HashMap<Integer, String>();
     82         sVmpack.put(0x0c, "_tcp.local.");
     83         sVmpack.put(0x11, "local.");
     84         sVmpack.put(0x1c, "_udp.local.");
     85     }
     86 
     87     /**
     88      * Returns query DNS name.
     89      * @return DNS name.
     90      */
     91     public String getDnsQueryName() {
     92         return mDnsQueryName;
     93     }
     94 
     95     /**
     96      * Return query DNS type.
     97      * @return DNS type.
     98      */
     99     public int getDnsType() {
    100         return mDnsType;
    101     }
    102 
    103     /**
    104      * Return bonjour version number.
    105      * @return version number.
    106      */
    107     public int getVersion() {
    108         return mVersion;
    109     }
    110 
    111     /**
    112      * Return instance name.
    113      * @return
    114      */
    115     public String getInstanceName() {
    116         return mInstanceName;
    117     }
    118 
    119     /**
    120      * Return TXT record data.
    121      * @return TXT record data.
    122      */
    123     public Map<String, String> getTxtRecord() {
    124         return mTxtRecord;
    125     }
    126 
    127     @Override
    128     public String toString() {
    129         StringBuffer sbuf = new StringBuffer();
    130         sbuf.append("serviceType:DnsSd(").append(mServiceType).append(")");
    131         sbuf.append(" status:").append(Status.toString(mStatus));
    132         sbuf.append(" srcAddr:").append(mDevice.deviceAddress);
    133         sbuf.append(" version:").append(String.format("%02x", mVersion));
    134         sbuf.append(" dnsName:").append(mDnsQueryName);
    135         sbuf.append(" TxtRecord:");
    136         for (String key : mTxtRecord.keySet()) {
    137             sbuf.append(" key:").append(key).append(" value:").append(mTxtRecord.get(key));
    138         }
    139         if (mInstanceName != null) {
    140             sbuf.append(" InsName:").append(mInstanceName);
    141         }
    142         return sbuf.toString();
    143     }
    144 
    145     /**
    146      * This is only used in framework.
    147      * @param status status code.
    148      * @param dev source device.
    149      * @param data RDATA.
    150      * @hide
    151      */
    152     protected WifiP2pDnsSdServiceResponse(int status,
    153             int tranId, WifiP2pDevice dev, byte[] data) {
    154         super(WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR,
    155                 status, tranId, dev, data);
    156         if (!parse()) {
    157             throw new IllegalArgumentException("Malformed bonjour service response");
    158         }
    159     }
    160 
    161     /**
    162      * Parse DnsSd service discovery response.
    163      *
    164      * @return {@code true} if the operation succeeded
    165      */
    166     private boolean parse() {
    167         /*
    168          * The data format from Wi-Fi Direct spec is as follows.
    169          * ________________________________________________
    170          * |  encoded and compressed dns name (variable)  |
    171          * ________________________________________________
    172          * |       dnstype(2byte)      |  version(1byte)  |
    173          * ________________________________________________
    174          * |              RDATA (variable)                |
    175          */
    176         if (mData == null) {
    177             // the empty is OK.
    178             return true;
    179         }
    180 
    181         DataInputStream dis = new DataInputStream(new ByteArrayInputStream(mData));
    182 
    183         mDnsQueryName = readDnsName(dis);
    184         if (mDnsQueryName == null) {
    185             return false;
    186         }
    187 
    188         try {
    189             mDnsType = dis.readUnsignedShort();
    190             mVersion = dis.readUnsignedByte();
    191         } catch (IOException e) {
    192             e.printStackTrace();
    193             return false;
    194         }
    195 
    196         if (mDnsType == WifiP2pDnsSdServiceInfo.DNS_TYPE_PTR) {
    197             String rData = readDnsName(dis);
    198             if (rData == null) {
    199                 return false;
    200             }
    201             if (rData.length() <= mDnsQueryName.length()) {
    202                 return false;
    203             }
    204 
    205             mInstanceName = rData.substring(0,
    206                     rData.length() - mDnsQueryName.length() -1);
    207         } else if (mDnsType == WifiP2pDnsSdServiceInfo.DNS_TYPE_TXT) {
    208             return readTxtData(dis);
    209         } else {
    210             return false;
    211         }
    212 
    213         return true;
    214     }
    215 
    216     /**
    217      * Read dns name.
    218      *
    219      * @param dis data input stream.
    220      * @return dns name
    221      */
    222     private String readDnsName(DataInputStream dis) {
    223         StringBuffer sb = new StringBuffer();
    224 
    225         // copy virtual memory packet.
    226         HashMap<Integer, String> vmpack = new HashMap<Integer, String>(sVmpack);
    227         if (mDnsQueryName != null) {
    228             vmpack.put(0x27, mDnsQueryName);
    229         }
    230         try {
    231             while (true) {
    232                 int i = dis.readUnsignedByte();
    233                 if (i == 0x00) {
    234                     return sb.toString();
    235                 } else if (i == 0xc0) {
    236                     // refer to pointer.
    237                     String ref = vmpack.get(dis.readUnsignedByte());
    238                     if (ref == null) {
    239                         //invalid.
    240                         return null;
    241                     }
    242                     sb.append(ref);
    243                     return sb.toString();
    244                 } else {
    245                     byte[] data = new byte[i];
    246                     dis.readFully(data);
    247                     sb.append(new String(data));
    248                     sb.append(".");
    249                 }
    250             }
    251         } catch (IOException e) {
    252             e.printStackTrace();
    253         }
    254         return null;
    255     }
    256 
    257     /**
    258      * Read TXT record data.
    259      *
    260      * @param dis
    261      * @return true if TXT data is valid
    262      */
    263     private boolean readTxtData(DataInputStream dis) {
    264         try {
    265             while (dis.available() > 0) {
    266                 int len = dis.readUnsignedByte();
    267                 if (len == 0) {
    268                     break;
    269                 }
    270                 byte[] data = new byte[len];
    271                 dis.readFully(data);
    272                 String[] keyVal = new String(data).split("=");
    273                 if (keyVal.length != 2) {
    274                     return false;
    275                 }
    276                 mTxtRecord.put(keyVal[0], keyVal[1]);
    277             }
    278             return true;
    279         } catch (IOException e) {
    280             e.printStackTrace();
    281         }
    282         return false;
    283     }
    284 
    285     /**
    286      * Creates DnsSd service response.
    287      *  This is only called from WifiP2pServiceResponse
    288      *
    289      * @param status status code.
    290      * @param dev source device.
    291      * @param data DnsSd response data.
    292      * @return DnsSd service response data.
    293      * @hide
    294      */
    295     static WifiP2pDnsSdServiceResponse newInstance(int status,
    296             int transId, WifiP2pDevice dev, byte[] data) {
    297         if (status != WifiP2pServiceResponse.Status.SUCCESS) {
    298             return new WifiP2pDnsSdServiceResponse(status,
    299                     transId, dev, null);
    300         }
    301         try {
    302             return new WifiP2pDnsSdServiceResponse(status,
    303                     transId, dev, data);
    304         } catch (IllegalArgumentException e) {
    305             e.printStackTrace();
    306         }
    307         return null;
    308     }
    309 }
    310