1 /*
2  * Copyright (c) 2013, 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 package sun.management.jdp;
26 
27 import java.io.IOException;
28 import java.net.Inet6Address;
29 import java.net.InetAddress;
30 import java.net.InetSocketAddress;
31 import java.net.NetworkInterface;
32 import java.net.ProtocolFamily;
33 import java.net.StandardProtocolFamily;
34 import java.net.StandardSocketOptions;
35 import java.nio.ByteBuffer;
36 import java.nio.channels.DatagramChannel;
37 import java.nio.channels.UnsupportedAddressTypeException;
38 import java.util.Enumeration;
39 
40 /**
41  * JdpBroadcaster is responsible for sending pre-built JDP packet across a Net
42  *
43  * <p> Multicast group address, port number and ttl have to be chosen on upper
44  * level and passed to broadcaster constructor. Also it's possible to specify
45  * source address to broadcast from. </p>
46  *
47  * <p>JdpBradcaster doesn't perform any validation on a supplied {@code port} and {@code ttl} because
48  * the allowed values depend on an operating system setup</p>
49  *
50  */
51 public final class JdpBroadcaster {
52 
53     private final InetAddress addr;
54     private final int port;
55     private final DatagramChannel channel;
56 
57     /**
58      * Create a new broadcaster
59      *
60      * @param address - multicast group address
61      * @param srcAddress - address of interface we should use to broadcast.
62      * @param port - udp port to use
63      * @param ttl - packet ttl
64      * @throws IOException
65      */
JdpBroadcaster(InetAddress address, InetAddress srcAddress, int port, int ttl)66     public JdpBroadcaster(InetAddress address, InetAddress srcAddress, int port, int ttl)
67             throws IOException, JdpException {
68         this.addr = address;
69         this.port = port;
70 
71         ProtocolFamily family = (address instanceof Inet6Address)
72                 ? StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
73 
74         channel = DatagramChannel.open(family);
75         channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
76         channel.setOption(StandardSocketOptions.IP_MULTICAST_TTL, ttl);
77 
78         // with srcAddress equal to null, this constructor do exactly the same as
79         // if srcAddress is not passed
80         if (srcAddress != null) {
81             // User requests particular interface to bind to
82             NetworkInterface interf = NetworkInterface.getByInetAddress(srcAddress);
83 
84             if (interf == null) {
85                 throw new JdpException("Unable to get network interface for " + srcAddress.toString());
86             }
87 
88             if (!interf.isUp()) {
89                 throw new JdpException(interf.getName() + " is not up.");
90             }
91 
92             if (!interf.supportsMulticast()) {
93                 throw new JdpException(interf.getName() + " does not support multicast.");
94             }
95 
96             try {
97                 channel.bind(new InetSocketAddress(srcAddress, 0));
98             } catch (UnsupportedAddressTypeException ex) {
99                 throw new JdpException("Unable to bind to source address");
100             }
101             channel.setOption(StandardSocketOptions.IP_MULTICAST_IF, interf);
102         }
103     }
104 
105     /**
106      * Create a new broadcaster
107      *
108      * @param address - multicast group address
109      * @param port - udp port to use
110      * @param ttl - packet ttl
111      * @throws IOException
112      */
JdpBroadcaster(InetAddress address, int port, int ttl)113     public JdpBroadcaster(InetAddress address, int port, int ttl)
114             throws IOException, JdpException {
115         this(address, null, port, ttl);
116     }
117 
118     /**
119      * Broadcast pre-built packet
120      *
121      * @param packet - instance of JdpPacket
122      * @throws IOException
123      */
sendPacket(JdpPacket packet)124     public void sendPacket(JdpPacket packet)
125             throws IOException {
126         byte[] data = packet.getPacketData();
127         // Unlike allocate/put wrap don't need a flip afterward
128         ByteBuffer b = ByteBuffer.wrap(data);
129         channel.send(b, new InetSocketAddress(addr, port));
130     }
131 
132     /**
133      * Shutdown broadcaster and close underlying socket channel
134      *
135      * @throws IOException
136      */
shutdown()137     public void shutdown() throws IOException {
138         channel.close();
139     }
140 }
141