ptrdiff_t BufferedStream::find(const std::string &str, size_t sanitySize, bool throwIfNotFound) { if (supportsSeek()) flush(false); if (sanitySize == (size_t)~0) sanitySize = 2 * m_bufferSize; sanitySize += str.size(); while (true) { size_t readAvailable = m_readBuffer.readAvailable(); if (readAvailable > 0) { ptrdiff_t result = m_readBuffer.find(str, std::min(sanitySize, readAvailable)); if (result != -1) { return result; } } if (readAvailable >= sanitySize) { if (throwIfNotFound) MORDOR_THROW_EXCEPTION(BufferOverflowException()); return -(ptrdiff_t)m_readBuffer.readAvailable() - 1; } MORDOR_LOG_TRACE(g_log) << this << " parent()->read(" << m_bufferSize << ")"; size_t result = parent()->read(m_readBuffer, m_bufferSize); MORDOR_LOG_DEBUG(g_log) << this << " parent()->read(" << m_bufferSize << "): " << result; if (result == 0) { // EOF if (throwIfNotFound) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); return -(ptrdiff_t)m_readBuffer.readAvailable() - 1; } } }
std::string Buffer::getDelimited(char delimiter, bool eofIsDelimiter, bool includeDelimiter) { ptrdiff_t offset = find(delimiter, ~0); MORDOR_ASSERT(offset >= -1); if (offset == -1 && !eofIsDelimiter) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); eofIsDelimiter = offset == -1; if (offset == -1) offset = readAvailable();; std::string result; result.resize(offset + (eofIsDelimiter ? 0 : (includeDelimiter ? 1 : 0))); copyOut(&result[0], result.size()); consume(result.size()); if (!eofIsDelimiter && !includeDelimiter) consume(1u); return result; }
size_t ZlibStream::read(Buffer &buffer, size_t length) { if (m_closed) return 0; struct iovec outbuf = buffer.writeBuffer(length, false); m_strm.next_out = (Bytef*)outbuf.iov_base; m_strm.avail_out = outbuf.iov_len; while (true) { std::vector<iovec> inbufs = m_inBuffer.readBuffers(); size_t avail_in; if (!inbufs.empty()) { m_strm.next_in = (Bytef*)inbufs[0].iov_base; avail_in = inbufs[0].iov_len; m_strm.avail_in = inbufs[0].iov_len; } else { m_strm.next_in = NULL; m_strm.avail_in = 0; } int rc = inflate(&m_strm, Z_NO_FLUSH); MORDOR_LOG_DEBUG(g_log) << this << " inflate((" << (inbufs.empty() ? 0 : inbufs[0].iov_len) << ", " << outbuf.iov_len << ")): " << rc << " (" << m_strm.avail_in << ", " << m_strm.avail_out << ")"; if (!inbufs.empty()) m_inBuffer.consume(inbufs[0].iov_len - m_strm.avail_in); size_t result; switch (rc) { case Z_STREAM_END: // May have still produced output result = outbuf.iov_len - m_strm.avail_out; buffer.produce(result); inflateEnd(&m_strm); m_closed = true; return result; case Z_OK: result = outbuf.iov_len - m_strm.avail_out; // It consumed input, but produced no output... DON'T return eof if (result == 0) continue; buffer.produce(result); return result; case Z_BUF_ERROR: // no progress... we need to provide more input (since we're // guaranteed to provide output) MORDOR_ASSERT(m_strm.avail_in == 0); MORDOR_ASSERT(inbufs.empty()); result = parent()->read(m_inBuffer, m_bufferSize); if (result == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); break; case Z_MEM_ERROR: throw std::bad_alloc(); case Z_NEED_DICT: MORDOR_THROW_EXCEPTION(NeedPresetDictionaryException()); case Z_DATA_ERROR: MORDOR_THROW_EXCEPTION(CorruptedZlibStreamException()); default: MORDOR_NOTREACHED(); } } }
Stream::ptr tunnel(HTTP::StreamBroker::ptr streamBroker, const URI &proxy, IPAddress::ptr targetIP, const std::string &targetDomain, unsigned short targetPort, unsigned char version) { MORDOR_ASSERT(version == 4 || version == 5); MORDOR_ASSERT(version == 5 || !targetIP || targetIP->family() == AF_INET); MORDOR_ASSERT(streamBroker); MORDOR_ASSERT(targetIP || !targetDomain.empty()); std::string buffer; buffer.resize(std::max<size_t>(targetDomain.size() + 1u, 16u) + 9); Stream::ptr stream = streamBroker->getStream(proxy); if (version == 5) { buffer[0] = version; buffer[1] = 1; buffer[2] = 0; size_t written = 0; while (written < 3) written += stream->write(buffer.data() + written, 3 - written); if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if (buffer[0] != 5) MORDOR_THROW_EXCEPTION(ProtocolViolationException()); if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if ((unsigned char)buffer[0] == 0xff) MORDOR_THROW_EXCEPTION(NoAcceptableAuthenticationMethodException()); if (buffer[0] != 0) MORDOR_THROW_EXCEPTION(ProtocolViolationException()); } buffer[0] = version; buffer[1] = 1; size_t size; if (version == 4) { if (targetIP) *(unsigned short *)&buffer[2] = htons(targetIP->port()); else *(unsigned short *)&buffer[2] = htons(targetPort); if (targetIP) *(unsigned int *)&buffer[4] = htonl((unsigned int)(((sockaddr_in *)targetIP->name())->sin_addr.s_addr)); else *(unsigned int *)&buffer[4] = htonl(0x00000001); buffer[8] = 0; if (!targetIP) { memcpy(&buffer[9], targetDomain.c_str(), targetDomain.size()); buffer[9 + targetDomain.size()] = 0; } size = 9 + targetDomain.size() + (targetDomain.empty() ? 0 : 1); } else { buffer[2] = 0; if (targetIP) { if (targetIP->family() == AF_INET) { buffer[3] = 1; *(unsigned int *)&buffer[4] = htonl((unsigned int)(((sockaddr_in *)targetIP->name())->sin_addr.s_addr)); size = 7; } else { buffer[3] = 4; memcpy(&buffer[4], &((sockaddr_in6 *)targetIP->name())->sin6_addr, 16); size = 19; } } else { buffer[3] = 3; buffer[4] = (unsigned char)targetDomain.size(); memcpy(&buffer[5], targetDomain.c_str(), targetDomain.size()); size = 5 + targetDomain.size(); } if (targetIP) *(unsigned short *)&buffer[size] = htons(targetIP->port()); else *(unsigned short *)&buffer[size] = htons(targetPort); size += 2; } size_t written = 0; while (written < size) written += stream->write(buffer.data() + written, size - written); if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if ((version == 4 && buffer[0] != 0) || (version == 5 && buffer[0] != 5)) MORDOR_THROW_EXCEPTION(ProtocolViolationException()); if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if ((version == 4 && buffer[0] != 0x5a) || (version == 5 && buffer[0] != 0)) MORDOR_THROW_EXCEPTION(InvalidResponseException(buffer[0])); if (version == 4) { size = 0; while (size < 6) { written = stream->read(&buffer[0], 6 - size); if (written == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); size += written; } } else { if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if (buffer[0] != 0) MORDOR_THROW_EXCEPTION(InvalidResponseException(buffer[0])); if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if (buffer[1] == 3) { if (buffer[0] != 0) MORDOR_THROW_EXCEPTION(ProtocolViolationException()); size = buffer[1] + 2; } else if (buffer[1] == 1) { size = 6; } else if (buffer[1] == 4) { size = 18; } else { MORDOR_THROW_EXCEPTION(ProtocolViolationException()); } while (size > 0) { written = stream->read(&buffer[0], size); if (written == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); size -= written; } } return stream; }
void SSLStream::connect() { while (true) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_connect, m_ssl.get()), &error); MORDOR_LOG_DEBUG(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result > 0) { flush(false); return; } switch (error) { case SSL_ERROR_NONE: flush(false); return; case SSL_ERROR_ZERO_RETURN: // Received close_notify message return; case SSL_ERROR_WANT_READ: flush(); wantRead(); continue; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: MORDOR_NOTREACHED(); case SSL_ERROR_SYSCALL: if (hasOpenSSLError()) { std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_connect"); ; } MORDOR_LOG_ERROR(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result == 0) { MORDOR_THROW_EXCEPTION(UnexpectedEofException()); } MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_connect"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_connect"); ; } default: MORDOR_NOTREACHED(); } } }
size_t SSLStream::write(const void *buffer, size_t length) { flush(false); if (length == 0) return 0; const int toWrite = (int)std::min<size_t>(0x7fffffff, length); while (true) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_write, m_ssl.get(), buffer, toWrite), &error); if (result > 0) { return result; } MORDOR_LOG_DEBUG(g_log) << this << " SSL_write(" << m_ssl.get() << ", " << toWrite << "): " << result << " (" << error << ")"; switch (error) { case SSL_ERROR_NONE: return result; case SSL_ERROR_ZERO_RETURN: // Received close_notify message MORDOR_ASSERT(result != 0); return result; case SSL_ERROR_WANT_READ: MORDOR_THROW_EXCEPTION(OpenSSLException("SSL_write generated SSL_ERROR_WANT_READ")); case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: MORDOR_NOTREACHED(); case SSL_ERROR_SYSCALL: if (hasOpenSSLError()) { std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_write(" << m_ssl.get() << ", " << toWrite << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_write"); ; } MORDOR_LOG_ERROR(g_log) << this << " SSL_write(" << m_ssl.get() << ", " << toWrite << "): " << result << " (" << error << ")"; if (result == 0) { MORDOR_THROW_EXCEPTION(UnexpectedEofException()); } MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_write"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_write(" << m_ssl.get() << ", " << toWrite << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_write"); ; } default: MORDOR_NOTREACHED(); } } }