1 /*******************************************************************************
2  * Copyright (c) 1997, 2008 by ProSyst Software GmbH
3  * http://www.prosyst.com
4  *
5  * This program and the accompanying materials
6  * are made available under the terms of the Eclipse Public License 2.0
7  * which accompanies this distribution, and is available at
8  * https://www.eclipse.org/legal/epl-2.0/
9  *
10  * SPDX-License-Identifier: EPL-2.0
11  *
12  * Contributors:
13  *    ProSyst Software GmbH - initial API and implementation
14  *******************************************************************************/
15 package org.eclipse.equinox.internal.ip.dscagent;
16 
17 import java.io.IOException;
18 import java.io.InterruptedIOException;
19 import java.net.*;
20 import java.util.Dictionary;
21 import org.eclipse.equinox.internal.ip.ProvisioningInfoProvider;
22 import org.eclipse.equinox.internal.ip.impl.Log;
23 import org.eclipse.equinox.internal.ip.impl.ProvisioningAgent;
24 import org.osgi.framework.BundleContext;
25 import org.osgi.framework.ServiceReference;
26 import org.osgi.service.provisioning.ProvisioningService;
27 
28 /**
29  * Agent who make FW available for UDM multicast discovery. It joins
30  * provisioning agent to a MulticastSocket and waits for ping replies. The
31  * response contains data that describes gateway. HTTP port is determined using
32  * next algorithm: <BR>
33  * <OL>
34  * <LI>If <I>"equinox.provisioning.gwhttp.port"</I> property is set it is used
35  * for HTTP port value.</LI>
36  * <LI>Else if there has <I>org.osgi.service.HttpService</I> is registered and
37  * the default such service has registration property "openPort", value of this
38  * property is assumed as port. </LI>
39  * <LI>Else HTTP port is assumed to be "80" (the default HTTP port).</LI>
40  * </OL>
41  *
42  * @author Avgustin Marinov
43  * @author Pavlin Dobrev
44  * @version 1.0
45  */
46 
47 public class DiscoveryAgent implements Runnable {
48 
49 	/**
50 	 * This property can be used when HTTP service is not a prosyst's
51 	 * implementation or HTTP is not always, which gives a port with
52 	 * registration property "openPort", available.
53 	 */
54 	public static final String HTTP_PORT = "equinox.provisioning.gwhttp.port";
55 
56 	/**
57 	 * This property can be used when HTTPS service is not a prosyst's
58 	 * implementation, HTTPS is old prosyst's https implementation, or http is
59 	 * not always, which gives a port with registration property
60 	 * "secureOpenPort", or HTTPS is not always available.
61 	 */
62 	public static final String HTTPS_PORT = "equinox.provisioning.gwhttps.port";
63 
64 	/**
65 	 * Packet Timeout
66 	 */
67 	public static final String TIMEOUT = "equinox.provisioning.packet.timeout";
68 
69 	/**
70 	 * Separator used in string representing gateway. It separates different
71 	 * parts of that string.
72 	 */
73 	public static final char SEPARATOR = '#';
74 
75 	/**
76 	 * The String that corresponds to lack of sPID.
77 	 */
78 	public static final String NULL = new String(new byte[] {1});
79 
80 	/** Reference to provisioning agent. */
81 	private ProvisioningAgent prvAgent;
82 
83 	/** If discoverer service is active. */
84 	private boolean active = true;
85 
86 	// Net staff
87 	/** The multicast socket. */
88 	private MulticastSocket mcsocket;
89 	/** The group. */
90 	private InetAddress group;
91 
92 	/**
93 	 * Bundles context used for determining of HTTP port in ProSyst HTTPS
94 	 * Service Implementation.
95 	 */
96 	private BundleContext bc;
97 
98 	/**
99 	 * Constructs instance of Discovery agent.
100 	 *
101 	 * @param address
102 	 *            address of multicast host.
103 	 * @param port
104 	 *            port of multicast host.
105 	 * @param bc
106 	 *            bundle context used to be accessed framework.
107 	 * @param prvAgent
108 	 *            the provisioning agent
109 	 * @throws UnknownHostException
110 	 *             when address cannot be resolved
111 	 * @throws IOException
112 	 *             on I/O error, when accessing the given address
113 	 */
DiscoveryAgent(String address, int port, BundleContext bc, ProvisioningAgent prvAgent)114 	public DiscoveryAgent(String address, int port, BundleContext bc, ProvisioningAgent prvAgent) throws UnknownHostException, IOException {
115 		group = InetAddress.getByName(address);
116 		mcsocket = new MulticastSocket(port);
117 		this.bc = bc;
118 		this.prvAgent = prvAgent;
119 		mcsocket.joinGroup(group);
120 		mcsocket.setSoTimeout(ProvisioningAgent.getInteger(TIMEOUT, 10000));
121 		Log.debug("Discovery Agent has joined to multicast socket " + address + ":" + port + ".");
122 	}
123 
124 	/**
125 	 * Until close() method is invoked this method accepts packages broadcasted
126 	 * to multicast host.
127 	 */
run()128 	public void run() {
129 		byte[] buffer = new byte[256];
130 		DatagramPacket request = new DatagramPacket(buffer, buffer.length);
131 		Log.debug("Discovery Agent starting listening.");
132 		int errors = 0;
133 		while (active) {
134 			try {
135 				mcsocket.receive(request);
136 				/* It is ping send by the backend discoverer */
137 				if ("equinox.provisioning.ping".equals(new String(request.getData(), 0, request.getLength()))) {
138 					byte[] data = getResponse();
139 					if (data != null) {
140 						DatagramPacket response = new DatagramPacket(data, data.length, group, request.getPort());
141 						/* response.setData(data); */
142 						mcsocket.send(response);
143 					}
144 				}
145 				request.setLength(buffer.length); /* Restore packet length */
146 			} catch (InterruptedIOException _) {
147 			} catch (IOException e) {
148 				if (errors++ > 5) {
149 					Log.debug("Seventh unexpected exception. Discoverer will be closed!", e);
150 					return;
151 				}
152 			}
153 		}
154 	}
155 
156 	/**
157 	 * Closes discoverer agent
158 	 */
close()159 	public void close() {
160 		try {
161 			active = false;
162 			mcsocket.leaveGroup(group);
163 			mcsocket.close();
164 		} catch (Exception e) {
165 			Log.debug(e);
166 		}
167 	}
168 
169 	/**
170 	 * Encodes some valuable gateways parameters into string. The format is as
171 	 * follows:<BR>
172 	 * <I>&lt;spid&gt;#&lt;host&gt;#&lt;http port&gt;#&lt;ready&gt;#&lt;others
173 	 * info&gt;</I><BR>
174 	 * where:<BR>
175 	 * <OL>
176 	 * <LI><I>sPID</I> is service platform id</LI>
177 	 * <LI><I>host</I> is service platform host</LI>
178 	 * <LI><I>HTTP port</I> is service platform HTTP port</LI>
179 	 * <LI><I>ready</I> is service platform is ready with deploying management
180 	 * agent bundle</LI>
181 	 * <LI><I>others info</I> is service platform others info in format:
182 	 * <I>{&lt;key0&gt;=&lt;value0&gt;,&lt;key1&gt;=&lt;value1&gt;...}</I></LI>
183 	 * </OL>
184 	 *
185 	 * @return string representation of gateway data as byte array.
186 	 */
getResponse()187 	private byte[] getResponse() {
188 		String httpPort = ProvisioningAgent.bc.getProperty(HTTP_PORT);
189 		String httpsPort = ProvisioningAgent.bc.getProperty(HTTPS_PORT);
190 
191 		// try to determine from service
192 		if (httpPort == null || httpsPort == null) {
193 			// Not using HttpService.class.getName(), because we don't need to
194 			// import
195 			// it.
196 			ServiceReference sref = bc.getServiceReference("org.osgi.service.http.HttpService");
197 			// HTTP Service is available - explore it
198 			if (httpPort == null)
199 				httpPort = getPortProperty(sref, "openPort");
200 			if (httpsPort == null)
201 				httpsPort = getPortProperty(sref, "secureOpenPort");
202 		}
203 
204 		Dictionary info = prvAgent.getInformation();
205 		if (info == null) {
206 			return null;
207 		}
208 
209 		StringBuffer buff = new StringBuffer();
210 		String spid = (String) info.get(ProvisioningService.PROVISIONING_SPID);
211 		buff.append(spid == null || spid.length() == 0 ? NULL : spid);
212 		buff.append(SEPARATOR);
213 		String host = null;
214 		try {
215 			host = InetAddress.getLocalHost().getHostName();
216 		} catch (Exception e) {
217 			host = "unknown";
218 		}
219 		buff.append(host);
220 		buff.append(SEPARATOR);
221 		buff.append(httpPort);
222 		buff.append(SEPARATOR);
223 		buff.append(httpsPort);
224 		buff.append(SEPARATOR);
225 		buff.append(info.get(ProvisioningInfoProvider.MANAGER_URL) != null);
226 		buff.append(SEPARATOR);
227 		buff.append('{');
228 		buff.append(ProvisioningService.PROVISIONING_REFERENCE);
229 		buff.append('=');
230 		buff.append(info.get(ProvisioningService.PROVISIONING_REFERENCE));
231 		buff.append(',');
232 		buff.append(ProvisioningService.PROVISIONING_START_BUNDLE);
233 		buff.append('=');
234 		buff.append(info.get(ProvisioningService.PROVISIONING_START_BUNDLE));
235 		buff.append('}');
236 		buff.append(SEPARATOR);
237 		buff.append(getFlag());
238 		Log.debug("Discoverer agent sends gw info : " + buff);
239 		return buff.toString().getBytes();
240 	}
241 
getFlag()242 	public int getFlag() {
243 		int flag = 0;
244 		if (prvAgent.getHttpAllowed()) {
245 			try {
246 				new URL("http://").openConnection();
247 				flag |= 0x01;
248 			} catch (Exception e) {
249 			}
250 		}
251 
252 		try {
253 			new URL("rsh://").openConnection();
254 			flag |= 0x02;
255 		} catch (Exception e) {
256 		}
257 
258 		try {
259 			new URL("https://").openConnection();
260 			flag |= 0x04;
261 		} catch (Exception e) {
262 		}
263 
264 		return flag;
265 	}
266 
getPortProperty(ServiceReference ref, String property)267 	private static final String getPortProperty(ServiceReference ref, String property) {
268 		Object ret = ref != null ? ref.getProperty(property) : null;
269 		return ret == null ? "-1" : "" + ret;
270 	}
271 
272 }
273