28c3f4f392cd26f7697adc885826cf4bab589a12
[WeStealzYourDataz.git] / src / uk / ac / ntu / n0521366 / wsyd / libs / net / NetworkServerUDPMulticast.java
1 /*
2  * The MIT License
3  *
4  * Copyright 2015 TJ <hacker@iam.tj>.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 package uk.ac.ntu.n0521366.wsyd.libs.net;
25
26 import java.io.IOException;
27 import java.net.MulticastSocket;
28 import java.net.SocketException;
29 import java.net.NetworkInterface;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
32 import java.util.ArrayList;
33 import java.util.Enumeration;
34
35 /**
36  *
37  * @author TJ <hacker@iam.tj>
38  */
39 public class NetworkServerUDPMulticast extends NetworkServerUDP {
40     
41     /**
42      * the Multicast socket.
43      * 
44      * Deliberately hides the DatagramSocket in NetworkServerUDP so that inherited methods
45      * can access the same name.
46      */
47     private MulticastSocket _multicastSocket = null;
48     /**
49      * Construct the server with a Logger.
50      * 
51      * No socket is opened.
52      * 
53      * @param socketAddress The socket to listen on
54      * @param title source identifier for use in log messages and sent NetworkMessage objects
55      * @param logger An instance of Logger to be used by all objects of this class
56      */
57     public NetworkServerUDPMulticast(WSYD_SocketAddress socketAddress, String title, Logger logger) {
58         super(socketAddress, title, logger);
59         // permit broadcasting to pseudo-host 'all' since this is multicast
60         this._serviceToHostMap.put("all", new LastSeenHost(socketAddress.getSocketAddress()));
61     }
62
63     /**
64      * Construct the server without a Logger.
65      * 
66      * No socket is opened.
67      * 
68      * @param socketAddress The socket to listen on
69      * @param title source identifier for use in log messages and sent NetworkMessage objects
70      */
71     public NetworkServerUDPMulticast(WSYD_SocketAddress socketAddress, String title) {
72         super(socketAddress, title);
73     }
74
75     /**
76      * Get the MulticastSocket object for this service.
77      * 
78      * @return the base-class DatagramSocket type
79      */
80     @Override
81     public java.net.DatagramSocket getSocket() {
82         return this._multicastSocket;
83     }
84     /**
85      * Join the multicast group on all interfaces ready for accepting packets.
86      * 
87      * It should also set a reasonable socket timeout with a call to setSoTimeout()
88      * to prevent unnecessary blocking.
89      * 
90      * @throws SocketException 
91      */
92     @Override
93     public  void serverOpen() throws SocketException {
94         try {
95             ArrayList<String> messages = new ArrayList<>();
96
97             this._multicastSocket = new MulticastSocket(_socketAddress.getPort());
98             this._multicastSocket.setTimeToLive(2); // don't traverse more than 2 routers
99             this._multicastSocket.setSoTimeout(100); // 1/10th second blocking timeout on receive()
100             this._multicastSocket.setLoopbackMode(true); // inverted logic; true == disable. Don't want to receive our own sent packets
101             
102             Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces();
103             while (ifs.hasMoreElements()) {
104                 NetworkInterface iface = ifs.nextElement();
105                 messages.clear();
106                 messages.add(iface.getName());
107                 messages.add(Boolean.toString(iface.supportsMulticast()));
108                 log(Level.INFO, _title, "Interface {0}: probe multicast support: {1}", messages);
109                 if (iface.supportsMulticast()) {
110                     try {
111                         messages.clear();
112                         messages.add(iface.getName());
113                         messages.add(_socketAddress.getSocketAddress().getAddress().toString());
114                         this._multicastSocket.joinGroup(_socketAddress.getSocketAddress(), iface);
115                     log(Level.INFO, _title, "Interface {0}: joined multicast group {1}", messages);
116                     } catch (Exception e) {
117                         messages.clear();
118                         messages.add(iface.getName());
119                         log(Level.SEVERE, _title, "Interface {0}: failed to join multicast group", messages);
120                     }
121                 }
122             }
123         } catch (IOException e) {
124             log(Level.SEVERE, _title, "Failed to open multicast socket");
125         }
126     }
127
128     /**
129      * Close the socket.
130      * 
131      * @throws SocketException
132      */
133     @Override
134     public void serverClose() throws SocketException {
135         if (this._multicastSocket != null) {
136             try {
137                 log(Level.INFO, _title, "Leaving multicast group");
138                 this._multicastSocket.leaveGroup(_socketAddress.getAddress());
139                 this._multicastSocket.close();
140             } catch (IOException e) {
141                 log(Level.SEVERE, _title, "failed to leave multicast group");
142             }
143             
144         }
145     }
146
147     /**
148      * Remove stale service records.
149      * 
150      * @param ageInMillis milliseconds since last seen to be considered stale
151      * @return quantity of records removed
152      */
153     public ArrayList<String> cleanServiceToHostMap(long ageInMillis) {
154         ArrayList<String> result =  new ArrayList<>();
155         long expireTime = System.currentTimeMillis() - ageInMillis;
156         java.util.Enumeration<String> keys = this._serviceToHostMap.keys();
157         while (keys.hasMoreElements()) {
158             String key = keys.nextElement();
159
160             // XXX: special handling for "all" target - never remove it
161             if (!key.equals("all")) {
162                 LastSeenHost host = _serviceToHostMap.get(key);
163                 if (host != null) {
164                     if (host.timeInMillis < expireTime) {
165                         if (_serviceToHostMap.remove(key, host)) {
166                             result.add(key);
167                             ArrayList<String> messages = new ArrayList<>();
168                             messages.add(key);
169                             log(Level.INFO, _title, "Removed \"{0}\" from service map", messages);
170                         }
171                     }
172                 }
173             }
174         }
175         return result;
176     }
177
178 }