1 /* 2 * Conditions Of Use 3 * 4 * This software was developed by employees of the National Institute of 5 * Standards and Technology (NIST), an agency of the Federal Government. 6 * Pursuant to title 15 Untied States Code Section 105, works of NIST 7 * employees are not subject to copyright protection in the United States 8 * and are considered to be in the public domain. As a result, a formal 9 * license is not needed to use the software. 10 * 11 * This software is provided by NIST as a service and is expressly 12 * provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED 13 * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF 14 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT 15 * AND DATA ACCURACY. NIST does not warrant or make any representations 16 * regarding the use of the software or the results thereof, including but 17 * not limited to the correctness, accuracy, reliability or usefulness of 18 * the software. 19 * 20 * Permission to use this software is contingent upon your acceptance 21 * of the terms of this agreement 22 * 23 * . 24 * 25 */ 26 /****************************************************************************** 27 * Product of NIST/ITL Advanced Networking Technologies Division (ANTD) * 28 ******************************************************************************/ 29 package gov.nist.javax.sip.parser; 30 31 /* 32 * 33 * Lamine Brahimi and Yann Duponchel (IBM Zurich) noticed that the parser was 34 * blocking so I threw out some cool pipelining which ran fast but only worked 35 * when the phase of the moon matched its mood. Now things are serialized and 36 * life goes slower but more reliably. 37 * 38 */ 39 import gov.nist.core.*; 40 import gov.nist.javax.sip.message.*; 41 import gov.nist.javax.sip.header.*; 42 import java.text.ParseException; 43 import java.io.*; 44 45 /** 46 * This implements a pipelined message parser suitable for use with a stream - 47 * oriented input such as TCP. The client uses this class by instatiating with 48 * an input stream from which input is read and fed to a message parser. It 49 * keeps reading from the input stream and process messages in a never ending 50 * interpreter loop. The message listener interface gets called for processing 51 * messages or for processing errors. The payload specified by the 52 * content-length header is read directly from the input stream. This can be 53 * accessed from the SIPMessage using the getContent and getContentBytes methods 54 * provided by the SIPMessage class. 55 * 56 * @version 1.2 $Revision: 1.23 $ $Date: 2009/08/16 17:28:28 $ 57 * 58 * @author M. Ranganathan 59 * 60 * @see SIPMessageListener 61 */ 62 public final class PipelinedMsgParser implements Runnable { 63 64 65 66 /** 67 * The message listener that is registered with this parser. (The message 68 * listener has methods that can process correct and erroneous messages.) 69 */ 70 protected SIPMessageListener sipMessageListener; 71 private Thread mythread; // Preprocessor thread 72 //private byte[] messageBody; 73 //private boolean errorFlag; 74 private Pipeline rawInputStream; 75 private int maxMessageSize; 76 private int sizeCounter; 77 //private int messageSize; 78 79 /** 80 * default constructor. 81 */ 82 protected PipelinedMsgParser() { 83 super(); 84 85 } 86 87 private static int uid = 0; 88 89 private static synchronized int getNewUid() { 90 return uid++; 91 } 92 93 /** 94 * Constructor when we are given a message listener and an input stream 95 * (could be a TCP connection or a file) 96 * 97 * @param sipMessageListener 98 * Message listener which has methods that get called back from 99 * the parser when a parse is complete 100 * @param in 101 * Input stream from which to read the input. 102 * @param debug 103 * Enable/disable tracing or lexical analyser switch. 104 */ 105 public PipelinedMsgParser(SIPMessageListener sipMessageListener, 106 Pipeline in, boolean debug, int maxMessageSize) { 107 this(); 108 this.sipMessageListener = sipMessageListener; 109 rawInputStream = in; 110 this.maxMessageSize = maxMessageSize; 111 mythread = new Thread(this); 112 mythread.setName("PipelineThread-" + getNewUid()); 113 114 } 115 116 /** 117 * This is the constructor for the pipelined parser. 118 * 119 * @param mhandler 120 * a SIPMessageListener implementation that provides the message 121 * handlers to handle correctly and incorrectly parsed messages. 122 * @param in 123 * An input stream to read messages from. 124 */ 125 126 public PipelinedMsgParser(SIPMessageListener mhandler, Pipeline in, 127 int maxMsgSize) { 128 this(mhandler, in, false, maxMsgSize); 129 } 130 131 /** 132 * This is the constructor for the pipelined parser. 133 * 134 * @param in - 135 * An input stream to read messages from. 136 */ 137 138 public PipelinedMsgParser(Pipeline in) { 139 this(null, in, false, 0); 140 } 141 142 /** 143 * Start reading and processing input. 144 */ 145 public void processInput() { 146 mythread.start(); 147 } 148 149 /** 150 * Create a new pipelined parser from an existing one. 151 * 152 * @return A new pipelined parser that reads from the same input stream. 153 */ 154 protected Object clone() { 155 PipelinedMsgParser p = new PipelinedMsgParser(); 156 157 p.rawInputStream = this.rawInputStream; 158 p.sipMessageListener = this.sipMessageListener; 159 Thread mythread = new Thread(p); 160 mythread.setName("PipelineThread"); 161 return p; 162 } 163 164 /** 165 * Add a class that implements a SIPMessageListener interface whose methods 166 * get called * on successful parse and error conditons. 167 * 168 * @param mlistener 169 * a SIPMessageListener implementation that can react to correct 170 * and incorrect pars. 171 */ 172 173 public void setMessageListener(SIPMessageListener mlistener) { 174 sipMessageListener = mlistener; 175 } 176 177 /** 178 * read a line of input (I cannot use buffered reader because we may need to 179 * switch encodings mid-stream! 180 */ 181 private String readLine(InputStream inputStream) throws IOException { 182 StringBuffer retval = new StringBuffer(""); 183 while (true) { 184 char ch; 185 int i = inputStream.read(); 186 if (i == -1) { 187 throw new IOException("End of stream"); 188 } else 189 ch = (char) i; 190 // reduce the available read size by 1 ("size" of a char). 191 if (this.maxMessageSize > 0) { 192 this.sizeCounter--; 193 if (this.sizeCounter <= 0) 194 throw new IOException("Max size exceeded!"); 195 } 196 if (ch != '\r') 197 retval.append(ch); 198 if (ch == '\n') { 199 break; 200 } 201 } 202 return retval.toString(); 203 } 204 205 /** 206 * This is input reading thread for the pipelined parser. You feed it input 207 * through the input stream (see the constructor) and it calls back an event 208 * listener interface for message processing or error. It cleans up the 209 * input - dealing with things like line continuation 210 */ 211 public void run() { 212 213 Pipeline inputStream = this.rawInputStream; 214 // inputStream = new MyFilterInputStream(this.rawInputStream); 215 // I cannot use buffered reader here because we may need to switch 216 // encodings to read the message body. 217 try { 218 while (true) { 219 this.sizeCounter = this.maxMessageSize; 220 // this.messageSize = 0; 221 StringBuffer inputBuffer = new StringBuffer(); 222 223 if (Debug.parserDebug) 224 Debug.println("Starting parse!"); 225 226 String line1; 227 String line2 = null; 228 229 while (true) { 230 try { 231 line1 = readLine(inputStream); 232 // ignore blank lines. 233 if (line1.equals("\n")) { 234 if (Debug.parserDebug) { 235 Debug.println("Discarding blank line. "); 236 } 237 continue; 238 } else 239 break; 240 } catch (IOException ex) { 241 Debug.printStackTrace(ex); 242 this.rawInputStream.stopTimer(); 243 return; 244 245 } 246 } 247 248 inputBuffer.append(line1); 249 // Guard against bad guys. 250 this.rawInputStream.startTimer(); 251 252 Debug.println("Reading Input Stream"); 253 while (true) { 254 try { 255 line2 = readLine(inputStream); 256 inputBuffer.append(line2); 257 if (line2.trim().equals("")) 258 break; 259 } catch (IOException ex) { 260 this.rawInputStream.stopTimer(); 261 Debug.printStackTrace(ex); 262 return; 263 264 } 265 } 266 267 // Stop the timer that will kill the read. 268 this.rawInputStream.stopTimer(); 269 inputBuffer.append(line2); 270 StringMsgParser smp = new StringMsgParser(sipMessageListener); 271 smp.readBody = false; 272 SIPMessage sipMessage = null; 273 274 try { 275 if (Debug.debug) { 276 Debug.println("About to parse : " + inputBuffer.toString()); 277 } 278 sipMessage = smp.parseSIPMessage(inputBuffer.toString()); 279 if (sipMessage == null) { 280 this.rawInputStream.stopTimer(); 281 continue; 282 } 283 } catch (ParseException ex) { 284 // Just ignore the parse exception. 285 Debug.logError("Detected a parse error", ex); 286 continue; 287 } 288 289 if (Debug.debug) { 290 Debug.println("Completed parsing message"); 291 } 292 ContentLength cl = (ContentLength) sipMessage 293 .getContentLength(); 294 int contentLength = 0; 295 if (cl != null) { 296 contentLength = cl.getContentLength(); 297 } else { 298 contentLength = 0; 299 } 300 301 if (Debug.debug) { 302 Debug.println("contentLength " + contentLength); 303 } 304 305 if (contentLength == 0) { 306 sipMessage.removeContent(); 307 } else if (maxMessageSize == 0 308 || contentLength < this.sizeCounter) { 309 byte[] message_body = new byte[contentLength]; 310 int nread = 0; 311 while (nread < contentLength) { 312 // Start my starvation timer. 313 // This ensures that the other end 314 // writes at least some data in 315 // or we will close the pipe from 316 // him. This prevents DOS attack 317 // that takes up all our connections. 318 this.rawInputStream.startTimer(); 319 try { 320 321 int readlength = inputStream.read(message_body, 322 nread, contentLength - nread); 323 if (readlength > 0) { 324 nread += readlength; 325 } else { 326 break; 327 } 328 } catch (IOException ex) { 329 Debug.logError("Exception Reading Content",ex); 330 break; 331 } finally { 332 // Stop my starvation timer. 333 this.rawInputStream.stopTimer(); 334 } 335 } 336 sipMessage.setMessageContent(message_body); 337 } 338 // Content length too large - process the message and 339 // return error from there. 340 if (sipMessageListener != null) { 341 try { 342 sipMessageListener.processMessage(sipMessage); 343 } catch (Exception ex) { 344 // fatal error in processing - close the 345 // connection. 346 break; 347 } 348 } 349 } 350 } finally { 351 try { 352 inputStream.close(); 353 } catch (IOException e) { 354 InternalErrorHandler.handleException(e); 355 } 356 } 357 } 358 359 public void close() { 360 try { 361 this.rawInputStream.close(); 362 } catch (IOException ex) { 363 // Ignore. 364 } 365 } 366 } 367 /* 368 * $Log: PipelinedMsgParser.java,v $ 369 * Revision 1.23 2009/08/16 17:28:28 mranga 370 * Issue number: 208 371 * Obtained from: 372 * Submitted by: 373 * Reviewed by: 374 * 375 * Add authentication mechanism that uses H(username:domain:password) 376 * 377 * Revision 1.22 2009/07/17 18:58:02 emcho 378 * Converts indentation tabs to spaces so that we have a uniform indentation policy in the whole project. 379 * 380 * Revision 1.21 2008/05/24 04:10:01 mranga 381 * 382 * Issue number: 158 383 * Obtained from: 384 * Submitted by: 385 * Reviewed by: mranga 386 * 387 * Deliver tx timeout for Canceled INVITE. Fix pipeline thread exit. 388 * 389 * Revision 1.20 2008/05/22 19:38:07 jbemmel 390 * Fix for issue 149: the logic wasn't always closing the internal socket pipe, 391 * causing the pipe reader thread to block indefinitely 392 * 393 * Repeatedly starting/stopping the stack then gives hanging threads 394 * Revision 1.19 2007/01/28 13:06:21 mranga 395 * Issue number: 99 Obtained from: Submitted by: Reviewed by: mranga 396 * 397 * Fixed PRACK handling null pointer exception (for proxy case) and cleanup of 398 * unused variables. 399 * 400 * CVS: ---------------------------------------------------------------------- 401 * CVS: Issue number: CVS: If this change addresses one or more issues, CVS: 402 * then enter the issue number(s) here. CVS: Obtained from: CVS: If this change 403 * has been taken from another system, CVS: then name the system in this line, 404 * otherwise delete it. CVS: Submitted by: CVS: If this code has been 405 * contributed to the project by someone else; i.e., CVS: they sent us a patch 406 * or a set of diffs, then include their name/email CVS: address here. If this 407 * is your work then delete this line. CVS: Reviewed by: CVS: If we are doing 408 * pre-commit code reviews and someone else has CVS: reviewed your changes, 409 * include their name(s) here. CVS: If you have not had it reviewed then delete 410 * this line. 411 * 412 * Revision 1.18 2006/07/13 09:02:10 mranga Issue number: Obtained from: 413 * Submitted by: jeroen van bemmel Reviewed by: mranga Moved some changes from 414 * jain-sip-1.2 to java.net 415 * 416 * CVS: ---------------------------------------------------------------------- 417 * CVS: Issue number: CVS: If this change addresses one or more issues, CVS: 418 * then enter the issue number(s) here. CVS: Obtained from: CVS: If this change 419 * has been taken from another system, CVS: then name the system in this line, 420 * otherwise delete it. CVS: Submitted by: CVS: If this code has been 421 * contributed to the project by someone else; i.e., CVS: they sent us a patch 422 * or a set of diffs, then include their name/email CVS: address here. If this 423 * is your work then delete this line. CVS: Reviewed by: CVS: If we are doing 424 * pre-commit code reviews and someone else has CVS: reviewed your changes, 425 * include their name(s) here. CVS: If you have not had it reviewed then delete 426 * this line. 427 * 428 * Revision 1.4 2006/06/19 06:47:27 mranga javadoc fixups 429 * 430 * Revision 1.3 2006/06/17 10:18:14 mranga Added some synchronization to the 431 * sequence number checking. Small javadoc fixups 432 * 433 * Revision 1.2 2006/06/16 15:26:28 mranga Added NIST disclaimer to all public 434 * domain files. Clean up some javadoc. Fixed a leak 435 * 436 * Revision 1.1.1.1 2005/10/04 17:12:35 mranga 437 * 438 * Import 439 * 440 * 441 * Revision 1.16 2004/11/30 23:28:14 mranga Issue number: 44 Submitted by: Rob 442 * Daugherty Reviewed by: M. Ranganathan 443 * 444 * TCP Pipelining truncates content when other end of pipe is closed. 445 * 446 * Revision 1.15 2004/05/30 18:55:56 mranga Reviewed by: mranga Move to timers 447 * and eliminate the Transaction scanner Thread to improve scalability and 448 * reduce cpu usage. 449 * 450 * Revision 1.14 2004/05/16 14:13:22 mranga Reviewed by: mranga Fixed the 451 * use-count issue reported by Peter Parnes. Added property to prevent against 452 * content-length dos attacks. 453 * 454 * Revision 1.13 2004/03/19 04:22:22 mranga Reviewed by: mranga Added IO Pacing 455 * for long writes - split write into chunks and flush after each chunk to avoid 456 * socket back pressure. 457 * 458 * Revision 1.12 2004/03/18 22:01:19 mranga Reviewed by: mranga Get rid of the 459 * PipedInputStream from pipelined parser to avoid a copy. 460 * 461 * Revision 1.11 2004/03/07 22:25:23 mranga Reviewed by: mranga Added a new 462 * configuration parameter that instructs the stack to drop a server connection 463 * after server transaction termination set 464 * gov.nist.javax.sip.CACHE_SERVER_CONNECTIONS=false for this Default behavior 465 * is true. 466 * 467 * Revision 1.10 2004/02/29 15:32:58 mranga Reviewed by: mranga bug fixes on 468 * limiting the max message size. 469 * 470 * Revision 1.9 2004/02/29 00:46:34 mranga Reviewed by: mranga Added new 471 * configuration property to limit max message size for TCP transport. The 472 * property is gov.nist.javax.sip.MAX_MESSAGE_SIZE 473 * 474 * Revision 1.8 2004/02/25 21:43:03 mranga Reviewed by: mranga Added a couple of 475 * todo's and removed some debug printlns that could slow code down by a bit. 476 * 477 * Revision 1.7 2004/02/25 20:52:46 mranga Reviewed by: mranga Fix TCP transport 478 * so messages in excess of 8192 bytes are accepted. 479 * 480 * Revision 1.6 2004/01/22 18:39:41 mranga Reviewed by: M. Ranganathan Moved the 481 * ifdef SIMULATION and associated tags to the first column so Prep preprocessor 482 * can deal with them. 483 * 484 * Revision 1.5 2004/01/22 14:23:45 mranga Reviewed by: mranga Fixed some minor 485 * formatting issues. 486 * 487 * Revision 1.4 2004/01/22 13:26:31 sverker Issue number: Obtained from: 488 * Submitted by: sverker Reviewed by: mranga 489 * 490 * Major reformat of code to conform with style guide. Resolved compiler and 491 * javadoc warnings. Added CVS tags. 492 * 493 * CVS: ---------------------------------------------------------------------- 494 * CVS: Issue number: CVS: If this change addresses one or more issues, CVS: 495 * then enter the issue number(s) here. CVS: Obtained from: CVS: If this change 496 * has been taken from another system, CVS: then name the system in this line, 497 * otherwise delete it. CVS: Submitted by: CVS: If this code has been 498 * contributed to the project by someone else; i.e., CVS: they sent us a patch 499 * or a set of diffs, then include their name/email CVS: address here. If this 500 * is your work then delete this line. CVS: Reviewed by: CVS: If we are doing 501 * pre-commit code reviews and someone else has CVS: reviewed your changes, 502 * include their name(s) here. CVS: If you have not had it reviewed then delete 503 * this line. 504 * 505 */ 506