1/*
2 * Copyright (c) 2008, 2009, 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
26package sun.nio.ch;
27
28import java.nio.channels.*;
29import java.net.InetAddress;
30import java.net.NetworkInterface;
31import java.io.IOException;
32import java.util.HashSet;
33
34/**
35 * MembershipKey implementation.
36 */
37
38class MembershipKeyImpl
39    extends MembershipKey
40{
41    private final MulticastChannel ch;
42    private final InetAddress group;
43    private final NetworkInterface interf;
44    private final InetAddress source;
45
46    private volatile boolean invalid;
47
48    // lock used when creating or accessing blockedSet
49    private Object stateLock = new Object();
50
51    // set of source addresses that are blocked
52    private HashSet<InetAddress> blockedSet;
53
54    private MembershipKeyImpl(MulticastChannel ch,
55                              InetAddress group,
56                              NetworkInterface interf,
57                              InetAddress source)
58    {
59        this.ch = ch;
60        this.group = group;
61        this.interf = interf;
62        this.source = source;
63    }
64
65    /**
66     * MembershipKey will additional context for IPv4 membership
67     */
68    static class Type4 extends MembershipKeyImpl {
69        private final int groupAddress;
70        private final int interfAddress;
71        private final int sourceAddress;
72
73        Type4(MulticastChannel ch,
74              InetAddress group,
75              NetworkInterface interf,
76              InetAddress source,
77              int groupAddress,
78              int interfAddress,
79              int sourceAddress)
80        {
81            super(ch, group, interf, source);
82            this.groupAddress = groupAddress;
83            this.interfAddress = interfAddress;
84            this.sourceAddress = sourceAddress;
85        }
86
87        int groupAddress() {
88            return groupAddress;
89        }
90
91        int interfaceAddress() {
92            return interfAddress;
93        }
94
95        int source() {
96            return sourceAddress;
97        }
98    }
99
100    /**
101     * MembershipKey will additional context for IPv6 membership
102     */
103    static class Type6 extends MembershipKeyImpl {
104        private final byte[] groupAddress;
105        private final int index;
106        private final byte[] sourceAddress;
107
108        Type6(MulticastChannel ch,
109              InetAddress group,
110              NetworkInterface interf,
111              InetAddress source,
112              byte[] groupAddress,
113              int index,
114              byte[] sourceAddress)
115        {
116            super(ch, group, interf, source);
117            this.groupAddress = groupAddress;
118            this.index = index;
119            this.sourceAddress = sourceAddress;
120        }
121
122        byte[] groupAddress() {
123            return groupAddress;
124        }
125
126        int index() {
127            return index;
128        }
129
130        byte[] source() {
131            return sourceAddress;
132        }
133    }
134
135    public boolean isValid() {
136        return !invalid;
137    }
138
139    // package-private
140    void invalidate() {
141        invalid = true;
142    }
143
144    public void drop() {
145        // delegate to channel
146        ((DatagramChannelImpl)ch).drop(this);
147    }
148
149    @Override
150    public MulticastChannel channel() {
151        return ch;
152    }
153
154    @Override
155    public InetAddress group() {
156        return group;
157    }
158
159    @Override
160    public NetworkInterface networkInterface() {
161        return interf;
162    }
163
164    @Override
165    public InetAddress sourceAddress() {
166        return source;
167    }
168
169    @Override
170    public MembershipKey block(InetAddress toBlock)
171        throws IOException
172    {
173        if (source != null)
174            throw new IllegalStateException("key is source-specific");
175
176        synchronized (stateLock) {
177            if ((blockedSet != null) && blockedSet.contains(toBlock)) {
178                // already blocked, nothing to do
179                return this;
180            }
181
182            ((DatagramChannelImpl)ch).block(this, toBlock);
183
184            // created blocked set if required and add source address
185            if (blockedSet == null)
186                blockedSet = new HashSet<>();
187            blockedSet.add(toBlock);
188        }
189        return this;
190    }
191
192    @Override
193    public MembershipKey unblock(InetAddress toUnblock) {
194        synchronized (stateLock) {
195            if ((blockedSet == null) || !blockedSet.contains(toUnblock))
196                throw new IllegalStateException("not blocked");
197
198            ((DatagramChannelImpl)ch).unblock(this, toUnblock);
199
200            blockedSet.remove(toUnblock);
201        }
202        return this;
203    }
204
205    @Override
206    public String toString() {
207        StringBuilder sb = new StringBuilder(64);
208        sb.append('<');
209        sb.append(group.getHostAddress());
210        sb.append(',');
211        sb.append(interf.getName());
212        if (source != null) {
213            sb.append(',');
214            sb.append(source.getHostAddress());
215        }
216        sb.append('>');
217        return sb.toString();
218    }
219}
220