1 /*
   2  * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.nio.ch;
  27 
  28 import java.net.InetAddress;
  29 import java.net.NetworkInterface;
  30 import java.io.IOException;
  31 import java.nio.channels.MembershipKey;
  32 import java.nio.channels.MulticastChannel;
  33 import java.util.HashSet;
  34 
  35 /**
  36  * MembershipKey implementation.
  37  */
  38 
  39 class MembershipKeyImpl
  40     extends MembershipKey
  41 {
  42     private final MulticastChannel ch;
  43     private final InetAddress group;
  44     private final NetworkInterface interf;
  45     private final InetAddress source;
  46 
  47     private volatile boolean invalid;
  48 
  49     // lock used when creating or accessing blockedSet
  50     private final Object stateLock = new Object();
  51 
  52     // set of source addresses that are blocked
  53     private HashSet<InetAddress> blockedSet;
  54 
  55     private MembershipKeyImpl(MulticastChannel ch,
  56                               InetAddress group,
  57                               NetworkInterface interf,
  58                               InetAddress source)
  59     {
  60         this.ch = ch;
  61         this.group = group;
  62         this.interf = interf;
  63         this.source = source;
  64     }
  65 
  66     int interfaceIndex() {
  67         return interf.getIndex();
  68     }
  69 
  70     /**
  71      * MembershipKey will additional context for IPv4 membership
  72      */
  73     static class Type4 extends MembershipKeyImpl {
  74         private final int groupAddress;
  75         private final int interfAddress;
  76         private final int sourceAddress;
  77 
  78         Type4(MulticastChannel ch,
  79               InetAddress group,
  80               NetworkInterface interf,
  81               InetAddress source,
  82               int groupAddress,
  83               int interfAddress,
  84               int sourceAddress)
  85         {
  86             super(ch, group, interf, source);
  87             this.groupAddress = groupAddress;
  88             this.interfAddress = interfAddress;
  89             this.sourceAddress = sourceAddress;
  90         }
  91 
  92         int groupAddress() {
  93             return groupAddress;
  94         }
  95 
  96         int interfaceAddress() {
  97             return interfAddress;
  98         }
  99 
 100         int source() {
 101             return sourceAddress;
 102         }
 103     }
 104 
 105     /**
 106      * MembershipKey will additional context for IPv6 membership
 107      */
 108     static class Type6 extends MembershipKeyImpl {
 109         private final byte[] groupAddress;
 110         private final byte[] sourceAddress;
 111 
 112         Type6(MulticastChannel ch,
 113               InetAddress group,
 114               NetworkInterface interf,
 115               InetAddress source,
 116               byte[] groupAddress,
 117               byte[] sourceAddress)
 118         {
 119             super(ch, group, interf, source);
 120             this.groupAddress = groupAddress;
 121             this.sourceAddress = sourceAddress;
 122         }
 123 
 124         byte[] groupAddress() {
 125             return groupAddress;
 126         }
 127 
 128         byte[] source() {
 129             return sourceAddress;
 130         }
 131     }
 132 
 133     public boolean isValid() {
 134         return !invalid;
 135     }
 136 
 137     // package-private
 138     void invalidate() {
 139         invalid = true;
 140     }
 141 
 142     public void drop() {
 143         // delegate to channel
 144         ((DatagramChannelImpl)ch).drop(this);
 145     }
 146 
 147     @Override
 148     public MulticastChannel channel() {
 149         return ch;
 150     }
 151 
 152     @Override
 153     public InetAddress group() {
 154         return group;
 155     }
 156 
 157     @Override
 158     public NetworkInterface networkInterface() {
 159         return interf;
 160     }
 161 
 162     @Override
 163     public InetAddress sourceAddress() {
 164         return source;
 165     }
 166 
 167     @Override
 168     public MembershipKey block(InetAddress toBlock)
 169         throws IOException
 170     {
 171         if (source != null)
 172             throw new IllegalStateException("key is source-specific");
 173 
 174         synchronized (stateLock) {
 175             if ((blockedSet != null) && blockedSet.contains(toBlock)) {
 176                 // already blocked, nothing to do
 177                 return this;
 178             }
 179 
 180             ((DatagramChannelImpl)ch).block(this, toBlock);
 181 
 182             // created blocked set if required and add source address
 183             if (blockedSet == null)
 184                 blockedSet = new HashSet<>();
 185             blockedSet.add(toBlock);
 186         }
 187         return this;
 188     }
 189 
 190     @Override
 191     public MembershipKey unblock(InetAddress toUnblock) {
 192         synchronized (stateLock) {
 193             if ((blockedSet == null) || !blockedSet.contains(toUnblock))
 194                 throw new IllegalStateException("not blocked");
 195 
 196             ((DatagramChannelImpl)ch).unblock(this, toUnblock);
 197 
 198             blockedSet.remove(toUnblock);
 199         }
 200         return this;
 201     }
 202 
 203     @Override
 204     public String toString() {
 205         StringBuilder sb = new StringBuilder(64);
 206         sb.append('<');
 207         sb.append(group.getHostAddress());
 208         sb.append(',');
 209         sb.append(interf.getName());
 210         if (source != null) {
 211             sb.append(',');
 212             sb.append(source.getHostAddress());
 213         }
 214         sb.append('>');
 215         return sb.toString();
 216     }
 217 }