static JSON::Value parse(yaml_parser_t &parser) { JSON::Value result; yaml_document_t document; if (!yaml_parser_load(&parser, &document)) { yaml_error_type_t error = parser.error; MORDOR_ASSERT(error != YAML_NO_ERROR); Exception exception(parser.problem, parser.context); yaml_parser_delete(&parser); switch (error) { case YAML_MEMORY_ERROR: MORDOR_THROW_EXCEPTION(std::bad_alloc()); case YAML_READER_ERROR: MORDOR_THROW_EXCEPTION(InvalidUnicodeException()); default: MORDOR_THROW_EXCEPTION(exception); } } try { convertNode(result, yaml_document_get_root_node(&document), document); yaml_document_delete(&document); yaml_parser_delete(&parser); return result; } catch (...) { yaml_document_delete(&document); yaml_parser_delete(&parser); throw; } }
size_t SSLStream::read(void *buffer, size_t length) { const int toRead = (int)std::min<size_t>(0x0fffffff, length); while (true) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_read, m_ssl.get(), buffer, toRead), &error); if (result > 0) { return result; } MORDOR_LOG_DEBUG(g_log) << this << " SSL_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ")"; switch (error) { case SSL_ERROR_NONE: return result; case SSL_ERROR_ZERO_RETURN: // Received close_notify message MORDOR_ASSERT(result == 0); return 0; case SSL_ERROR_WANT_READ: 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_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_read"); ; } MORDOR_LOG_WARNING(g_log) << this << " SSL_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ")"; if (result == 0) { return 0; } MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_read"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_read"); ; } default: MORDOR_NOTREACHED(); } } }
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; } } }
BodyPart::BodyPart(Multipart::ptr multipart) : m_multipart(multipart) { if (m_multipart->m_stream->supportsRead()) { HTTP::TrailerParser parser(m_headers); parser.run(m_multipart->m_stream); if (parser.error()) MORDOR_THROW_EXCEPTION(HTTP::BadMessageHeaderException()); if (!parser.complete()) MORDOR_THROW_EXCEPTION(HTTP::IncompleteMessageHeaderException()); m_stream.reset(new BodyPartStream(m_multipart->m_stream, m_multipart->m_boundary)); NotifyStream *notify = new NotifyStream(m_stream); notify->notifyOnEof = boost::bind(&Multipart::partDone, m_multipart); m_stream.reset(notify); } }
Multipart::Multipart(Stream::ptr stream, std::string boundary) : m_stream(stream), m_boundary(boundary), m_finished(false) { MORDOR_ASSERT(m_stream); MORDOR_ASSERT(m_stream->supportsRead() || m_stream->supportsWrite()); MORDOR_ASSERT(!(m_stream->supportsRead() && m_stream->supportsWrite())); while (!m_boundary.empty() && m_boundary[m_boundary.size() - 1] == ' ') m_boundary.resize(m_boundary.size() - 1); MORDOR_ASSERT(!m_boundary.empty()); MORDOR_ASSERT(m_boundary.size() <= 70); if (m_boundary.find_first_not_of(allowedBoundaryChars) != std::string::npos) { if (stream->supportsWrite()) { MORDOR_ASSERT(false); } else { MORDOR_THROW_EXCEPTION(InvalidMultipartBoundaryException()); } } m_boundary = "\r\n--" + m_boundary; if (m_stream->supportsRead()) { MORDOR_ASSERT(m_stream->supportsFind()); MORDOR_ASSERT(m_stream->supportsUnread()); } }
SSLStream::SSLStream(Stream::ptr parent, bool client, bool own, SSL_CTX *ctx) : MutatingFilterStream(parent, own) { MORDOR_ASSERT(parent); clearSSLError(); if (ctx) m_ctx.reset(ctx, &nop<SSL_CTX *>); else m_ctx.reset(SSL_CTX_new(client ? SSLv23_client_method() : SSLv23_server_method()), &SSL_CTX_free); if (!m_ctx) { MORDOR_ASSERT(hasOpenSSLError()); MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage())) // << boost::errinfo_api_function("SSL_CTX_new"); ; } // Auto-generate self-signed server cert if (!ctx && !client) { std::shared_ptr<X509> cert; std::shared_ptr<EVP_PKEY> pkey; mkcert(cert, pkey, 1024, rand(), 365); SSL_CTX_use_certificate(m_ctx.get(), cert.get()); SSL_CTX_use_PrivateKey(m_ctx.get(), pkey.get()); } m_ssl.reset(SSL_new(m_ctx.get()), &SSL_free); if (!m_ssl) { MORDOR_ASSERT(hasOpenSSLError()); MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage())) // << boost::errinfo_api_function("SSL_CTX_new"); ; } m_readBio = BIO_new(BIO_s_mem()); m_writeBio = BIO_new(BIO_s_mem()); if (!m_readBio || !m_writeBio) { if (m_readBio) BIO_free(m_readBio); if (m_writeBio) BIO_free(m_writeBio); MORDOR_ASSERT(hasOpenSSLError()); MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage())) // << boost::errinfo_api_function("BIO_new"); ; } BIO_set_mem_eof_return(m_readBio, -1); SSL_set_bio(m_ssl.get(), m_readBio, m_writeBio); }
JSON::Value parse(const std::string &string) { yaml_parser_t parser; if (!yaml_parser_initialize(&parser)) MORDOR_THROW_EXCEPTION(std::bad_alloc()); yaml_parser_set_input_string(&parser, (const unsigned char *)string.c_str(), string.size()); return parse(parser); }
void Buffer::copyIn(const Buffer &buffer, size_t length, size_t pos) { if (pos > buffer.readAvailable()) MORDOR_THROW_EXCEPTION(std::out_of_range("position out of range")); if (length == (size_t)~0) length = buffer.readAvailable() - pos; MORDOR_ASSERT(buffer.readAvailable() >= length + pos); invariant(); if (length == 0) return; // Split any mixed read/write bufs if (m_writeIt != m_segments.end() && m_writeIt->readAvailable() != 0) { m_segments.insert(m_writeIt, Segment(m_writeIt->readBuffer())); m_writeIt->consume(m_writeIt->readAvailable()); invariant(); } std::list<Segment>::const_iterator it = buffer.m_segments.begin(); while (pos != 0 && it != buffer.m_segments.end()) { if (pos < it->readAvailable()) break; pos -= it->readAvailable(); ++it; } MORDOR_ASSERT(it != buffer.m_segments.end()); for (; it != buffer.m_segments.end(); ++it) { size_t toConsume = (std::min)(it->readAvailable() - pos, length); if (m_readAvailable != 0 && it == buffer.m_segments.begin()) { std::list<Segment>::iterator previousIt = m_writeIt; --previousIt; if ((char *)previousIt->readBuffer().start() + previousIt->readBuffer().length() == (char *)it->readBuffer().start() + pos && previousIt->m_data.m_array.get() == it->m_data.m_array.get()) { MORDOR_ASSERT(previousIt->writeAvailable() == 0); previousIt->extend(toConsume); m_readAvailable += toConsume; length -= toConsume; pos = 0; if (length == 0) break; continue; } } Segment newSegment = Segment(it->readBuffer().slice(pos, toConsume)); m_segments.insert(m_writeIt, newSegment); m_readAvailable += toConsume; length -= toConsume; pos = 0; if (length == 0) break; } MORDOR_ASSERT(length == 0); MORDOR_ASSERT(readAvailable() >= length); }
long long HandleStream::seek(long long offset, Anchor anchor) { SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler); if (m_ioManager) { if (supportsSeek()) { switch (anchor) { case BEGIN: if (offset < 0) { MORDOR_THROW_EXCEPTION(std::invalid_argument("resulting offset is negative")); } return m_pos = offset; case CURRENT: if (m_pos + offset < 0) { MORDOR_THROW_EXCEPTION(std::invalid_argument("resulting offset is negative")); } return m_pos += offset; case END: { long long end = size(); if (end + offset < 0) { MORDOR_THROW_EXCEPTION(std::invalid_argument("resulting offset is negative")); } return m_pos = end + offset; } default: MORDOR_ASSERT(false); } } else { MORDOR_ASSERT(false); } } long long pos; BOOL ret = SetFilePointerEx(m_hFile, *(LARGE_INTEGER*)&offset, (LARGE_INTEGER*)&pos, (DWORD)anchor); DWORD error = GetLastError(); MORDOR_LOG_LEVEL(g_log, ret ? Log::VERBOSE : Log::ERROR) << this << " SetFilePointerEx(" << m_hFile << ", " << offset << ", " << pos << ", " << anchor << "): " << ret << " (" << error << ")"; if (!ret) MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "SetFilePointerEx"); return pos; }
void SSLStream::verifyPeerCertificate() { const long verifyResult = sslCallWithLock(std::bind(SSL_get_verify_result, m_ssl.get()), NULL); MORDOR_LOG_LEVEL(g_log, verifyResult ? Log::WARNING : Log::DBG) << this << " SSL_get_verify_result(" << m_ssl.get() << "): " << verifyResult; if (verifyResult != X509_V_OK) MORDOR_THROW_EXCEPTION(CertificateVerificationException(verifyResult)); }
size_t HandleStream::write(const void *buffer, size_t length) { if (m_cancelWrite) MORDOR_THROW_EXCEPTION(OperationAbortedException()); SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler); DWORD written; OVERLAPPED *overlapped = NULL; if (m_ioManager) { MORDOR_ASSERT(Scheduler::getThis()); m_ioManager->registerEvent(&m_writeEvent); overlapped = &m_writeEvent.overlapped; if (supportsSeek()) { overlapped->Offset = (DWORD)m_pos; overlapped->OffsetHigh = (DWORD)(m_pos >> 32); } } length = (std::min)(length, m_maxOpSize); BOOL ret = WriteFile(m_hFile, buffer, (DWORD)length, &written, overlapped); Log::Level level = Log::DEBUG; if (!ret) { if (m_ioManager && GetLastError() == ERROR_IO_PENDING) level = Log::TRACE; else level = Log::ERROR; } DWORD error = GetLastError(); MORDOR_LOG_LEVEL(g_log, level) << this << " WriteFile(" << m_hFile << ", " << length << "): " << ret << " - " << written << " (" << error << ")"; if (m_ioManager) { if (!ret && error != ERROR_IO_PENDING) { m_ioManager->unregisterEvent(&m_writeEvent); MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("WriteFile"); } if (m_skipCompletionPortOnSuccess && ret) m_ioManager->unregisterEvent(&m_writeEvent); else Scheduler::yieldTo(); DWORD error = pRtlNtStatusToDosError((NTSTATUS)m_writeEvent.overlapped.Internal); MORDOR_LOG_LEVEL(g_log, error ? Log::ERROR : Log::VERBOSE) << this << " WriteFile(" << m_hFile << ", " << length << "): " << m_writeEvent.overlapped.InternalHigh << " (" << error << ")"; if (error) MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "WriteFile"); if (supportsSeek()) { m_pos = ((long long)overlapped->Offset | ((long long)overlapped->OffsetHigh << 32)) + m_writeEvent.overlapped.InternalHigh; } return m_writeEvent.overlapped.InternalHigh; } if (!ret) MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "WriteFile"); return written; }
BodyPart::ptr Multipart::nextPart() { if (m_stream->supportsWrite()) { MORDOR_ASSERT(!m_finished); MORDOR_ASSERT(!m_currentPart); m_currentPart.reset(new BodyPart(shared_from_this())); std::string boundary = m_boundary + "\r\n"; m_stream->write(boundary.c_str(), boundary.size()); return m_currentPart; } else { if (m_finished) { MORDOR_ASSERT(!m_currentPart); return m_currentPart; } if (m_currentPart) { transferStream(m_currentPart->stream(), NullStream::get()); // Changed by the notification callback MORDOR_ASSERT(!m_currentPart); } size_t offsetToBoundary = m_stream->find(m_boundary); Buffer b; size_t result = m_stream->read(b, offsetToBoundary + m_boundary.size()); MORDOR_ASSERT(result == offsetToBoundary + m_boundary.size()); b.clear(); result = m_stream->read(b, 2); if (b == "--") { m_finished = true; } if (b == "\n") { m_stream->unread(b, 1); } if (b != "\r\n") { std::string restOfLine = m_stream->getDelimited(); MORDOR_ASSERT(!restOfLine.empty()); restOfLine.resize(restOfLine.size() - 1); if (restOfLine.find_first_not_of(" \r\t") != std::string::npos) { MORDOR_THROW_EXCEPTION(InvalidMultipartBoundaryException()); } } if (m_finished) { if (multipartFinished) multipartFinished(); return m_currentPart; } m_currentPart.reset(new BodyPart(shared_from_this())); return m_currentPart; } }
JSON::Value parse(Stream &stream) { yaml_parser_t parser; if (!yaml_parser_initialize(&parser)) MORDOR_THROW_EXCEPTION(std::bad_alloc()); GetDataContext context; context.stream = &stream; yaml_parser_set_input(&parser, &getData, &context); try { return parse(parser); } catch (...) { if (context.exception) ::Mordor::rethrow_exception(context.exception); throw; } }
static void convertNode(JSON::Value &value, yaml_node_t *node, yaml_document_t &document) { switch (node->type) { case YAML_SCALAR_NODE: value = std::string((char *)node->data.scalar.value, node->data.scalar.length); break; case YAML_SEQUENCE_NODE: { value = JSON::Array(); JSON::Array &array = value.get<JSON::Array>(); yaml_node_item_t *item = node->data.sequence.items.start; array.resize(node->data.sequence.items.top - item); JSON::Array::iterator it = array.begin(); while (item < node->data.sequence.items.top) { convertNode(*it, yaml_document_get_node(&document, *item), document); ++it; ++item; } break; } case YAML_MAPPING_NODE: { value = JSON::Object(); JSON::Object &object = value.get<JSON::Object>(); yaml_node_pair_t *pair = node->data.mapping.pairs.start; while (pair < node->data.mapping.pairs.top) { yaml_node_t *keyNode = yaml_document_get_node(&document, pair->key); yaml_node_t *valueNode = yaml_document_get_node(&document, pair->value); if (keyNode->type != YAML_SCALAR_NODE) MORDOR_THROW_EXCEPTION(std::runtime_error("Can't use a non-string as a key")); std::string key((char *)keyNode->data.scalar.value, keyNode->data.scalar.length); convertNode(object.insert(std::make_pair(key, JSON::Value()))->second, valueNode, document); ++pair; } break; } default: MORDOR_NOTREACHED(); } }
static bool hasOpenSSLError() { unsigned long err = ERR_peek_error(); if (err == SSL_ERROR_NONE) return false; switch (ERR_GET_REASON(err)) { case ERR_R_MALLOC_FAILURE: throw std::bad_alloc(); case ERR_R_PASSED_NULL_PARAMETER: { char buf[120]; ERR_error_string(err, buf); MORDOR_THROW_EXCEPTION(std::invalid_argument(buf)); } default: return true; } }
void SSLStream::serverNameIndication(const std::string &hostname) { // Older versions of OpenSSL don't support this (I'm looking at you, // Leopard); just ignore it then #ifdef SSL_set_tlsext_host_name std::lock_guard<std::mutex> lock(m_mutex); if (!SSL_set_tlsext_host_name(m_ssl.get(), hostname.c_str())) { if (!hasOpenSSLError()) return; std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_set_tlsext_host_name(" << m_ssl.get() << ", " << hostname.c_str() << "): " << message; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_set_tlsext_host_name"); ; } #endif }
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; }
Multipart::ptr BodyPart::multipart() { if (m_childMultipart) return m_childMultipart; MORDOR_ASSERT(m_headers.contentType.type == "multipart"); if (!m_stream) { MORDOR_ASSERT(m_multipart->m_stream->supportsWrite()); std::ostringstream os; os << m_headers; std::string headers = os.str(); m_multipart->m_stream->write(headers.c_str(), headers.size()); NotifyStream *notify = new NotifyStream(m_multipart->m_stream, false); notify->notifyOnClose = boost::bind(&Multipart::partDone, m_multipart); m_stream.reset(notify); } HTTP::StringMap::const_iterator it = m_headers.contentType.parameters.find("boundary"); if (it == m_headers.contentType.parameters.end()) { MORDOR_ASSERT(!m_multipart->m_stream->supportsWrite()); MORDOR_THROW_EXCEPTION(InvalidMultipartBoundaryException()); } m_childMultipart.reset(new Multipart(m_stream, it->second)); return m_childMultipart; }
void SSLStream::close(CloseType type) { MORDOR_ASSERT(type == BOTH); if (!(sslCallWithLock(std::bind(SSL_get_shutdown, m_ssl.get()), NULL) & SSL_SENT_SHUTDOWN)) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_shutdown, m_ssl.get()), &error); if (result <= 0) { MORDOR_LOG_DEBUG(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; switch (error) { case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: break; case 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_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result == 0) break; MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_shutdown"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } default: MORDOR_NOTREACHED(); } } flush(false); } while (!(sslCallWithLock(std::bind(SSL_get_shutdown, m_ssl.get()), NULL) & SSL_RECEIVED_SHUTDOWN)) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_shutdown, m_ssl.get()), &error); MORDOR_LOG_DEBUG(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result > 0) { break; } switch (error) { case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: break; 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_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } MORDOR_LOG_WARNING(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result == 0) { break; } MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_shutdown"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } default: MORDOR_NOTREACHED(); } break; } parent()->close(); }
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(); } } }
void authorize(const AuthParams &challenge, AuthParams &authorization, const URI &uri, const std::string &method, const std::string &username, const std::string &password) { std::string realm, qop, nonce, opaque, algorithm; StringMap::const_iterator it; if ( (it = challenge.parameters.find("realm")) != challenge.parameters.end()) realm = it->second; if ( (it = challenge.parameters.find("qop")) != challenge.parameters.end()) qop = it->second; if ( (it = challenge.parameters.find("nonce")) != challenge.parameters.end()) nonce = it->second; if ( (it = challenge.parameters.find("opaque")) != challenge.parameters.end()) opaque = it->second; if ( (it = challenge.parameters.find("algorithm")) != challenge.parameters.end()) algorithm = it->second; if (algorithm.empty()) algorithm = "MD5"; StringSet qopValues; bool authQop = false; // If the server specified a quality of protection (qop), make sure it allows "auth" if (!qop.empty()) { ListParser parser(qopValues); parser.run(qop); if (parser.error() || !parser.complete()) MORDOR_THROW_EXCEPTION(BadMessageHeaderException()); if (qopValues.find("auth") == qopValues.end()) MORDOR_THROW_EXCEPTION(InvalidQopException(qop)); authQop = true; } // come up with a suitable client nonce std::ostringstream os; os << std::hex << TimerManager::now(); std::string cnonce = os.str(); std::string nc = "00000001"; // compute A1 std::string A1; if (algorithm == "MD5") A1 = username + ':' + realm + ':' + password; else if (algorithm == "MD5-sess") A1 = md5( username + ':' + realm + ':' + password ) + ':' + nonce + ':' + cnonce; else MORDOR_THROW_EXCEPTION(InvalidAlgorithmException(algorithm)); // compute A2 - our qop is always auth or unspecified os.str(""); os << method << ':' << uri; std::string A2 = os.str(); authorization.scheme = "Digest"; authorization.base64.clear(); authorization.parameters["username"] = username; authorization.parameters["realm"] = realm; authorization.parameters["nonce"] = nonce; authorization.parameters["uri"] = uri.toString(); authorization.parameters["algorithm"] = algorithm; std::string response; if (authQop) { qop = "auth"; response = md5( md5(A1) + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + md5(A2) ); authorization.parameters["qop"] = qop; authorization.parameters["nc"] = nc; authorization.parameters["cnonce"] = cnonce; } else { response = md5( md5(A1) + ':' + nonce + ':' + md5(A2) ); } authorization.parameters["response"] = response; if (!opaque.empty()) authorization.parameters["opaque"] = opaque; }
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(); } } }
void SSLStream::verifyPeerCertificate(const std::string &hostname) { if (!hostname.empty()) { std::lock_guard<std::mutex> lock(m_mutex); std::string wildcardHostname = "*"; size_t dot = hostname.find('.'); if (dot != std::string::npos) wildcardHostname.append(hostname.substr(dot)); std::shared_ptr<X509> cert; cert.reset(SSL_get_peer_certificate(m_ssl.get()), &X509_free); if (!cert) MORDOR_THROW_EXCEPTION(CertificateVerificationException( X509_V_ERR_APPLICATION_VERIFICATION, "No Certificate Presented")); int critical = -1, altNameIndex = -1; GENERAL_NAMES *gens = (GENERAL_NAMES *)X509_get_ext_d2i(cert.get(), NID_subject_alt_name, &critical, &altNameIndex); if (gens) { do { try { bool success = false; for(int i = 0; i < sk_GENERAL_NAME_num(gens); i++) { GENERAL_NAME *gen = sk_GENERAL_NAME_value(gens, i); if(gen->type != GEN_DNS) continue; std::string altName((const char *)gen->d.dNSName->data, gen->d.dNSName->length); if (altName == wildcardHostname || altName == hostname) { success = true; break; } } sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); if (success) return; } catch (...) { sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); throw; } gens = (GENERAL_NAMES *)X509_get_ext_d2i(cert.get(), NID_subject_alt_name, &critical, &altNameIndex); } while (gens); } X509_NAME *name = X509_get_subject_name(cert.get()); if (!name) MORDOR_THROW_EXCEPTION(CertificateVerificationException( X509_V_ERR_APPLICATION_VERIFICATION, "No Subject Name")); int len = X509_NAME_get_text_by_NID(name, NID_commonName, NULL, 0); if (len == -1) MORDOR_THROW_EXCEPTION(CertificateVerificationException( X509_V_ERR_APPLICATION_VERIFICATION, "No Common Name")); std::string commonName; commonName.resize(len); X509_NAME_get_text_by_NID(name, NID_commonName, &commonName[0], len + 1); if (commonName == wildcardHostname || commonName == hostname) return; MORDOR_THROW_EXCEPTION(CertificateVerificationException( X509_V_ERR_APPLICATION_VERIFICATION, "No Matching Common Name")); } }
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; }