예제 #1
0
amqp_channel_t ChannelImpl::GetNextChannelId()
{
    int max_channels = amqp_get_channel_max(m_connection);
    int channel_count = static_cast<int>(m_open_channels.size());
    if (0 == max_channels)
    {
        if (std::numeric_limits<boost::uint16_t>::max() <= channel_count)
        {
            throw std::runtime_error("Too many channels open");
        }
    }
    else if (max_channels <= channel_count)
    {
        throw std::runtime_error("Too many channels open");
    }

    while (m_open_channels.end() != m_open_channels.find(++m_next_channel_id)) {
        /* Empty */
    }

    m_open_channels.insert(std::make_pair(m_next_channel_id, frame_queue_t()));
    return m_next_channel_id;
}
amqp_connection_resource *connection_resource_constructor(amqp_connection_params *params, zend_bool persistent TSRMLS_DC)
{
	struct timeval tv = {0};
	struct timeval *tv_ptr = &tv;

	char *std_datetime;
	amqp_table_entry_t client_properties_entries[5];
	amqp_table_t       client_properties_table;

	amqp_table_entry_t custom_properties_entries[1];
	amqp_table_t       custom_properties_table;

	amqp_connection_resource *resource;

	/* Allocate space for the connection resource */
	resource = (amqp_connection_resource *)pecalloc(1, sizeof(amqp_connection_resource), persistent);

	/* Create the connection */
	resource->connection_state = amqp_new_connection();

	/* Create socket object */
	resource->socket = amqp_tcp_socket_new(resource->connection_state);

	if (params->connect_timeout > 0) {
		tv.tv_sec = (long int) params->connect_timeout;
		tv.tv_usec = (long int) ((params->connect_timeout - tv.tv_sec) * 1000000);
	} else {
		tv_ptr = NULL;
	}

	/* Try to connect and verify that no error occurred */
	if (amqp_socket_open_noblock(resource->socket, params->host, params->port, tv_ptr)) {

		zend_throw_exception(amqp_connection_exception_class_entry, "Socket error: could not connect to host.", 0 TSRMLS_CC);

		connection_resource_destructor(resource, persistent TSRMLS_CC);

		return NULL;
	}

	if (!php_amqp_set_resource_read_timeout(resource, params->read_timeout TSRMLS_CC)) {
		connection_resource_destructor(resource, persistent TSRMLS_CC);
		return NULL;
	}

	if (!php_amqp_set_resource_write_timeout(resource, params->write_timeout TSRMLS_CC)) {
		connection_resource_destructor(resource, persistent TSRMLS_CC);
		return NULL;
	}

	std_datetime = php_std_date(time(NULL) TSRMLS_CC);

	client_properties_entries[0].key               = amqp_cstring_bytes("type");
	client_properties_entries[0].value.kind        = AMQP_FIELD_KIND_UTF8;
	client_properties_entries[0].value.value.bytes = amqp_cstring_bytes("php-amqp extension");

	client_properties_entries[1].key               = amqp_cstring_bytes("version");
	client_properties_entries[1].value.kind        = AMQP_FIELD_KIND_UTF8;
	client_properties_entries[1].value.value.bytes = amqp_cstring_bytes(PHP_AMQP_VERSION);

	client_properties_entries[2].key               = amqp_cstring_bytes("revision");
	client_properties_entries[2].value.kind        = AMQP_FIELD_KIND_UTF8;
	client_properties_entries[2].value.value.bytes = amqp_cstring_bytes(PHP_AMQP_REVISION);

	client_properties_entries[3].key               = amqp_cstring_bytes("connection type");
	client_properties_entries[3].value.kind        = AMQP_FIELD_KIND_UTF8;
	client_properties_entries[3].value.value.bytes = amqp_cstring_bytes(persistent ? "persistent" : "transient");

	client_properties_entries[4].key               = amqp_cstring_bytes("connection started");
	client_properties_entries[4].value.kind        = AMQP_FIELD_KIND_UTF8;
	client_properties_entries[4].value.value.bytes = amqp_cstring_bytes(std_datetime);

	client_properties_table.entries = client_properties_entries;
	client_properties_table.num_entries = sizeof(client_properties_entries) / sizeof(amqp_table_entry_t);

	custom_properties_entries[0].key               = amqp_cstring_bytes("client");
	custom_properties_entries[0].value.kind        = AMQP_FIELD_KIND_TABLE;
	custom_properties_entries[0].value.value.table = client_properties_table;

	custom_properties_table.entries     = custom_properties_entries;
	custom_properties_table.num_entries = sizeof(custom_properties_entries) / sizeof(amqp_table_entry_t);

	/* We can assume that connection established here but it is not true, real handshake goes during login */

	assert(params->frame_max > 0);

	amqp_rpc_reply_t res = amqp_login_with_properties(
		resource->connection_state,
		params->vhost,
		params->channel_max,
		params->frame_max,
		params->heartbeat,
		&custom_properties_table,
		AMQP_SASL_METHOD_PLAIN,
		params->login,
		params->password
	);

	efree(std_datetime);

	if (res.reply_type != AMQP_RESPONSE_NORMAL) {
		char *message, *long_message;

		php_amqp_connection_resource_error(res, &message, resource, 0 TSRMLS_CC);

		spprintf(&long_message, 0, "%s - Potential login failure.", message);
		zend_throw_exception(amqp_connection_exception_class_entry, long_message, 0 TSRMLS_CC);

		efree(message);
		efree(long_message);

		/* https://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf
		 *
		 * 2.2.4 The Connection Class:
		 * ... a peer that detects an error MUST close the socket without sending any further data.
		 *
		 * 4.10.2 Denial of Service Attacks:
		 * ... The general response to any exceptional condition in the connection negotiation is to pause that connection
		 * (presumably a thread) for a period of several seconds and then to close the network connection. This
		 * includes syntax errors, over-sized data, and failed attempts to authenticate.
		 */
		connection_resource_destructor(resource, persistent TSRMLS_CC);
		return NULL;
	}

	/* Allocate space for the channel slots in the ring buffer */
    resource->max_slots = (amqp_channel_t) amqp_get_channel_max(resource->connection_state);
	assert(resource->max_slots > 0);

	resource->slots = (amqp_channel_resource **)pecalloc(resource->max_slots + 1, sizeof(amqp_channel_object*), persistent);

	resource->is_connected = '\1';

	return resource;
}