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 ******************************************************************************/
29package gov.nist.javax.sip.stack;
30
31import gov.nist.javax.sip.header.*;
32import gov.nist.javax.sip.message.*;
33import gov.nist.javax.sip.parser.*;
34import gov.nist.core.*;
35import java.net.*;
36import java.io.*;
37import java.text.ParseException;
38import java.util.TimerTask;
39
40import javax.sip.address.Hop;
41
42/*
43 * Ahmet Uyar <auyar@csit.fsu.edu>sent in a bug report for TCP operation of the JAIN sipStack.
44 * Niklas Uhrberg suggested that a mechanism be added to limit the number of simultaneous open
45 * connections. The TLS Adaptations were contributed by Daniel Martinez. Hagai Sela contributed a
46 * bug fix for symmetric nat. Jeroen van Bemmel added compensation for buggy clients ( Microsoft
47 * RTC clients ). Bug fixes by viswashanti.kadiyala@antepo.com, Joost Yervante Damand
48 */
49
50/**
51 * This is a stack abstraction for TCP connections. This abstracts a stream of parsed messages.
52 * The SIP sipStack starts this from the main SIPStack class for each connection that it accepts.
53 * It starts a message parser in its own thread and talks to the message parser via a pipe. The
54 * message parser calls back via the parseError or processMessage functions that are defined as
55 * part of the SIPMessageListener interface.
56 *
57 * @see gov.nist.javax.sip.parser.PipelinedMsgParser
58 *
59 *
60 * @author M. Ranganathan <br/>
61 *
62 * @version 1.2 $Revision: 1.59 $ $Date: 2009/11/20 04:45:53 $
63 */
64public class TCPMessageChannel extends MessageChannel implements SIPMessageListener, Runnable,
65        RawMessageChannel {
66
67    private Socket mySock;
68
69    private PipelinedMsgParser myParser;
70
71    protected InputStream myClientInputStream; // just to pass to thread.
72
73    protected OutputStream myClientOutputStream;
74
75    protected String key;
76
77    protected boolean isCached;
78
79    protected boolean isRunning;
80
81    private Thread mythread;
82
83    protected SIPTransactionStack sipStack;
84
85    protected String myAddress;
86
87    protected int myPort;
88
89    protected InetAddress peerAddress;
90
91    protected int peerPort;
92
93    protected String peerProtocol;
94
95    // Incremented whenever a transaction gets assigned
96    // to the message channel and decremented when
97    // a transaction gets freed from the message channel.
98    // protected int useCount;
99
100    private TCPMessageProcessor tcpMessageProcessor;
101
102    protected TCPMessageChannel(SIPTransactionStack sipStack) {
103        this.sipStack = sipStack;
104
105    }
106
107    /**
108     * Constructor - gets called from the SIPStack class with a socket on accepting a new client.
109     * All the processing of the message is done here with the sipStack being freed up to handle
110     * new connections. The sock input is the socket that is returned from the accept. Global data
111     * that is shared by all threads is accessible in the Server structure.
112     *
113     * @param sock Socket from which to read and write messages. The socket is already connected
114     *        (was created as a result of an accept).
115     *
116     * @param sipStack Ptr to SIP Stack
117     */
118
119    protected TCPMessageChannel(Socket sock, SIPTransactionStack sipStack,
120            TCPMessageProcessor msgProcessor) throws IOException {
121
122        if (sipStack.isLoggingEnabled()) {
123            sipStack.getStackLogger().logDebug("creating new TCPMessageChannel ");
124            sipStack.getStackLogger().logStackTrace();
125        }
126        mySock = sock;
127        peerAddress = mySock.getInetAddress();
128        myAddress = msgProcessor.getIpAddress().getHostAddress();
129        myClientInputStream = mySock.getInputStream();
130        myClientOutputStream = mySock.getOutputStream();
131        mythread = new Thread(this);
132        mythread.setDaemon(true);
133        mythread.setName("TCPMessageChannelThread");
134        // Stash away a pointer to our sipStack structure.
135        this.sipStack = sipStack;
136        this.peerPort = mySock.getPort();
137
138        this.tcpMessageProcessor = msgProcessor;
139        this.myPort = this.tcpMessageProcessor.getPort();
140        // Bug report by Vishwashanti Raj Kadiayl
141        super.messageProcessor = msgProcessor;
142        // Can drop this after response is sent potentially.
143        mythread.start();
144    }
145
146    /**
147     * Constructor - connects to the given inet address. Acknowledgement -- Lamine Brahimi (IBM
148     * Zurich) sent in a bug fix for this method. A thread was being uncessarily created.
149     *
150     * @param inetAddr inet address to connect to.
151     * @param sipStack is the sip sipStack from which we are created.
152     * @throws IOException if we cannot connect.
153     */
154    protected TCPMessageChannel(InetAddress inetAddr, int port, SIPTransactionStack sipStack,
155            TCPMessageProcessor messageProcessor) throws IOException {
156        if (sipStack.isLoggingEnabled()) {
157            sipStack.getStackLogger().logDebug("creating new TCPMessageChannel ");
158            sipStack.getStackLogger().logStackTrace();
159        }
160        this.peerAddress = inetAddr;
161        this.peerPort = port;
162        this.myPort = messageProcessor.getPort();
163        this.peerProtocol = "TCP";
164        this.sipStack = sipStack;
165        this.tcpMessageProcessor = messageProcessor;
166        this.myAddress = messageProcessor.getIpAddress().getHostAddress();
167        // Bug report by Vishwashanti Raj Kadiayl
168        this.key = MessageChannel.getKey(peerAddress, peerPort, "TCP");
169        super.messageProcessor = messageProcessor;
170
171    }
172
173    /**
174     * Returns "true" as this is a reliable transport.
175     */
176    public boolean isReliable() {
177        return true;
178    }
179
180    /**
181     * Close the message channel.
182     */
183    public void close() {
184        try {
185            if (mySock != null) {
186                mySock.close();
187                mySock = null;
188            }
189            if (sipStack.isLoggingEnabled())
190                sipStack.getStackLogger().logDebug("Closing message Channel " + this);
191        } catch (IOException ex) {
192            if (sipStack.isLoggingEnabled())
193                sipStack.getStackLogger().logDebug("Error closing socket " + ex);
194        }
195    }
196
197    /**
198     * Get my SIP Stack.
199     *
200     * @return The SIP Stack for this message channel.
201     */
202    public SIPTransactionStack getSIPStack() {
203        return sipStack;
204    }
205
206    /**
207     * get the transport string.
208     *
209     * @return "tcp" in this case.
210     */
211    public String getTransport() {
212        return "TCP";
213    }
214
215    /**
216     * get the address of the client that sent the data to us.
217     *
218     * @return Address of the client that sent us data that resulted in this channel being
219     *         created.
220     */
221    public String getPeerAddress() {
222        if (peerAddress != null) {
223            return peerAddress.getHostAddress();
224        } else
225            return getHost();
226    }
227
228    protected InetAddress getPeerInetAddress() {
229        return peerAddress;
230    }
231
232    public String getPeerProtocol() {
233        return this.peerProtocol;
234    }
235
236    /**
237     * Send message to whoever is connected to us. Uses the topmost via address to send to.
238     *
239     * @param msg is the message to send.
240     * @param retry
241     */
242    private void sendMessage(byte[] msg, boolean retry) throws IOException {
243
244        /*
245         * Patch from kircuv@dev.java.net (Issue 119 ) This patch avoids the case where two
246         * TCPMessageChannels are now pointing to the same socket.getInputStream().
247         *
248         * JvB 22/5 removed
249         */
250       // Socket s = this.sipStack.ioHandler.getSocket(IOHandler.makeKey(
251       // this.peerAddress, this.peerPort));
252        Socket sock = this.sipStack.ioHandler.sendBytes(this.messageProcessor.getIpAddress(),
253                this.peerAddress, this.peerPort, this.peerProtocol, msg, retry, this);
254
255        // Created a new socket so close the old one and stick the new
256        // one in its place but dont do this if it is a datagram socket.
257        // (could have replied via udp but received via tcp!).
258        // if (mySock == null && s != null) {
259        // this.uncache();
260        // } else
261        if (sock != mySock && sock != null) {
262            try {
263                if (mySock != null)
264                    mySock.close();
265            } catch (IOException ex) {
266            }
267            mySock = sock;
268            this.myClientInputStream = mySock.getInputStream();
269            this.myClientOutputStream = mySock.getOutputStream();
270            Thread thread = new Thread(this);
271            thread.setDaemon(true);
272            thread.setName("TCPMessageChannelThread");
273            thread.start();
274        }
275
276    }
277
278    /**
279     * Return a formatted message to the client. We try to re-connect with the peer on the other
280     * end if possible.
281     *
282     * @param sipMessage Message to send.
283     * @throws IOException If there is an error sending the message
284     */
285    public void sendMessage(SIPMessage sipMessage) throws IOException {
286        byte[] msg = sipMessage.encodeAsBytes(this.getTransport());
287
288        long time = System.currentTimeMillis();
289
290        // JvB: also retry for responses, if the connection is gone we should
291        // try to reconnect
292        this.sendMessage(msg, /* sipMessage instanceof SIPRequest */true);
293
294        if (this.sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES))
295            logMessage(sipMessage, peerAddress, peerPort, time);
296    }
297
298    /**
299     * Send a message to a specified address.
300     *
301     * @param message Pre-formatted message to send.
302     * @param receiverAddress Address to send it to.
303     * @param receiverPort Receiver port.
304     * @throws IOException If there is a problem connecting or sending.
305     */
306    public void sendMessage(byte message[], InetAddress receiverAddress, int receiverPort,
307            boolean retry) throws IOException {
308        if (message == null || receiverAddress == null)
309            throw new IllegalArgumentException("Null argument");
310         Socket sock = this.sipStack.ioHandler.sendBytes(this.messageProcessor.getIpAddress(),
311                receiverAddress, receiverPort, "TCP", message, retry, this);
312        if (sock != mySock && sock != null) {
313            if (mySock != null) {
314                /*
315                 * Delay the close of the socket for some time in case it is being used.
316                 */
317                sipStack.getTimer().schedule(new TimerTask() {
318                    @Override
319                    public boolean cancel() {
320                        try {
321                            mySock.close();
322                            super.cancel();
323                        } catch (IOException ex) {
324
325                        }
326                        return true;
327                    }
328
329                    @Override
330                    public void run() {
331                        try {
332                            mySock.close();
333                        } catch (IOException ex) {
334
335                        }
336                    }
337                }, 8000);
338            }
339
340            mySock = sock;
341            this.myClientInputStream = mySock.getInputStream();
342            this.myClientOutputStream = mySock.getOutputStream();
343            // start a new reader on this end of the pipe.
344            Thread mythread = new Thread(this);
345            mythread.setDaemon(true);
346            mythread.setName("TCPMessageChannelThread");
347            mythread.start();
348        }
349
350    }
351
352    /**
353     * Exception processor for exceptions detected from the parser. (This is invoked by the parser
354     * when an error is detected).
355     *
356     * @param sipMessage -- the message that incurred the error.
357     * @param ex -- parse exception detected by the parser.
358     * @param header -- header that caused the error.
359     * @throws ParseException Thrown if we want to reject the message.
360     */
361    public void handleException(ParseException ex, SIPMessage sipMessage, Class hdrClass,
362            String header, String message) throws ParseException {
363        if (sipStack.isLoggingEnabled())
364            sipStack.getStackLogger().logException(ex);
365        // Log the bad message for later reference.
366        if ((hdrClass != null)
367                && (hdrClass.equals(From.class) || hdrClass.equals(To.class)
368                        || hdrClass.equals(CSeq.class) || hdrClass.equals(Via.class)
369                        || hdrClass.equals(CallID.class) || hdrClass.equals(RequestLine.class) || hdrClass
370                        .equals(StatusLine.class))) {
371            if (sipStack.isLoggingEnabled()) {
372                sipStack.getStackLogger().logDebug(
373                        "Encountered Bad Message \n" + sipMessage.toString());
374            }
375
376            // JvB: send a 400 response for requests (except ACK)
377            // Currently only UDP, @todo also other transports
378            String msgString = sipMessage.toString();
379            if (!msgString.startsWith("SIP/") && !msgString.startsWith("ACK ")) {
380
381                String badReqRes = createBadReqRes(msgString, ex);
382                if (badReqRes != null) {
383                    if (sipStack.isLoggingEnabled()) {
384                        sipStack.getStackLogger().logDebug("Sending automatic 400 Bad Request:");
385                        sipStack.getStackLogger().logDebug(badReqRes);
386                    }
387                    try {
388                        this.sendMessage(badReqRes.getBytes(), this.getPeerInetAddress(), this
389                                .getPeerPort(), false);
390                    } catch (IOException e) {
391                        this.sipStack.getStackLogger().logException(e);
392                    }
393                } else {
394                    if (sipStack.isLoggingEnabled()) {
395                        sipStack.getStackLogger().logDebug(
396                                "Could not formulate automatic 400 Bad Request");
397                    }
398                }
399            }
400
401            throw ex;
402        } else {
403            sipMessage.addUnparsed(header);
404        }
405    }
406
407    /**
408     * Gets invoked by the parser as a callback on successful message parsing (i.e. no parser
409     * errors).
410     *
411     * @param sipMessage Mesage to process (this calls the application for processing the
412     *        message).
413     */
414    public void processMessage(SIPMessage sipMessage) throws Exception {
415        try {
416            if (sipMessage.getFrom() == null
417                    || // sipMessage.getFrom().getTag()
418                    // == null ||
419                    sipMessage.getTo() == null || sipMessage.getCallId() == null
420                    || sipMessage.getCSeq() == null || sipMessage.getViaHeaders() == null) {
421                String badmsg = sipMessage.encode();
422                if (sipStack.isLoggingEnabled()) {
423                    sipStack.getStackLogger().logDebug(">>> Dropped Bad Msg");
424                    sipStack.getStackLogger().logDebug(badmsg);
425                }
426
427                return;
428            }
429
430            ViaList viaList = sipMessage.getViaHeaders();
431            // For a request
432            // first via header tells where the message is coming from.
433            // For response, this has already been recorded in the outgoing
434            // message.
435            if (sipMessage instanceof SIPRequest) {
436                Via v = (Via) viaList.getFirst();
437                Hop hop = sipStack.addressResolver.resolveAddress(v.getHop());
438                this.peerProtocol = v.getTransport();
439                try {
440                    this.peerAddress = mySock.getInetAddress();
441                    // Check to see if the received parameter matches
442                    // the peer address and tag it appropriately.
443
444                    // JvB: dont do this. It is both costly and incorrect
445                    // Must set received also when it is a FQDN, regardless
446                    // whether
447                    // it resolves to the correct IP address
448                    // InetAddress sentByAddress =
449                    // InetAddress.getByName(hop.getHost());
450                    // JvB: if sender added 'rport', must always set received
451                    if (v.hasParameter(Via.RPORT)
452                            || !hop.getHost().equals(this.peerAddress.getHostAddress())) {
453                        v.setParameter(Via.RECEIVED, this.peerAddress.getHostAddress());
454                    }
455                    // @@@ hagai
456                    // JvB: technically, may only do this when Via already
457                    // contains
458                    // rport
459                    v.setParameter(Via.RPORT, Integer.toString(this.peerPort));
460                } catch (java.text.ParseException ex) {
461                    InternalErrorHandler.handleException(ex, sipStack.getStackLogger());
462                }
463                // Use this for outgoing messages as well.
464                if (!this.isCached) {
465                    ((TCPMessageProcessor) this.messageProcessor).cacheMessageChannel(this);
466                    this.isCached = true;
467                    int remotePort = ((java.net.InetSocketAddress) mySock.getRemoteSocketAddress()).getPort();
468                    String key = IOHandler.makeKey(mySock.getInetAddress(), remotePort);
469                    sipStack.ioHandler.putSocket(key, mySock);
470                }
471            }
472
473
474            // Foreach part of the request header, fetch it and process it
475
476            long receptionTime = System.currentTimeMillis();
477
478            if (sipMessage instanceof SIPRequest) {
479                // This is a request - process the request.
480                SIPRequest sipRequest = (SIPRequest) sipMessage;
481                // Create a new sever side request processor for this
482                // message and let it handle the rest.
483
484                if (sipStack.isLoggingEnabled()) {
485                    sipStack.getStackLogger().logDebug("----Processing Message---");
486                }
487
488                // Check for reasonable size - reject message
489                // if it is too long.
490                if (this.sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES)) {
491                    sipStack.serverLogger.logMessage(sipMessage, this.getPeerHostPort().toString(),
492                            this.getMessageProcessor().getIpAddress().getHostAddress() + ":"
493                                    + this.getMessageProcessor().getPort(), false, receptionTime);
494
495                }
496
497                if (sipStack.getMaxMessageSize() > 0
498                        && sipRequest.getSize()
499                                + (sipRequest.getContentLength() == null ? 0 : sipRequest
500                                        .getContentLength().getContentLength()) > sipStack
501                                .getMaxMessageSize()) {
502                    SIPResponse sipResponse = sipRequest
503                            .createResponse(SIPResponse.MESSAGE_TOO_LARGE);
504                    byte[] resp = sipResponse.encodeAsBytes(this.getTransport());
505                    this.sendMessage(resp, false);
506                    throw new Exception("Message size exceeded");
507                }
508
509                ServerRequestInterface sipServerRequest = sipStack.newSIPServerRequest(
510                        sipRequest, this);
511
512                if (sipServerRequest != null) {
513                    try {
514                        sipServerRequest.processRequest(sipRequest, this);
515                    } finally {
516                        if (sipServerRequest instanceof SIPTransaction) {
517                            SIPServerTransaction sipServerTx = (SIPServerTransaction) sipServerRequest;
518                            if (!sipServerTx.passToListener())
519                                ((SIPTransaction) sipServerRequest).releaseSem();
520                        }
521                    }
522                } else {
523                	if (sipStack.isLoggingEnabled())
524                		this.sipStack.getStackLogger()
525                            .logWarning("Dropping request -- could not acquire semaphore in 10 sec");
526                }
527
528            } else {
529                SIPResponse sipResponse = (SIPResponse) sipMessage;
530                // JvB: dont do this
531                // if (sipResponse.getStatusCode() == 100)
532                // sipResponse.getTo().removeParameter("tag");
533                try {
534                    sipResponse.checkHeaders();
535                } catch (ParseException ex) {
536                    if (sipStack.isLoggingEnabled())
537                        sipStack.getStackLogger()
538                                .logError("Dropping Badly formatted response message >>> "
539                                        + sipResponse);
540                    return;
541                }
542                // This is a response message - process it.
543                // Check the size of the response.
544                // If it is too large dump it silently.
545                if (sipStack.getMaxMessageSize() > 0
546                        && sipResponse.getSize()
547                                + (sipResponse.getContentLength() == null ? 0 : sipResponse
548                                        .getContentLength().getContentLength()) > sipStack
549                                .getMaxMessageSize()) {
550                    if (sipStack.isLoggingEnabled())
551                        this.sipStack.getStackLogger().logDebug("Message size exceeded");
552                    return;
553
554                }
555                ServerResponseInterface sipServerResponse = sipStack.newSIPServerResponse(
556                        sipResponse, this);
557                if (sipServerResponse != null) {
558                    try {
559                        if (sipServerResponse instanceof SIPClientTransaction
560                                && !((SIPClientTransaction) sipServerResponse)
561                                        .checkFromTag(sipResponse)) {
562                            if (sipStack.isLoggingEnabled())
563                                sipStack.getStackLogger()
564                                        .logError("Dropping response message with invalid tag >>> "
565                                                + sipResponse);
566                            return;
567                        }
568
569                        sipServerResponse.processResponse(sipResponse, this);
570                    } finally {
571                        if (sipServerResponse instanceof SIPTransaction
572                                && !((SIPTransaction) sipServerResponse).passToListener())
573                            ((SIPTransaction) sipServerResponse).releaseSem();
574                    }
575                } else {
576                    sipStack
577                            .getStackLogger()
578                            .logWarning(
579                                    "Application is blocked -- could not acquire semaphore -- dropping response");
580                }
581            }
582        } finally {
583        }
584    }
585
586    /**
587     * This gets invoked when thread.start is called from the constructor. Implements a message
588     * loop - reading the tcp connection and processing messages until we are done or the other
589     * end has closed.
590     */
591    public void run() {
592        Pipeline hispipe = null;
593        // Create a pipeline to connect to our message parser.
594        hispipe = new Pipeline(myClientInputStream, sipStack.readTimeout,
595                ((SIPTransactionStack) sipStack).getTimer());
596        // Create a pipelined message parser to read and parse
597        // messages that we write out to him.
598        myParser = new PipelinedMsgParser(this, hispipe, this.sipStack.getMaxMessageSize());
599        // Start running the parser thread.
600        myParser.processInput();
601        // bug fix by Emmanuel Proulx
602        int bufferSize = 4096;
603        this.tcpMessageProcessor.useCount++;
604        this.isRunning = true;
605        try {
606            while (true) {
607                try {
608                    byte[] msg = new byte[bufferSize];
609                    int nbytes = myClientInputStream.read(msg, 0, bufferSize);
610                    // no more bytes to read...
611                    if (nbytes == -1) {
612                        hispipe.write("\r\n\r\n".getBytes("UTF-8"));
613                        try {
614                            if (sipStack.maxConnections != -1) {
615                                synchronized (tcpMessageProcessor) {
616                                    tcpMessageProcessor.nConnections--;
617                                    tcpMessageProcessor.notify();
618                                }
619                            }
620                            hispipe.close();
621                            mySock.close();
622                        } catch (IOException ioex) {
623                        }
624                        return;
625                    }
626                    hispipe.write(msg, 0, nbytes);
627
628                } catch (IOException ex) {
629                    // Terminate the message.
630                    try {
631                        hispipe.write("\r\n\r\n".getBytes("UTF-8"));
632                    } catch (Exception e) {
633                        // InternalErrorHandler.handleException(e);
634                    }
635
636                    try {
637                        if (sipStack.isLoggingEnabled())
638                            sipStack.getStackLogger().logDebug("IOException  closing sock " + ex);
639                        try {
640                            if (sipStack.maxConnections != -1) {
641                                synchronized (tcpMessageProcessor) {
642                                    tcpMessageProcessor.nConnections--;
643                                    // System.out.println("Notifying!");
644                                    tcpMessageProcessor.notify();
645                                }
646                            }
647                            mySock.close();
648                            hispipe.close();
649                        } catch (IOException ioex) {
650                        }
651                    } catch (Exception ex1) {
652                        // Do nothing.
653                    }
654                    return;
655                } catch (Exception ex) {
656                    InternalErrorHandler.handleException(ex, sipStack.getStackLogger());
657                }
658            }
659        } finally {
660            this.isRunning = false;
661            this.tcpMessageProcessor.remove(this);
662            this.tcpMessageProcessor.useCount--;
663            myParser.close();
664        }
665
666    }
667
668    protected void uncache() {
669    	if (isCached && !isRunning) {
670    		this.tcpMessageProcessor.remove(this);
671    	}
672    }
673
674    /**
675     * Equals predicate.
676     *
677     * @param other is the other object to compare ourselves to for equals
678     */
679
680    public boolean equals(Object other) {
681
682        if (!this.getClass().equals(other.getClass()))
683            return false;
684        else {
685            TCPMessageChannel that = (TCPMessageChannel) other;
686            if (this.mySock != that.mySock)
687                return false;
688            else
689                return true;
690        }
691    }
692
693    /**
694     * Get an identifying key. This key is used to cache the connection and re-use it if
695     * necessary.
696     */
697    public String getKey() {
698        if (this.key != null) {
699            return this.key;
700        } else {
701            this.key = MessageChannel.getKey(this.peerAddress, this.peerPort, "TCP");
702            return this.key;
703        }
704    }
705
706    /**
707     * Get the host to assign to outgoing messages.
708     *
709     * @return the host to assign to the via header.
710     */
711    public String getViaHost() {
712        return myAddress;
713    }
714
715    /**
716     * Get the port for outgoing messages sent from the channel.
717     *
718     * @return the port to assign to the via header.
719     */
720    public int getViaPort() {
721        return myPort;
722    }
723
724    /**
725     * Get the port of the peer to whom we are sending messages.
726     *
727     * @return the peer port.
728     */
729    public int getPeerPort() {
730        return peerPort;
731    }
732
733    public int getPeerPacketSourcePort() {
734        return this.peerPort;
735    }
736
737    public InetAddress getPeerPacketSourceAddress() {
738        return this.peerAddress;
739    }
740
741    /**
742     * TCP Is not a secure protocol.
743     */
744    public boolean isSecure() {
745        return false;
746    }
747}
748