Source code: org/altara/mars/engine/SendExpectClient.java
1 /* MARS Network Monitoring Engine
2 Copyright (C) 1999 Brian H. Trammell
3 Copyright (C) 2002 Leapfrog Research & Development, LLC
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, it is available at
17 http:///www.gnu.org/copyleft/gpl.html, or by writing to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
20 */
21
22 package org.altara.mars.engine;
23
24 import org.apache.oro.text.regex.*;
25 import org.altara.mars.*;
26 import java.util.*;
27 import java.net.*;
28 import java.io.*;
29
30 public class SendExpectClient implements Serializable {
31 // for numbering child threads
32 private static int nctid = 0;
33 // maximum expect length in bytes; this is a hack used to keep
34 // a garbage-spewing service (like SSH) from causing problems.
35 private static final int MAXRECV_LEN = 512;
36
37 private static final char ENDSTREAM = (char)0xFFFF;
38
39 private transient InetAddress host;
40 private transient int port;
41 private transient long timeout;
42 private List script;
43
44 private transient Socket sock;
45 private transient Writer writer;
46 private transient Reader reader;
47 private transient StringBuffer buffer;
48 private transient int bufptr;
49
50 private transient Perl5Matcher matcher;
51
52 private transient boolean didTimeout;
53 private transient boolean didClose;
54 private transient boolean didFail;
55
56 public SendExpectClient() {
57 this(null,0,0);
58 }
59
60 public SendExpectClient(InetAddress host, int port, long timeout) {
61 // store all instance variables for script run
62 setServer(host,port,timeout);
63 // prepare an empty script
64 this.script = new LinkedList();
65 }
66
67 public void setServer(InetAddress host, int port, long timeout) {
68 this.host = host;
69 this.port = port;
70 this.timeout = timeout;
71 }
72
73 public void clear() {
74 script.clear();
75 }
76
77 public void send(Object out) {
78 script.add(new SendStep(out));
79 }
80
81 public void expect(Object in) {
82 script.add(new ExpectStep(in));
83 }
84
85 public void expect(Object pass, Object fail) {
86 script.add(new ExpectStep(pass,fail));
87 }
88
89 public synchronized Status runScript() {
90 // do nothing if no server
91 if (host == null) return new Status(Status.PROBEFAIL);
92 try {
93 // open a socket
94 try {
95 sock = new Socket(host, port);
96 } catch (IOException ex) {
97 return new Status(Status.DOWN);
98 }
99
100 // set up I/O on the socket
101 try {
102 writer = new OutputStreamWriter(sock.getOutputStream());
103 reader = new InputStreamReader(sock.getInputStream());
104 buffer = new StringBuffer();
105 bufptr = 0;
106 } catch (IOException ex) {
107 return new Status(Status.FASTCLOSE);
108 }
109
110 // start the helper threads
111 didTimeout = false;
112 didClose = false;
113 didFail = false;
114 new ReaderThread().start();
115 new ReaperThread().start();
116 // get the time for response time logging
117 long startTime = System.currentTimeMillis();
118 // run through the script
119 ListIterator scriptIter = script.listIterator();
120 while (scriptIter.hasNext()) {
121 // run the step
122 ((Step)scriptIter.next()).run();
123 // check for timeout
124 if (didTimeout) {
125 Status out = new Status(Status.TIMEOUT);
126 out.setProperty("received",buffer.toString());
127 return out;
128 }
129 // check for premature closing
130 if (didClose) {
131 Status out = new Status(Status.FASTCLOSE);
132 out.setProperty("received",buffer.toString());
133 return out;
134 }
135 // check for overrun or expect failure
136 if (didFail) {
137 Status out = new Status(Status.UNEXPECTED);
138 out.setProperty("received",buffer.toString());
139 return out;
140 }
141 }
142 // we made it to the end of the script, so we must be up
143 Status out =
144 new Status(Status.UP,System.currentTimeMillis() - startTime);
145 out.setProperty("received",buffer.toString());
146 return out;
147 } finally {
148 // close the socket no matter what
149 try {
150 sock.close();
151 } catch (Exception ignored) {}
152 }
153 }
154
155 public String dumpScript() {
156 StringBuffer out = new StringBuffer();
157 String hostName = "[prototype]";
158 if (host != null) hostName = host.getHostName();
159 out.append("SEC "+hostName+":"+port+" {\n");
160 ListIterator scriptIter = script.listIterator();
161 while (scriptIter.hasNext()) {
162 // print the step
163 Step nextStep = (Step)scriptIter.next();
164 if (nextStep instanceof SendStep) {
165 Object toSend = ((SendStep)nextStep).toSend;
166 out.append("\t> "+getShortClassName(toSend.getClass())+
167 " "+toSend.toString()+"\n");
168 } else if (nextStep instanceof ExpectStep) {
169 Object pass = ((ExpectStep)nextStep).pass;
170 Object fail = ((ExpectStep)nextStep).fail;
171 if (pass instanceof Perl5Pattern) {
172 out.append("\t< "+getShortClassName(pass.getClass())+
173 " "+((Perl5Pattern)pass).getPattern()+"\n");
174 } else {
175 out.append("\t< "+getShortClassName(pass.getClass())+
176 " "+pass.toString()+"\n");
177 }
178 if (fail != null) {
179 if (fail instanceof Perl5Pattern) {
180 out.append("\tX "+getShortClassName(pass.getClass())+
181 " "+((Perl5Pattern)fail).getPattern()+"\n");
182 } else {
183 out.append("\tX "+getShortClassName(fail.getClass())+
184 " "+fail.toString()+"\n");
185 }
186 }
187 } else {
188 out.append("\tBAD STEP "+nextStep.getClass()+"\n");
189 }
190 }
191 out.append("}\n");
192 return out.toString();
193 }
194
195 private static String getShortClassName(Class clazz) {
196 String longname = clazz.getName();
197 return longname.substring(longname.lastIndexOf(".")+1);
198 }
199
200 private class ReaderThread extends Thread {
201 private ReaderThread() {
202 super ("SECReader "+(nctid++));
203 }
204
205 public void run() {
206 try {
207 while(true) {
208 int c = reader.read();
209 synchronized (buffer) {
210 if (c == -1) {
211 buffer.append(ENDSTREAM);
212 buffer.notify();
213 break;
214 } else if (buffer.length() - bufptr > MAXRECV_LEN) {
215 didFail = true;
216 } else {
217 buffer.append((char)c);
218 }
219 buffer.notify();
220 }
221 }
222 } catch (IOException ex) {
223 didClose = true;
224 synchronized (buffer) {
225 buffer.notify();
226 }
227 }
228 }
229 }
230
231 private class ReaperThread extends Thread {
232 private ReaperThread() {
233 super ("SECReaper "+(nctid++));
234 }
235
236 public void run() {
237 try {
238 Thread.sleep(timeout);
239 } catch (InterruptedException ignored) {}
240 didTimeout = true;
241 synchronized(buffer) {
242 buffer.notify();
243 }
244 }
245 }
246
247 public class SendLocalHostname implements Serializable {
248 public String toString() {
249 return sock.getLocalAddress().getHostName();
250 }
251 }
252
253 public static class SendRemoteHostname implements Serializable {
254 private SendExpectProbe sep;
255
256 public SendRemoteHostname(SendExpectProbe sep) {
257 this.sep = sep;
258 }
259
260 public String toString() {
261 if (sep.getService() == null) return "[no svc]";
262 return sep.getService().getHost()
263 .getAddress().getHostName();
264 }
265 }
266
267 public static class SendParameter implements Serializable {
268 private SendExpectProbe sep;
269 private String name;
270
271 public SendParameter(SendExpectProbe sep, String name) {
272 this.sep = sep;
273 this.name = name;
274 }
275
276 public String toString() {
277 Service svc = sep.getService();
278 if (svc == null) return "[no svc]";
279 String param = svc.getParameter(name);
280 if (param == null || param.length() == 0)
281 param = svc.getProbeFactory().getServiceParamDefault(svc,name);
282 return param;
283 }
284 }
285
286 private abstract class Step implements Serializable {
287 public abstract void run();
288 }
289
290 private class SendStep extends Step {
291 private Object toSend;
292
293 public SendStep(Object toSend) {
294 this.toSend = toSend;
295 }
296
297 public void run() {
298 // convert to string
299 String out = toSend.toString();
300 // do nothing for null sends
301 if (out.length() == 0) return;
302 // set the buffer pointer to the current buffer end
303 bufptr = buffer.length();
304 // send the string
305 try {
306 writer.write(out);
307 //System.out.println("> "+out);
308 writer.flush();
309 } catch (IOException ex) {
310 didClose = true;
311 }
312 }
313 }
314
315 private class ExpectStep extends Step {
316 private Object pass, fail;
317
318 public ExpectStep(Object pass) {
319 this(pass,null);
320 }
321
322 public ExpectStep(Object pass, Object fail) {
323 this.pass = pass;
324 this.fail = fail;
325 }
326
327 public void run() {
328 while (true) {
329 synchronized (buffer) {
330 String bufarea = buffer.substring(bufptr);
331 // Check for pass match
332 if (checkMatch(bufarea, pass)) {
333 return;
334 }
335 // Check for fail match
336 if (fail != null && checkMatch(bufarea, fail)) {
337 didFail = true;
338 return;
339 }
340 // check for EOS
341 if (bufarea.indexOf(ENDSTREAM) >= 0) {
342 // end of stream found before expected string
343 // set didClose flag.
344 didClose = true;
345 return;
346 }
347 if (didClose || didTimeout || didFail) return;
348 try {
349 buffer.wait();
350 } catch (InterruptedException ex) {
351 return;
352 }
353 }
354 }
355 }
356
357 private boolean checkMatch (String bufarea, Object match) {
358 // Check for pattern match
359 if (match instanceof Perl5Pattern) {
360 if (matcher == null)
361 matcher = new Perl5Matcher();
362 if (matcher.contains(bufarea,((Perl5Pattern)match)))
363 return true;
364 // Check for string match
365 } else {
366 if (bufarea.indexOf(match.toString()) >= 0) return true;
367 }
368 // No match found if we made it here.
369 return false;
370 }
371 }
372 }