001: /*
002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
003: * Reserved. Use is subject to license terms.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025: /*
026: * TCPMessageChannel.java
027: *
028: * Created on September 3, 2002, 3:47 PM
029: */
030:
031: package gov.nist.siplite.stack;
032:
033: import gov.nist.siplite.header.*;
034: import gov.nist.siplite.parser.*;
035: import gov.nist.siplite.message.*;
036: import gov.nist.siplite.SIPConstants;
037: import gov.nist.core.*;
038: import javax.microedition.io.*;
039: import java.io.*;
040:
041: import com.sun.midp.log.Logging;
042: import com.sun.midp.log.LogChannels;
043:
044: /**
045: * Handle a TCP stream connection.
046: *
047: * @version 1.0
048: */
049: public class TCPMessageChannel extends MessageChannel implements
050: SIPMessageListener {
051: /** Stream connection handle. */
052: private SocketConnection mySock;
053: /** Message parser handle. */
054: private PipelinedMsgParser myParser;
055: /** Input stream for client thread. */
056: private InputStream myClientInputStream;
057: /** Output stream for client thread. */
058: private OutputStream myClientOutputStream;
059: /** Current SIP message stack. */
060: private SIPMessageStack stack;
061: /** Local address. */
062: private String myAddress;
063: /** Local port number. */
064: private int myPort;
065: /** Remote address. */
066: private String peerAddress;
067: /** Remote port number. */
068: private int peerPort;
069: /** Remote transport type. */
070: private String peerProtocol;
071: /** Indicates channel is shutting down. */
072: private boolean exitFlag;
073: /** Number of transaction that uses this channel. */
074: private int useCounter;
075:
076: /**
077: * Constructor - gets called from the SIPMessageStack class with a socket
078: * on accepting a new client. All the processing of the message is
079: * done here with the stack being freed up to handle new connections.
080: * The sock input is the socket that is returned from the accept.
081: * Global data that is shared by all threads is accessible in the Server
082: * structure.
083: * @param sock Socket from which to read and write messages. The socket
084: * is already connected (was created as a result of an accept).
085: * @param sipStack Ptr to SIP Stack
086: * @param msgProcessor handler for TCP message communication
087: */
088: protected TCPMessageChannel(SocketConnection sock,
089: SIPMessageStack sipStack, TCPMessageProcessor msgProcessor)
090: throws IOException {
091: mySock = sock;
092: myClientInputStream = sock.openInputStream();
093: myClientOutputStream = sock.openOutputStream();
094: stack = sipStack;
095: messageProcessor = msgProcessor;
096: // peerAddress will be updated when
097: // first Request packet is received
098: // see processMessage(...)
099: peerAddress = sock.getAddress();
100: peerPort = sock.getPort();
101: myAddress = sock.getLocalAddress();
102: myPort = sock.getLocalPort();
103: start();
104: incrementUseCounter();
105: }
106:
107: /**
108: * Constructor - connects to the given inet address.
109: * @param targetHostPort inet address to connect to.
110: * @param sipStack is the sip stack from which we are created.
111: * @param msgProcessor TCP message processor
112: * @throws IOException if we cannot connect.
113: */
114: protected TCPMessageChannel(HostPort targetHostPort,
115: SIPMessageStack sipStack, TCPMessageProcessor msgProcessor)
116: throws IOException {
117: stack = sipStack;
118: messageProcessor = msgProcessor;
119: myAddress = sipStack.getHostAddress();
120: peerAddress = targetHostPort.getHost().getHostname();
121: peerPort = targetHostPort.getPort();
122: makeSocket(peerAddress, peerPort);
123: start();
124: incrementUseCounter();
125: }
126:
127: /**
128: * Creates a socket connection.
129: * @param host Host name
130: * @param port Port number
131: * @exception IOException if the socket can not be created.
132: */
133: private void makeSocket(String host, int port) throws IOException {
134: if (host == null || host.length() == 0 || port < 0) {
135: throw new IOException("Invalid hostname or port number");
136: }
137:
138: String name = "//" + host + ":" + port;
139:
140: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
141: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
142: "Opening outbound socket connection :" + host + ":"
143: + port);
144: }
145:
146: /*
147: * Original NIST method for opening the socket connection
148: * has been replaced by direct calls to instantiate the protocol
149: * handler, in order to pass the security token for use of lower
150: * level transport connection.
151: * Original NIST sequence is :
152: *
153: * socket = (SocketConnection)Connector.open(name);
154: *
155: */
156: com.sun.midp.io.j2me.socket.Protocol conn = new com.sun.midp.io.j2me.socket.Protocol();
157: try {
158: mySock = (SocketConnection) conn.openPrim(stack
159: .getSecurityToken(), name);
160: } catch (ConnectionNotFoundException ex) {
161: throw new IOException("Can't connect to " + name);
162: }
163: myClientInputStream = mySock.openInputStream();
164: myClientOutputStream = mySock.openOutputStream();
165: }
166:
167: /**
168: * Returns "true" as this is a reliable transport.
169: * @return true if reliable stream transport
170: */
171: public boolean isReliable() {
172: return true;
173: }
174:
175: /**
176: * Closes the message channel.
177: */
178: synchronized public void close() {
179: if (0 != useCounter) {
180: useCounter--;
181: }
182: if (0 == useCounter) {
183: exit();
184: if (null != messageProcessor) {
185: ((TCPMessageProcessor) messageProcessor)
186: .notifyClose(this );
187: }
188: }
189: }
190:
191: /**
192: * Closes the message channel regardless whether it is used or not.
193: */
194: synchronized protected void exit() {
195: useCounter = 0;
196: exitFlag = true;
197: shutDownConnection();
198: // allow gc to collect MP
199: messageProcessor = null;
200: }
201:
202: /**
203: * Gets my SIP Stack.
204: * @return The SIP Stack for this message channel.
205: */
206: public SIPMessageStack getSIPStack() {
207: return stack;
208: }
209:
210: /**
211: * Gets the transport string.
212: * @return "tcp" in this case.
213: */
214: public String getTransport() {
215: return SIPConstants.TRANSPORT_TCP;
216: }
217:
218: /**
219: * Gets the address of the client that sent the data to us.
220: * @return Address of the client that sent us data
221: * that resulted in this channel being
222: * created.
223: */
224: public String getPeerAddress() {
225: return peerAddress;
226: }
227:
228: /**
229: * Sends message to whoever is connected to us.
230: * Uses the topmost via address to send to.
231: * @param msg is the message to send.
232: */
233: synchronized private void sendMessage(byte[] msg)
234: throws IOException {
235: if (exitFlag) {
236: return;
237: }
238:
239: if (mySock == null) {
240: reConnect();
241: }
242:
243: myClientOutputStream.write(msg);
244: }
245:
246: /**
247: * Returns a formatted message to the client.
248: * We try to re-connect with the peer on the other end if possible.
249: * @param sipMessage Message to send.
250: * @throws IOException If there is an error sending the message
251: */
252: public void sendMessage(Message sipMessage) throws IOException {
253: if (sipMessage == null) {
254: throw new NullPointerException("null arg!");
255: }
256:
257: byte[] msg = sipMessage.encodeAsBytes();
258: long time = System.currentTimeMillis();
259:
260: sendMessage(msg);
261:
262: if (ServerLog.needsLogging(ServerLog.TRACE_MESSAGES)) {
263: logMessage(sipMessage, peerAddress, peerPort, time);
264: }
265: }
266:
267: /**
268: * Sends a message to a specified address.
269: * @param message Pre-formatted message to send.
270: * @param receiverAddress Address to send it to.
271: * @param receiverPort Receiver port.
272: * @throws IOException If there is a problem connecting or sending.
273: */
274: public void sendMessage(byte message[], String receiverAddress,
275: int receiverPort) throws IOException,
276: IllegalArgumentException {
277: if (message == null || receiverAddress == null) {
278: throw new IllegalArgumentException("Null argument");
279: }
280:
281: if (!receiverAddress.equals(peerAddress)
282: || peerPort != receiverPort) {
283: throw new IOException(
284: "This channel is bound to different peer");
285: }
286:
287: sendMessage(message);
288: }
289:
290: /**
291: * Exception processor for exceptions detected from the application.
292: * @param ex The exception that was generated.
293: */
294: public void handleException(SIPServerException ex) {
295: // Return a parse error message to the client on the other end
296: // if he is still alive.
297: int rc = ex.getRC();
298: String msgString = ex.getMessage();
299: if (rc != 0) {
300: // Do we have a valid Return code ? --
301: // in this case format the message.
302: Request request = (Request) ex.getSIPMessage();
303: Response response = request.createResponse(rc);
304: try {
305: sendMessage(response);
306: } catch (IOException ioex) {
307: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
308: Logging.report(Logging.ERROR,
309: LogChannels.LC_JSR180, ioex.getMessage());
310: }
311: }
312: } else {
313: // Otherwise, message is already formatted --
314: // just return it
315: try {
316: sendMessage(msgString.getBytes());
317: } catch (IOException ioex) {
318: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
319: Logging.report(Logging.ERROR,
320: LogChannels.LC_JSR180, ioex.getMessage());
321: }
322: }
323: }
324: }
325:
326: /**
327: * Exception processor for exceptions detected from the parser. (This
328: * is invoked by the parser when an error is detected).
329: * @param ex parse exception detected by the parser.
330: * @param sipMessage message that incurred the error.
331: * @param hdrClass header parsing class
332: * @param header header that caused the error.
333: * @param message descriptive text for exception
334: * @throws ParseException Thrown if we want to reject the message.
335: */
336: public void handleException(ParseException ex, Message sipMessage,
337: Class hdrClass, String header, String message)
338: throws ParseException {
339:
340: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
341: Logging.report(Logging.ERROR, LogChannels.LC_JSR180, ex
342: .getMessage());
343: }
344:
345: // Log the bad message for later reference.
346: if (hdrClass.equals(FromHeader.clazz)
347: || hdrClass.equals(ToHeader.clazz)
348: || hdrClass.equals(CSeqHeader.clazz)
349: || hdrClass.equals(ViaHeader.clazz)
350: || hdrClass.equals(CallIdHeader.clazz)
351: || hdrClass.equals(RequestLine.clazz)
352: || hdrClass.equals(StatusLine.clazz)) {
353: stack.logBadMessage(message);
354: throw ex;
355: } else {
356: sipMessage.addUnparsed(header);
357: }
358:
359: }
360:
361: /**
362: * Gets invoked by the parser as a callback on successful message
363: * parsing (i.e. no parser errors).
364: * @param sipMessage Mesage to process (this calls the application
365: * for processing the message).
366: */
367: public void processMessage(Message sipMessage) {
368: if (sipMessage.getFromHeader() == null
369: || sipMessage.getTo() == null
370: || sipMessage.getCallId() == null
371: || sipMessage.getCSeqHeader() == null
372: || sipMessage.getViaHeaders() == null) {
373: String badmsg = sipMessage.encode();
374:
375: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
376: ServerLog.logMessage("bad message " + badmsg);
377: ServerLog.logMessage(">>> Dropped Bad Msg");
378: }
379:
380: stack.logBadMessage(badmsg);
381:
382: return;
383: }
384:
385: ViaList viaList = sipMessage.getViaHeaders();
386:
387: // For a request
388: // first via header tells where the message is coming from.
389: // For response, this has already been recorded in the outgoing
390: // message.
391: long receptionTime = 0;
392:
393: if (sipMessage instanceof Request) {
394: ViaHeader v = (ViaHeader) viaList.first();
395: if (v.hasPort()) {
396: peerPort = v.getPort();
397: } else {
398: peerPort = SIPConstants.DEFAULT_NONTLS_PORT;
399: }
400: peerProtocol = v.getTransport();
401:
402: // System.out.println("receiver address = " + receiverAddress);
403:
404: // Foreach part of the request header, fetch it and process it
405: receptionTime = System.currentTimeMillis();
406:
407: // This is a request - process the request.
408: Request sipRequest = (Request) sipMessage;
409:
410: // Create a new sever side request processor for this
411: // message and let it handle the rest.
412:
413: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
414: Logging.report(Logging.INFORMATION,
415: LogChannels.LC_JSR180,
416: "----Processing Message---");
417: }
418:
419: SIPServerRequestInterface sipServerRequest = stack
420: .newSIPServerRequest(sipRequest, this );
421: try {
422: sipServerRequest.processRequest(sipRequest, this );
423:
424: ServerLog.logMessage(sipMessage, sipRequest
425: .getViaHost()
426: + ":" + sipRequest.getViaPort(), stack
427: .getHostAddress()
428: + ":" + stack.getPort(this .getTransport()),
429: false, receptionTime);
430:
431: } catch (SIPServerException ex) {
432: ServerLog.logMessage(sipMessage, sipRequest
433: .getViaHost()
434: + ":" + sipRequest.getViaPort(), stack
435: .getHostAddress()
436: + ":" + stack.getPort(this .getTransport()), ex
437: .getMessage(), false, receptionTime);
438: handleException(ex);
439: }
440: } else {
441: // This is a response message - process it.
442: Response sipResponse = (Response) sipMessage;
443: SIPServerResponseInterface sipServerResponse = stack
444: .newSIPServerResponse(sipResponse, this );
445:
446: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
447: Logging
448: .report(Logging.INFORMATION,
449: LogChannels.LC_JSR180,
450: "got a response interface "
451: + sipServerResponse);
452: }
453:
454: try {
455: sipServerResponse.processResponse(sipResponse, this );
456: } catch (SIPServerException ex) {
457: // An error occured processing the message -- just log it.
458: ServerLog.logMessage(sipMessage, getPeerAddress()
459: .toString()
460: + ":" + getPeerPort(), stack.getHostAddress()
461: + ":" + stack.getPort(this .getTransport()), ex
462: .getMessage(), false, receptionTime);
463: // Ignore errors while processing responses??
464: }
465: }
466: }
467:
468: /**
469: * This gets invoked when thread.start is called from the constructor.
470: * Implements a message loop - reading the tcp connection and processing
471: * messages until we are done or the other end has closed.
472: */
473: private void start() {
474: // Create a pipelined message parser to read and parse
475: // messages that we write out to him.
476: myParser = new PipelinedMsgParser(this , myClientInputStream);
477: // Enable the flag to parse message content.
478: // Start running the parser thread.
479: myParser.processInput();
480:
481: }
482:
483: /**
484: * Increments the number of user of this channel.
485: */
486: synchronized protected void incrementUseCounter() {
487: if (!exitFlag) {
488: useCounter++;
489: }
490: }
491:
492: /**
493: * Called when the pipelined parser cannot read input because the
494: * other end closed the connection.
495: */
496: public void handleIOException() {
497: if (!exitFlag) {
498: shutDownConnection();
499: }
500: }
501:
502: /**
503: * Closes all input and output stream and socket.
504: */
505: synchronized private void shutDownConnection() {
506: try {
507: if (null != myClientInputStream) {
508: myClientInputStream.close();
509: }
510: } catch (IOException ioe) {
511: // intentionally ignored
512: }
513:
514: try {
515: if (null != myClientOutputStream) {
516: myClientOutputStream.close();
517: }
518:
519: } catch (IOException ioe) {
520: // intentionally ignored
521: }
522:
523: try {
524: if (null != mySock) {
525: mySock.close();
526: }
527: } catch (IOException ioe) {
528: // intentionally ignored
529: }
530: // null mySock indicates closed connection
531: // see sendMessage()
532: mySock = null;
533: }
534:
535: /**
536: * Reconnect to the server.
537: * @exception IOException if the connection can not be established
538: */
539: private void reConnect() throws IOException {
540: shutDownConnection();
541: makeSocket(peerAddress, peerPort);
542: myParser = new PipelinedMsgParser(this , myClientInputStream);
543: myParser.processInput();
544: }
545:
546: /**
547: * Equals predicate.
548: * @param other is the other object to compare ourselves to for equals
549: * @return true if object matches
550: */
551: public boolean equals(Object other) {
552: if (!this .getClass().equals(other.getClass()))
553: return false;
554: else {
555: TCPMessageChannel that = (TCPMessageChannel) other;
556: if (this .mySock != that.mySock)
557: return false;
558: else
559: return true;
560: }
561: }
562:
563: /**
564: * Gets an identifying key. This key is used to cache the connection
565: * and re-use it if necessary.
566: * @return the identifying key
567: */
568: public String getKey() {
569: return getKey(peerAddress, peerPort, SIPConstants.TRANSPORT_TCP);
570: }
571:
572: /**
573: * Gets the host to assign to outgoing messages.
574: * @return the host to assign to the via header.
575: */
576: public String getViaHost() {
577: return myAddress;
578: }
579:
580: /**
581: * Gets the port for outgoing messages sent from the channel.
582: * @return the port to assign to the via header.
583: */
584: public int getViaPort() {
585: return myPort;
586: }
587:
588: /**
589: * Gets the port of the peer to whom we are sending messages.
590: * @return the peer port.
591: */
592: public int getPeerPort() {
593: return peerPort;
594: }
595:
596: /**
597: * TCP Is not a secure protocol.
598: * @return always false
599: */
600: public boolean isSecure() {
601: return false;
602: }
603: }
|