/// Write as much of the buffered messages as can be written. // Executed by the thread. void SipClientWriteBuffer::writeMore() { // 'exit_loop' will be set to TRUE if an attempt to write does // not write any bytes, and we will then return. UtlBoolean exit_loop = FALSE; while (mWriteQueued && !exit_loop) { if (mWritePointer >= mWriteString.length()) { // We have written all of the first message. // Pop it and set up to write the next message. delete mWriteBuffer.get(); mWriteString.remove(0); mWritePointer = 0; mWriteQueued = ! mWriteBuffer.isEmpty(); if (mWriteQueued) { // get the message on the head of the queue, and figure out which kind it is UtlContainable* nextMsg = mWriteBuffer.first(); SipMessage* sipMsg; UtlString* keepAliveMsg; if ((sipMsg = dynamic_cast<SipMessage*>(nextMsg))) // a SIP message { ssize_t length; sipMsg->getBytes(&mWriteString, &length); } else if ((keepAliveMsg = dynamic_cast<UtlString*>(nextMsg))) // a keepalive CRLF { mWriteString.append(*keepAliveMsg); } else { Os::Logger::instance().log(FAC_SIP, PRI_CRIT, "SipClientWriteBuffer[%s]::writeMore " "unrecognized message type in queue", mName.data()); assert(false); delete mWriteBuffer.get(); mWriteQueued = mWriteBuffer.isEmpty(); } } } else { // Some portion of the first message remains to be written. // If the socket has failed, attempt to reconnect it. // :NOTE: OsConnectionSocket::reconnect isn't implemented. if (!mClientSocket->isOk()) { mClientSocket->reconnect(); } // Calculate the length to write. int length = mWriteString.length() - mWritePointer; // ret is the value returned from write attempt. // -1 means an error was seen. int ret; if (mClientSocket->isOk()) { // Write what we can. ret = mClientSocket->write(mWriteString.data() + mWritePointer, length); // Theoretically, ret > 0, since the socket is ready for writing, // but it appears that that ret can be 0. } else { // Record the error. ret = -1; // Set a special errno value, which hopefully is not a real value. errno = 1000; } if (ret > 0) { // We successfully sent some data, perhaps all of the // remainder of the first message. // Update the last-activity time. touch(); // Update the state variables. mWritePointer += ret; } else if (ret == 0) { // No data sent, even though (in our caller) poll() // reported the socket was ready to write. exit_loop = TRUE; } else { // Error while writing. Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipClientWriteBuffer[%s]::writeMore " "OsSocket::write() returned %d, errno = %d", getName().data(), ret, errno); // Return all buffered messages with a transport error indication. emptyBuffer(TRUE); // Because TCP is a connection protocol, we know that we cannot // send successfully any more and so should shut down this client. clientStopSelf(); // Exit the loop so handleMessage() can process the stop request. exit_loop = TRUE; } } } }
// Thread execution code. int SipClient::run(void* runArg) { OsMsg* pMsg = NULL; OsStatus res; // Buffer to hold data read from the socket but not yet parsed // into incoming SIP messages. UtlString readBuffer; bool waitingToReportErr = FALSE; // controls whether to read-select on socket bool tcpOnErrWaitForSend = TRUE; int repeatedEOFs = 0; Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run start " "tcpOnErrWaitForSend-%d waitingToReportErr-%d mbTcpOnErrWaitForSend-%d repeatedEOFs-%d", mName.data(), tcpOnErrWaitForSend, waitingToReportErr, mbTcpOnErrWaitForSend, repeatedEOFs); // Wait structure: struct pollfd fds[2]; // Incoming message on the message queue (to be sent on the socket). fds[0].fd = mPipeReadingFd; // Socket ready to write (to continue sending message). // Socket ready to read (message to be received). do { assert(repeatedEOFs < 20); // The file descriptor for the socket may changemsg->getSendAddress(&fromIpAddress, &fromPort);, as OsSocket's // can be re-opened. fds[1].fd = mClientSocket->getSocketDescriptor(); // Initialize the revents members. // This may not be necessary (the man page is not clear), but // Valgrind flags 'fds[*].revents' as undefined if they aren't // initialized. fds[0].revents = 0; fds[1].revents = 0; fds[0].events = POLLIN; // only read-select on pipe // For non-blocking connect failures, don't read-select on socket if // the initial read showed an error but we have to wait to report it. if (!waitingToReportErr) { // This is the normal path. // Read the socket only if the socket is not shared. // If it is shared, the ancestral SipClient will read it. // If multiple threads attempt to read the socket, poll() may // succeed but another may read the data, leaving us to block on // read. fds[1].events = mbSharedSocket ? 0 : POLLIN; // Set wait for writing the socket if there is queued messages to // send. if (mWriteQueued) { // Wait for output on the socket to not block. fds[1].events |= POLLOUT; } } else { // just waiting to report error, ignore the socket fds[1].fd =-1; fds[1].events = 0; } // If there is residual data in the read buffer, // pretend the socket is ready to read. if (!readBuffer.isNull()) { fds[1].revents = POLLIN; } else { // Otherwise, call poll() to wait. int resPoll = poll(&fds[0], sizeof (fds) / sizeof (fds[0]), POLL_TIMEOUT); assert(resPoll >= 0 || (resPoll == -1 && errno == EINTR)); if (resPoll != 0) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run " "resPoll= %d revents: fd[0]= %x fd[1]= %x", mName.data(), resPoll, fds[0].revents, fds[1].revents ); } } if ((fds[1].revents & (POLLERR | POLLHUP)) != 0) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run " "SipMessage::poll error(%d) ", mName.data(), errno); if (OsSocket::isFramed(mClientSocket->getIpProtocol())) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipClient[%s]::run " "SipMessage::poll error(%d) got POLLERR | POLLHUP on UDP socket", mName.data(), errno); } else // eg. tcp socket // This client's socket is a connection-oriented protocol and the // connection has been terminated (probably by the remote end). // We must terminate the SipClient. // We drop the queued messages, but we do not report them to // SipUserAgent as failed sends. This will cause SipUserAgent to // retry the send using the same transport (rather than continuing // to the next transport), which should cause a new connection to // be made to the remote end. { // On non-blocking connect failures, we need to get the first send message // in order to successfully trigger the protocol fallback mechanism if (!tcpOnErrWaitForSend) { // Return all buffered messages with a transport error indication. emptyBuffer(TRUE); clientStopSelf(); } else { fds[1].revents &= ~(POLLERR | POLLHUP); // clear error bit if waiting waitingToReportErr = TRUE; } } } // Check for message queue messages (fds[0]) before checking the socket(fds[1]), // to make sure that we process shutdown messages promptly, even // if we would be spinning trying to service the socket. else if ((fds[0].revents & POLLIN) != 0) { // Poll finished because the pipe is ready to read. // (One byte in pipe means message available in queue.) // Only a SipClient with a derived SipClientWriteBuffer // uses the pipe in the Sip message send process // Check to see how many messages are in the queue. int numberMsgs = (getMessageQueue())->numMsgs(); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run got pipe-select " "Number of Messages waiting: %d", mName.data(), numberMsgs); int i; char buffer[1]; for (i = 0; i < numberMsgs; i++) { // Receive the messages. res = receiveMessage((OsMsg*&) pMsg, OsTime::NO_WAIT); assert(res == OS_SUCCESS); // Normally, this is a SIP message for the write buffer. Once we have gotten // here, we are able to report any initial non-blocking connect error. mbTcpOnErrWaitForSend = FALSE; tcpOnErrWaitForSend = FALSE; Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run got pipe-select " "mbTcpOnErrWaitForSend-%d waitingToReportErr-%d mbTcpOnErrWaitForSend-%d repeatedEOFs-%d", mName.data(), mbTcpOnErrWaitForSend, waitingToReportErr, mbTcpOnErrWaitForSend, repeatedEOFs); // Read 1 byte from the pipe to clear it for this message. One byte is // inserted into the pipe for each message. assert(read(mPipeReadingFd, &buffer, 1) == 1); if (!handleMessage(*pMsg)) // process the message (from queue) { OsServerTask::handleMessage(*pMsg); } if (!pMsg->getSentFromISR()) { pMsg->releaseMsg(); // free the message } // In order to report an unframed(eg TCP) socket error to SipUserAgent dispatcher, // the error must be carried in a sip message from the client's message queue. // The message holds all the identifying information. if (waitingToReportErr) { // Return all buffered messages with a transport error indication. emptyBuffer(TRUE); clientStopSelf(); } } } // end reading msg-available-for-output-queue pipe else if ((fds[1].revents & POLLOUT) != 0) { // Poll finished because socket is ready to write. // Call method to continue writing data. writeMore(); } else if ((fds[1].revents & POLLIN) != 0) { // Poll finished because socket is ready to read. // Read message. // Must allocate a new message because SipUserAgent::dispatch will // take ownership of it. SipMessage* msg = new SipMessage; int res = msg->read(mClientSocket, HTTP_DEFAULT_SOCKET_BUFFER_SIZE, &readBuffer); if (res >= 65536) { // // This is more than the allowable size of a SIP message. Discard! // UtlString remoteHostAddress; int remoteHostPort; msg->getSendAddress(&remoteHostAddress, &remoteHostPort); OS_LOG_WARNING(FAC_SIP, "Received a SIP Message (" << res << " bytes) beyond the maximum allowable size from host " << remoteHostAddress.data() << ":" << remoteHostPort); delete msg; readBuffer.remove(0); continue; } // Use readBuffer to hold any unparsed data after the message // we read. // Note that if a message was successfully parsed, readBuffer // still contains as its prefix the characters of that message. // We save them for logging purposes below and will delete them later. UtlString remoteHostAddress; int remoteHostPort; msg->getSendAddress(&remoteHostAddress, &remoteHostPort); if (!mClientSocket->isSameHost(remoteHostAddress.data(), mLocalHostAddress.data())) { try { if (!remoteHostAddress.isNull()) { boost::asio::ip::address remoteIp = boost::asio::ip::address::from_string(remoteHostAddress.data()); if (rateLimit().isBannedAddress(remoteIp)) { delete msg; readBuffer.remove(0); continue; } rateLimit().logPacket(remoteIp, 0); } } catch(const std::exception& e) { Os::Logger::instance().log(FAC_SIP_INCOMING, PRI_CRIT, "SipClient[%s]::run rate limit exception: %s", mName.data(), e.what()); } } // Note that input was processed at this time. touch(); // // Count the CR/LF to see if this is a keep-alive // int crlfCount = 0; for (int i = 0; i < res; i++) { if (readBuffer(i) == '\r' || readBuffer(i) == '\n') { crlfCount++; } else { break; } } if (res == crlfCount) { repeatedEOFs = 0; // The 'message' was a keepalive (CR-LF or CR-LF-CR-LF). UtlString fromIpAddress; int fromPort; UtlString buffer; int bufferLen; // send one CRLF set in the reply buffer.append("\r\n"); bufferLen = buffer.length(); // Get the send address for response. msg->getSendAddress(&fromIpAddress, &fromPort); if ( !portIsValid(fromPort)) { fromPort = defaultPort(); } // Log the message at DEBUG level. // Only bother processing if the logs are enabled if ( mpSipUserAgent->isMessageLoggingEnabled() || Os::Logger::instance().willLog(FAC_SIP_INCOMING, PRI_DEBUG) ) { UtlString logMessage; logMessage.append("Read keepalive message:\n"); logMessage.append("----Local Host:"); logMessage.append(mLocalHostAddress); logMessage.append("---- Port: "); logMessage.appendNumber( portIsValid(mLocalHostPort) ? mLocalHostPort : defaultPort()); logMessage.append("----\n"); logMessage.append("----Remote Host:"); logMessage.append(fromIpAddress); logMessage.append("---- Port: "); logMessage.appendNumber( portIsValid(fromPort) ? fromPort : defaultPort()); logMessage.append("----\n"); logMessage.append(readBuffer.data(), res); UtlString messageString; logMessage.append(messageString); logMessage.append("====================END====================\n"); // Don't bother to send the message to the SipUserAgent for its internal log. // Write the message to the syslog. Os::Logger::instance().log(FAC_SIP_INCOMING, PRI_DEBUG, "%s", logMessage.data()); } // send the CR-LF response message switch (mSocketType) { case OsSocket::TCP: { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run send TCP keep-alive CR-LF response, ", mName.data()); SipClientSendMsg sendMsg(OsMsg::OS_EVENT, SipClientSendMsg::SIP_CLIENT_SEND_KEEP_ALIVE, fromIpAddress, fromPort); handleMessage(sendMsg); // add newly created keep-alive to write buffer } break; case OsSocket::UDP: { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run send UDP keep-alive CR-LF response, ", mName.data()); (dynamic_cast <OsDatagramSocket*> (mClientSocket))->write(buffer.data(), bufferLen, fromIpAddress, fromPort); } break; default: break; } // Delete the SipMessage allocated above, which is no longer needed. delete msg; // Now that logging is done, remove the parsed bytes and // remember any unparsed input for later use. readBuffer.remove(0, res); } // end keep-alive msg else if (res > 0) // got message, but not keep-alive { // Message successfully read. repeatedEOFs = 0; // Do preliminary processing of message to log it, // clean up its data, and extract any needed source address. preprocessMessage(*msg, readBuffer, res); // Dispatch the message. // dispatch() takes ownership of *msg. mpSipUserAgent->dispatch(msg); // Now that logging is done, remove the parsed bytes and // remember any unparsed input for later use. readBuffer.remove(0, res); } // end process read of >0 bytes else { // Something went wrong while reading the message. // (Possibly EOF on a connection-oriented socket.) repeatedEOFs++; // Delete the SipMessage allocated above, which is no longer needed. delete msg; Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run SipMessage::read returns %d (error(%d) or EOF), " "readBuffer = '%.1000s'", mName.data(), res, errno, readBuffer.data()); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run error wait status " "tcpOnErrWaitForSend-%d waitingToReportErr-%d " "mbTcpOnErrWaitForSend-%d repeatedEOFs-%d " "protocol %d framed %d", mName.data(), tcpOnErrWaitForSend, waitingToReportErr, mbTcpOnErrWaitForSend, repeatedEOFs, mClientSocket->getIpProtocol(), OsSocket::isFramed(mClientSocket->getIpProtocol())); // If the socket is not framed (is connection-oriented), // we need to abort the connection and post a message // :TODO: This doesn't work right for framed connection-oriented // protocols (like SCTP), but OsSocket doesn't have an EOF-query // method -- we need to close all connection-oriented // sockets as well in case it was an EOF. // Define a virtual function that returns the correct bit. if (!OsSocket::isFramed(mClientSocket->getIpProtocol())) { // On non-blocking connect failures, we need to get the first send message // in order to successfully trigger the protocol fallback mechanism if (!tcpOnErrWaitForSend) { // Return all buffered messages with a transport error indication. emptyBuffer(TRUE); clientStopSelf(); } else { fds[1].revents &= ~(POLLERR | POLLHUP); // clear error bit if waiting waitingToReportErr = TRUE; } } // Delete the data read so far, which will not have been // deleted by HttpMessage::read. readBuffer.remove(0); } } // end POLLIN reading socket } while (isStarted()); return 0; // and then exit }
/// Insert a message into the buffer. void SipClientWriteBuffer::insertMessage(SipMessage* message) { UtlBoolean wasEmpty = mWriteBuffer.isEmpty(); // // Let all outbound processors know about this message // if (message && mpSipUserAgent && mClientSocket && mClientSocket->isOk()) { UtlString remoteHostAddress; int remotePort; mClientSocket->getRemoteHostIp(&remoteHostAddress, &remotePort); // We are about to post a message that will cause the // SIP message to be sent. Notify the user agent so // that it can offer the message to all its registered // output processors. ssize_t msgLength = 0; UtlString msgText; message->getBytes(&msgText, &msgLength, true); if (msgLength) { system_tap_sip_tx( mLocalHostAddress.data(), portIsValid(mLocalHostPort) ? mLocalHostPort : defaultPort(), remoteHostAddress.data(), remotePort == PORT_NONE ? defaultPort() : remotePort, msgText.data(), msgLength); mpSipUserAgent->executeAllBufferedSipOutputProcessors(*message, remoteHostAddress.data(), remotePort == PORT_NONE ? defaultPort() : remotePort); } } // Add the message to the queue. mWriteBuffer.insert(message); // If the buffer was empty, we need to set mWriteString and // mWritePointer. if (wasEmpty) { ssize_t length; message->getBytes(&mWriteString, &length); mWritePointer = 0; } mWriteQueued = TRUE; // Check to see if our internal queue is getting too big, which means // that the socket has been blocked for writing for a long time. // We use the message queue length of this task as the limit, since // both queues are affected by the same traffic load factors. if (mWriteBuffer.entries() > (size_t) (getMessageQueue()->maxMsgs())) { // If so, abort all unsent messages and terminate this client (so // as to clear any state of the socket). Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipClientWriteBuffer[%s]::insertMessage " "mWriteBuffer has '%d' entries, exceeding the limit of maxMsgs '%d'", getName().data(), (int) mWriteBuffer.entries(), getMessageQueue()->maxMsgs()); emptyBuffer(TRUE); clientStopSelf(); } }