View Javadoc
1   /*
2     (C) Copyright IBM Corp. 2007, 2009
3    
4     THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE 
5     ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE 
6     CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
7    
8     You can obtain a current copy of the Eclipse Public License from
9     http://www.opensource.org/licenses/eclipse-1.0.php
10   
11    @author : Endre Bak, IBM, ebak@de.ibm.com
12   * 
13   * Change History
14   * Flag       Date        Prog         Description
15   *------------------------------------------------------------------------------- 
16   * 1804402    2007-09-28  ebak         IPv6 ready SLP
17   * 1804402    2007-11-10  ebak         IPv6 ready SLP - revision 4
18   * 1892103    2008-02-15  ebak         SLP improvements
19   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
20   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
21   * 2531371    2009-02-10  raman_arora  Upgrade client to JDK 1.5 (Phase 2)
22   * 2763216    2009-04-14  blaschke-oss Code cleanup: visible spelling/grammar errors
23   */
24  
25  package org.metricshub.wbem.sblim.slp.internal.sa;
26  
27  /*-
28   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
29   * WBEM Java Client
30   * ჻჻჻჻჻჻
31   * Copyright 2023 - 2025 MetricsHub
32   * ჻჻჻჻჻჻
33   * Licensed under the Apache License, Version 2.0 (the "License");
34   * you may not use this file except in compliance with the License.
35   * You may obtain a copy of the License at
36   *
37   *      http://www.apache.org/licenses/LICENSE-2.0
38   *
39   * Unless required by applicable law or agreed to in writing, software
40   * distributed under the License is distributed on an "AS IS" BASIS,
41   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42   * See the License for the specific language governing permissions and
43   * limitations under the License.
44   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
45   */
46  
47  import java.io.IOException;
48  import java.io.OutputStream;
49  import java.net.DatagramPacket;
50  import java.net.DatagramSocket;
51  import java.net.Socket;
52  import java.net.UnknownHostException;
53  import java.util.List;
54  import org.metricshub.wbem.sblim.slp.ServiceLocationException;
55  import org.metricshub.wbem.sblim.slp.ServiceURL;
56  import org.metricshub.wbem.sblim.slp.internal.IPv6MulticastAddressFactory;
57  import org.metricshub.wbem.sblim.slp.internal.Net;
58  import org.metricshub.wbem.sblim.slp.internal.SLPConfig;
59  import org.metricshub.wbem.sblim.slp.internal.SLPDefaults;
60  import org.metricshub.wbem.sblim.slp.internal.TRC;
61  import org.metricshub.wbem.sblim.slp.internal.msg.AttributeReply;
62  import org.metricshub.wbem.sblim.slp.internal.msg.AttributeRequest;
63  import org.metricshub.wbem.sblim.slp.internal.msg.FunctionIDs;
64  import org.metricshub.wbem.sblim.slp.internal.msg.MsgFactory;
65  import org.metricshub.wbem.sblim.slp.internal.msg.ReplyMessage;
66  import org.metricshub.wbem.sblim.slp.internal.msg.RequestMessage;
67  import org.metricshub.wbem.sblim.slp.internal.msg.SLPMessage;
68  import org.metricshub.wbem.sblim.slp.internal.msg.ServiceAcknowledgment;
69  import org.metricshub.wbem.sblim.slp.internal.msg.ServiceDeregistration;
70  import org.metricshub.wbem.sblim.slp.internal.msg.ServiceRegistration;
71  import org.metricshub.wbem.sblim.slp.internal.msg.ServiceReply;
72  import org.metricshub.wbem.sblim.slp.internal.msg.ServiceRequest;
73  import org.metricshub.wbem.sblim.slp.internal.msg.ServiceTypeReply;
74  
75  /**
76   * ServiceAgent
77   *
78   */
79  public class ServiceAgent implements FunctionIDs {
80  	private boolean iUseV4 = Net.hasIPv4() && SLPConfig.getGlobalCfg().useIPv4(), iUseV6 =
81  		Net.hasIPv6() && SLPConfig.getGlobalCfg().useIPv6();
82  
83  	boolean iStarted;
84  
85  	private volatile boolean iSkipFirstRequest;
86  
87  	private DatagramThread iDGThread = new DatagramThread(this);
88  
89  	private TCPThread iTCPThread = new TCPThread(this);
90  
91  	private ServiceTable iSrvTable = new ServiceTable(this.iDGThread);
92  
93  	private MessageTable iMsgTable = new MessageTable();
94  
95  	/**
96  	 * main
97  	 *
98  	 * @param pArgs
99  	 * @throws IOException
100 	 */
101 	public static void main(String[] pArgs) throws IOException {
102 		if (pArgs != null && pArgs.length >= 1) {
103 			int port = Integer.parseInt(pArgs[0]);
104 			SLPConfig.getGlobalCfg().setPort(port);
105 		}
106 		ServiceAgent srvAgent = new ServiceAgent();
107 		srvAgent.start();
108 		TRC.debug("starting idle loop");
109 		while (true) {
110 			try {
111 				Thread.sleep(100);
112 			} catch (InterruptedException e) {
113 				// lofasz
114 			}
115 		}
116 	}
117 
118 	/**
119 	 * setSkipFirstRequest
120 	 *
121 	 * @param pValue
122 	 */
123 	public void setSkipFirstRequest(boolean pValue) {
124 		this.iSkipFirstRequest = pValue;
125 	}
126 
127 	/**
128 	 * start
129 	 *
130 	 * @throws IOException
131 	 */
132 	public void start() throws IOException {
133 		if (this.iStarted) return;
134 		// launch receivers
135 		TRC.debug("start receivers");
136 		if (this.iUseV4 || this.iUseV6) {
137 			this.iTCPThread.start();
138 			this.iDGThread.start();
139 			TRC.debug("wait4 TCP init");
140 			this.iTCPThread.wait4init();
141 			TRC.debug("wait4 Datagram init");
142 			this.iDGThread.wait4init();
143 		}
144 		this.iStarted = true;
145 		TRC.debug("receivers started");
146 		// join multicast groups
147 		if (this.iUseV4) this.iDGThread.joinGroup(SLPConfig.getMulticastAddress());
148 		if (this.iUseV6) {
149 			this.iDGThread.joinGroup(IPv6MulticastAddressFactory.getSrvLocAddress(SLPDefaults.IPV6_MULTICAST_SCOPE));
150 		}
151 	}
152 
153 	/**
154 	 * stop
155 	 */
156 	public void stop() {
157 		// stop the receivers
158 		if (this.iUseV4 || this.iUseV6) {
159 			this.iTCPThread.stop();
160 			this.iDGThread.stop();
161 		}
162 		this.iStarted = false;
163 	}
164 
165 	/**
166 	 * processMessage
167 	 *
168 	 * @param pDGSock
169 	 * @param pPacket
170 	 */
171 	public void processMessage(DatagramSocket pDGSock, DatagramPacket pPacket) {
172 		byte[] reply;
173 		SLPMessage msg;
174 		try {
175 			msg = MsgFactory.parse(pPacket);
176 		} catch (Exception e) {
177 			// no response for unparseable messages
178 			TRC.debug("Message parsing error!", e);
179 			return;
180 		}
181 
182 		try {
183 			TRC.debug("processing: " + msg);
184 			reply = this.iMsgTable.getResponse(pPacket.getAddress(), msg);
185 			if (reply == null) {
186 				SLPMessage replyMsg = makeReply(msg);
187 				if (replyMsg != null) {
188 					TRC.debug("response : " + replyMsg);
189 					reply = replyMsg.serialize(false, true, true);
190 					this.iMsgTable.addResponse(pPacket.getAddress(), msg, reply);
191 					// debugging: do not reply to the first request
192 					if (this.iSkipFirstRequest) {
193 						TRC.debug("refusing response");
194 						return;
195 					}
196 				}
197 			} else {
198 				TRC.debug("cached response");
199 			}
200 		} catch (ServiceLocationException e) {
201 			TRC.debug(e.toString(), e);
202 			reply = makeErrorReply(msg, e); // known XID
203 		}
204 		if (reply != null) {
205 			try {
206 				TRC.debug("sending response");
207 				pDGSock.send(new DatagramPacket(reply, reply.length, pPacket.getAddress(), pPacket.getPort()));
208 			} catch (IOException e) {
209 				TRC.error(e);
210 			}
211 		}
212 	}
213 
214 	/**
215 	 * processMessage
216 	 *
217 	 * @param pStreamSock
218 	 */
219 	public void processMessage(Socket pStreamSock) {
220 		byte[] reply;
221 		SLPMessage msg;
222 		try {
223 			msg = MsgFactory.parse(pStreamSock.getInputStream());
224 		} catch (Exception e) {
225 			// no response for unparseable messages
226 			TRC.debug("Message parsing error!", e);
227 			return;
228 		}
229 		try {
230 			SLPMessage replyMsg = makeReply(msg);
231 			reply = replyMsg == null ? null : replyMsg.serialize(false, false, true);
232 		} catch (ServiceLocationException e) {
233 			reply = makeErrorReply(msg, e);
234 		}
235 		if (reply != null) {
236 			try {
237 				OutputStream os = pStreamSock.getOutputStream();
238 				os.write(reply);
239 				os.flush();
240 			} catch (IOException e) {
241 				TRC.error(e);
242 			}
243 		}
244 		try {
245 			pStreamSock.close();
246 		} catch (IOException e) {
247 			TRC.error(e);
248 		}
249 	}
250 
251 	/*
252 	 * XID is set to a unique value for each unique request. If the request is
253 	 * retransmitted, the same XID is used. Replies set the XID to the same
254 	 * value as the xid in the request. Only unsolicited DAAdverts are sent with
255 	 * an XID of 0.
256 	 */
257 	private SLPMessage makeReply(SLPMessage pRequest) throws ServiceLocationException {
258 		try {
259 			SLPMessage msg = pRequest instanceof RequestMessage
260 				? makeReply4Request((RequestMessage) pRequest)
261 				: makeReply4Others(pRequest);
262 			if (msg != null) msg.setXID(pRequest.getXID());
263 			return msg;
264 		} catch (UnknownHostException e) {
265 			TRC.error(e);
266 			throw new ServiceLocationException(ServiceLocationException.NETWORK_ERROR);
267 		} catch (IOException e) {
268 			TRC.error(e);
269 			throw new ServiceLocationException(ServiceLocationException.NETWORK_ERROR);
270 		}
271 	}
272 
273 	private ReplyMessage makeReply4Request(RequestMessage pRequest) {
274 		ReplyMessage replyMsg;
275 		List<String> scopes = pRequest.getScopeList();
276 		switch (pRequest.getFunctionID()) {
277 			case SRV_RQST:
278 				ServiceRequest srvRqst = (ServiceRequest) pRequest;
279 				if (SLPDefaults.DA_SERVICE_TYPE.equals(srvRqst.getServiceType())) return null;
280 				List<ServiceURL> urlList = this.iSrvTable.getServiceURLs(srvRqst.getServiceType(), scopes);
281 				TRC.debug("srvReply : " + urlList);
282 				replyMsg = new ServiceReply(0, urlList);
283 				break;
284 			case ATTR_RQST:
285 				AttributeRequest attrRqst = (AttributeRequest) pRequest;
286 				replyMsg = new AttributeReply(0, this.iSrvTable.getAttributes(attrRqst.getServiceURL(), scopes));
287 				break;
288 			case SRV_TYPE_RQST:
289 				// ServiceTypeRequest srvTypeRqst =
290 				// (ServiceTypeRequest)pRequest;
291 				replyMsg = new ServiceTypeReply(0, this.iSrvTable.getServiceTypes(scopes));
292 				break;
293 			default:
294 				return null;
295 		}
296 		return replyMsg;
297 	}
298 
299 	private SLPMessage makeReply4Others(SLPMessage pRequest) throws IOException {
300 		switch (pRequest.getFunctionID()) {
301 			case SRV_REG:
302 				ServiceRegistration srvReg = (ServiceRegistration) pRequest;
303 				this.iSrvTable.add(srvReg.getServiceURL(), srvReg.getAttributeList(), srvReg.getScopeList());
304 				return new ServiceAcknowledgment(0);
305 			case SRV_DEREG:
306 				ServiceDeregistration srvDereg = (ServiceDeregistration) pRequest;
307 				this.iSrvTable.remove(srvDereg.getServiceURL());
308 				return new ServiceAcknowledgment(0);
309 			default:
310 				// FIXME maybe returning an error message is better
311 				return null;
312 		}
313 	}
314 
315 	private static byte[] makeErrorReply(SLPMessage pRequest, ServiceLocationException pE) {
316 		return makeErrorReply(pRequest, pE.getErrorCode());
317 	}
318 
319 	/*
320 	 * Error reply: SRV_REG, SRV_DEREG -> SRV_ACK SRV_RQST -> SRV_RSP ATTR_RQST
321 	 * -> ATTR_RPLY SRV_TYPE_RQST -> SRV_TYPE_RPLY
322 	 */
323 
324 	private static byte[] makeErrorReply(SLPMessage pRequest, int pErrorCode) {
325 		ReplyMessage replyMsg;
326 		switch (pRequest.getFunctionID()) {
327 			case SRV_RQST:
328 				replyMsg = new ServiceReply(pErrorCode, null);
329 				break;
330 			case ATTR_RQST:
331 				replyMsg = new AttributeReply(pErrorCode, null);
332 				break;
333 			case SRV_TYPE_RQST:
334 				replyMsg = new ServiceTypeReply(pErrorCode, null);
335 				break;
336 			case SRV_REG:
337 			case SRV_DEREG:
338 				replyMsg = new ServiceAcknowledgment(pErrorCode);
339 				break;
340 			default:
341 				return null;
342 		}
343 		replyMsg.setXID(pRequest.getXID());
344 		try {
345 			return replyMsg.serialize(false, true, true);
346 		} catch (ServiceLocationException se) {
347 			TRC.error(se);
348 			return null;
349 		}
350 	}
351 }