Exemple #1
0
void swd::connection::handle_read(const boost::system::error_code& e,
 std::size_t bytes_transferred) {
	/**
	 * If an error occurs then no new asynchronous operations are started. This
	 * means that all shared_ptr references to the connection object will disappear
	 * and the object will be destroyed automatically after this handler returns.
	 * The connection class's destructor closes the socket.
	 */
	if (e) {
		return;
	}

	/**
	 * Since there was no error we can start parsing the input now. The parser
	 * fills the object request_ with data.
	 */
	boost::tribool result;
	boost::tie(result, boost::tuples::ignore) =
		request_parser_.parse(
			request_,
			buffer_.data(),
			buffer_.data() + bytes_transferred
		);

	/**
	 * If result is true the complete request is parsed. If it is false there was
	 * an error. If it is indeterminate then the parsing is not complete yet and
	 * the program will read more input and append it to the old request_ object.
	 */
	if (indeterminate(result)) {
		/* Not finished yet with this request, start reading again. */
		this->start_read();

		/* And don't process the input yet. */
		return;
	}

	/* The handler used to process the reply. */
	swd::reply_handler reply_handler(reply_);

	try {
		if (!result) {
			swd::log::i()->send(swd::warning, "Bad request from "
			 + remote_address_.to_string());
			throw swd::exceptions::connection_exception(STATUS_BAD_REQUEST);
		}

		/* Try to add a profile for the request. */
		try {
			swd::profile_ptr profile = swd::database::i()->get_profile(
				remote_address_.to_string(),
				request_->get_profile_id()
			);

			request_->set_profile(profile);
		} catch (swd::exceptions::database_exception& e) {
			swd::log::i()->send(swd::uncritical_error, e.what());
			throw swd::exceptions::connection_exception(STATUS_BAD_REQUEST);
		}

		/* The handler used to process the incoming request. */
		swd::request_handler request_handler(request_);

		/* Only continue processing the reply if it is signed correctly. */
		if (!request_handler.valid_signature()) {
			swd::log::i()->send(swd::warning, "Bad signature from "
			 + remote_address_.to_string());
			throw swd::exceptions::connection_exception(STATUS_BAD_SIGNATURE);
		}

		/**
		 * Before the request can be processed the input has to be transfered
		 * from the encoded json string to a swd::parameters list.
		 */
		if (!request_handler.decode()) {
			swd::log::i()->send(swd::warning, "Bad json from "
			 + remote_address_.to_string());
			throw swd::exceptions::connection_exception(STATUS_BAD_JSON);
		}

		/* Process the request. */
		std::vector<std::string> threats;

		try {
			if (swd::database::i()->is_flooding(request_->get_client_ip(), request_->get_profile_id())) {
				throw swd::exceptions::connection_exception(STATUS_BAD_REQUEST);
			}

			threats = request_handler.process();
		} catch (swd::exceptions::database_exception& e) {
			swd::log::i()->send(swd::uncritical_error, e.what());

			/**
			 * Problems with the database result in a bad request. If protection
			 * is enabled access to the site will not be granted.
			 */
			throw swd::exceptions::connection_exception(STATUS_BAD_REQUEST);
		}

		if (!threats.empty()) {
			reply_->set_threats(threats);
			reply_->set_status(STATUS_ATTACK);
		} else {
			reply_->set_status(STATUS_OK);
		}
	} catch(swd::exceptions::connection_exception& e) {
		reply_->set_status(e.code());
	}

	/* Encode the reply. */
	reply_handler.encode();

	/* Send the answer to the client. */
	if (ssl_) {
		boost::asio::async_write(
			ssl_socket_,
			reply_->to_buffers(),
			strand_.wrap(
				boost::bind(
					&connection::handle_write,
					shared_from_this(),
					boost::asio::placeholders::error
				)
			)
		);
	} else {
		boost::asio::async_write(
			socket_,
			reply_->to_buffers(),
			strand_.wrap(
				boost::bind(
					&connection::handle_write,
					shared_from_this(),
					boost::asio::placeholders::error
				)
			)
		);
	}
}
Exemple #2
0
void swd::connection::handle_read(const boost::system::error_code& e,
 std::size_t bytes_transferred) {
    /**
     * If an error occurs then no new asynchronous operations are started. This
     * means that all shared_ptr references to the connection object will disappear
     * and the object will be destroyed automatically after this handler returns.
     * The connection class's destructor closes the socket.
     */
    if (e) {
        return;
    }

    /**
     * Since there was no error we can start parsing the input now. The parser
     * fills the object request_ with data.
     */
    boost::tribool result;
    boost::tie(result, boost::tuples::ignore) =
        request_parser_.parse(
            request_,
            buffer_.data(),
            buffer_.data() + bytes_transferred
        );

    /**
     * If result is true the complete request is parsed. If it is false there was
     * an error. If it is indeterminate then the parsing is not complete yet and
     * the program will read more input and append it to the old request_ object.
     */
    if (indeterminate(result)) {
        /* Not finished yet with this request, start reading again. */
        this->start_read();

        /* And don't process the input yet. */
        return;
    }

    /* The handler used to process the reply. */
    swd::reply_handler reply_handler(reply_);

    try {
        if (!result) {
            swd::log::i()->send(swd::warning, "Bad request from "
             + remote_address_.to_string());
            throw swd::exceptions::connection_exception(STATUS_BAD_REQUEST);
        }

        /* Try to add a profile for the request. */
        try {
            swd::profile_ptr profile = database_->get_profile(
                remote_address_.to_string(),
                request_->get_profile_id()
            );

            request_->set_profile(profile);
        } catch (swd::exceptions::database_exception& e) {
            swd::log::i()->send(swd::uncritical_error, e.what());
            throw swd::exceptions::connection_exception(STATUS_BAD_REQUEST);
        }

        /* The handler used to process the incoming request. */
        swd::request_handler request_handler(request_, cache_, storage_);

        /* Only continue processing the reply if it is signed correctly. */
        if (!request_handler.valid_signature()) {
            swd::log::i()->send(swd::warning, "Bad signature from "
             + remote_address_.to_string());
            throw swd::exceptions::connection_exception(STATUS_BAD_SIGNATURE);
        }

        /**
         * Before the request can be processed the input has to be transfered
         * from the encoded json string to a swd::parameters list.
         */
        if (!request_handler.decode()) {
            swd::log::i()->send(swd::warning, "Bad json from "
             + remote_address_.to_string());
            throw swd::exceptions::connection_exception(STATUS_BAD_JSON);
        }

        /* Check profile for outdated cache. */
        swd::profile_ptr profile = request_->get_profile();

        if (profile->is_cache_outdated()) {
            cache_->reset(profile->get_id());
        }

        /* Process the request. */
        std::vector<std::string> threats;

        try {
            swd::parameters parameters = request_->get_parameters();

            /**
             * Check security limitations first.
             */
            int max_params = swd::config::i()->get<int>("max-parameters");

            if ((max_params > -1) && (parameters.size() > max_params)) {
                swd::log::i()->send(swd::notice, "Too many parameters");
                throw swd::exceptions::connection_exception(STATUS_BAD_REQUEST);
            }

            int max_length_path = swd::config::i()->get<int>("max-length-path");
            int max_length_value = swd::config::i()->get<int>("max-length-value");

            if ((max_length_path > -1) || (max_length_value > -1)) {
                for (swd::parameters::iterator it_parameter = parameters.begin();
                 it_parameter != parameters.end(); it_parameter++) {
                    swd::parameter_ptr parameter(*it_parameter);

                    if ((max_length_path > -1) && (parameter->get_path().length() > max_length_path)) {
                        swd::log::i()->send(swd::notice, "Too long parameter path");
                        throw swd::exceptions::connection_exception(STATUS_BAD_REQUEST);
                    }

                    if ((max_length_value > -1) && (parameter->get_value().length() > max_length_value)) {
                        swd::log::i()->send(swd::notice, "Too long parameter value");
                        throw swd::exceptions::connection_exception(STATUS_BAD_REQUEST);
                    }
                }
            }

            if (profile->is_flooding_enabled()) {
                if (database_->is_flooding(request_->get_client_ip(), profile->get_id())) {
                    swd::log::i()->send(swd::notice, "Too many requests");
                    throw swd::exceptions::connection_exception(STATUS_BAD_REQUEST);
                }
            }

            /* Time to analyze the request. */
            request_handler.process();
        } catch (swd::exceptions::database_exception& e) {
            swd::log::i()->send(swd::uncritical_error, e.what());

            /**
             * Problems with the database result in a bad request. If protection
             * is enabled access to the site will not be granted.
             */
            throw swd::exceptions::connection_exception(STATUS_BAD_REQUEST);
        }

        if (profile->get_mode() == MODE_ACTIVE) {
            if (request_->is_threat()) {
                reply_->set_status(STATUS_CRITICAL_ATTACK);
            } else if (request_->has_threats()) {
                reply_->set_threats(request_handler.get_threats());
                reply_->set_status(STATUS_ATTACK);
            } else {
                reply_->set_status(STATUS_OK);
            }
        } else {
            reply_->set_status(STATUS_OK);
        }
    } catch(swd::exceptions::connection_exception& e) {
        if (!request_->get_profile()) {
            reply_->set_status(STATUS_BAD_REQUEST);
        } else if (request_->get_profile()->get_mode() == MODE_ACTIVE) {
            reply_->set_status(e.code());
        } else {
            reply_->set_status(STATUS_OK);
        }
    }

    /* Encode the reply. */
    reply_handler.encode();

    /* Send the answer to the client. */
    if (ssl_) {
        boost::asio::async_write(
            ssl_socket_,
            reply_->to_buffers(),
            strand_.wrap(
                boost::bind(
                    &connection::handle_write,
                    shared_from_this(),
                    boost::asio::placeholders::error
                )
            )
        );
    } else {
        boost::asio::async_write(
            socket_,
            reply_->to_buffers(),
            strand_.wrap(
                boost::bind(
                    &connection::handle_write,
                    shared_from_this(),
                    boost::asio::placeholders::error
                )
            )
        );
    }
}