init
This commit is contained in:
190
librabbitmq/CMakeLists.txt
Normal file
190
librabbitmq/CMakeLists.txt
Normal file
@@ -0,0 +1,190 @@
|
||||
# Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
# SPDX-License-Identifier: mit
|
||||
|
||||
if (ENABLE_SSL_SUPPORT)
|
||||
SET(AMQP_SSL_SOCKET_SHIM_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include/amqp_ssl_socket.h)
|
||||
set(AMQP_SSL_SOCKET_H_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include/rabbitmq-c/ssl_socket.h)
|
||||
|
||||
set(AMQP_SSL_SRCS
|
||||
amqp_openssl.c
|
||||
amqp_openssl_bio.c
|
||||
amqp_openssl_bio.h
|
||||
)
|
||||
set(AMQP_SSL_LIBS OpenSSL::SSL)
|
||||
if (APPLE)
|
||||
# Apple has deprecated OpenSSL in 10.7+. This disables that warning.
|
||||
set_source_files_properties(${AMQP_SSL_SRCS}
|
||||
PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations)
|
||||
endif()
|
||||
|
||||
if (WIN32 AND NOT CMAKE_USE_PTHREADS_INIT)
|
||||
set(AMQP_SSL_SRCS ${AMQP_SSL_SRCS} win32/threads.h win32/threads.c)
|
||||
set(SSL_INCLUDE_DIRS win32)
|
||||
else()
|
||||
set(AMQP_SSL_SRCS ${AMQP_SSL_SRCS} unix/threads.h)
|
||||
set(SSL_INCLUDE_DIRS unix)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(PUBLIC_INCLUDE_DIRS
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
set(PRIVATE_INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${SSL_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(RMQ_SOURCES
|
||||
../include/amqp.h
|
||||
../include/amqp_framing.h
|
||||
${AMQP_SSL_SOCKET_SHIM_PATH}
|
||||
../include/amqp_tcp_socket.h
|
||||
../include/rabbitmq-c/amqp.h
|
||||
../include/rabbitmq-c/framing.h
|
||||
${AMQP_SSL_SOCKET_H_PATH}
|
||||
../include/rabbitmq-c/tcp_socket.h
|
||||
amqp_api.c
|
||||
amqp_connection.c
|
||||
amqp_consumer.c
|
||||
amqp_framing.c
|
||||
amqp_mem.c
|
||||
${AMQP_SSL_SRCS}
|
||||
amqp_private.h
|
||||
amqp_socket.c
|
||||
amqp_socket.h
|
||||
amqp_table.c
|
||||
amqp_table.h
|
||||
amqp_tcp_socket.c
|
||||
amqp_time.c
|
||||
amqp_time.h
|
||||
amqp_url.c
|
||||
)
|
||||
|
||||
set(RMQ_LIBRARIES ${AMQP_SSL_LIBS} ${SOCKET_LIBRARIES} ${LIBRT} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
if (NOT APPLE)
|
||||
set(CMAKE_INSTALL_RPATH $ORIGIN)
|
||||
endif()
|
||||
|
||||
add_library(rabbitmq SHARED)
|
||||
set(RMQ_GEN_EXPORT_TARGET rabbitmq)
|
||||
|
||||
target_sources(rabbitmq PRIVATE ${RMQ_SOURCES})
|
||||
|
||||
target_include_directories(rabbitmq
|
||||
PUBLIC ${PUBLIC_INCLUDE_DIRS}
|
||||
PRIVATE ${PRIVATE_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_definitions(rabbitmq PRIVATE -DHAVE_CONFIG_H)
|
||||
|
||||
target_link_libraries(rabbitmq PRIVATE ${RMQ_LIBRARIES})
|
||||
|
||||
set_target_properties(rabbitmq PROPERTIES
|
||||
VERSION ${RMQ_VERSION}
|
||||
SOVERSION ${RMQ_SOVERSION}
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
set_target_properties(rabbitmq PROPERTIES
|
||||
MACHO_CURRENT_VERSION ${RMQ_SOVERSION}.${RMQ_SOVERSION_AGE}.${RMQ_SOVERSION_REVISION}
|
||||
MACHO_COMPATIBILITY_VERSION ${RMQ_SOVERSION}
|
||||
)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
set_target_properties(rabbitmq PROPERTIES OUTPUT_NAME rabbitmq.${RMQ_SOVERSION})
|
||||
endif()
|
||||
|
||||
install(TARGETS rabbitmq EXPORT "${targets_export_name}"
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
COMPONENT rabbitmq-c-runtime
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
COMPONENT rabbitmq-c-runtime
|
||||
NAMELINK_COMPONENT rabbitmq-c-development
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
COMPONENT rabbitmq-c-development
|
||||
)
|
||||
|
||||
add_library(rabbitmq::rabbitmq ALIAS rabbitmq)
|
||||
endif()
|
||||
|
||||
if(BUILD_STATIC_LIBS)
|
||||
add_library(rabbitmq-static STATIC)
|
||||
|
||||
target_sources(rabbitmq-static PRIVATE ${RMQ_SOURCES})
|
||||
if (NOT BUILD_SHARED_LIBS)
|
||||
set(RMQ_GEN_EXPORT_TARGET rabbitmq-static)
|
||||
endif()
|
||||
|
||||
target_include_directories(rabbitmq-static
|
||||
PUBLIC ${PUBLIC_INCLUDE_DIRS}
|
||||
PRIVATE ${PRIVATE_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_definitions(rabbitmq-static
|
||||
PUBLIC -DAMQP_STATIC
|
||||
PRIVATE -DHAVE_CONFIG_H
|
||||
)
|
||||
|
||||
target_link_libraries(rabbitmq-static PRIVATE ${RMQ_LIBRARIES})
|
||||
|
||||
set_target_properties(rabbitmq-static PROPERTIES
|
||||
VERSION ${RMQ_VERSION}
|
||||
SOVERSION ${RMQ_SOVERSION}
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
set_target_properties(rabbitmq-static PROPERTIES
|
||||
MACHO_CURRENT_VERSION ${RMQ_SOVERSION}.${RMQ_SOVERSION_AGE}.${RMQ_SOVERSION_REVISION}
|
||||
MACHO_COMPATIBILITY_VERSION ${RMQ_SOVERSION}
|
||||
)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
set_target_properties(rabbitmq-static PROPERTIES OUTPUT_NAME librabbitmq.${RMQ_SOVERSION})
|
||||
else()
|
||||
set_target_properties(rabbitmq-static PROPERTIES OUTPUT_NAME rabbitmq)
|
||||
endif()
|
||||
|
||||
if(INSTALL_STATIC_LIBS)
|
||||
install(TARGETS rabbitmq-static EXPORT "${targets_export_name}"
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
COMPONENT rabbitmq-c-development
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(rabbitmq::rabbitmq-static ALIAS rabbitmq-static)
|
||||
endif()
|
||||
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(${RMQ_GEN_EXPORT_TARGET}
|
||||
BASE_NAME AMQP
|
||||
EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/../include/rabbitmq-c/export.h
|
||||
STATIC_DEFINE AMQP_STATIC
|
||||
INCLUDE_GUARD_NAME RABBITMQ_C_EXPORT_H
|
||||
)
|
||||
|
||||
install(FILES
|
||||
../include/amqp.h
|
||||
../include/amqp_framing.h
|
||||
../include/amqp_tcp_socket.h
|
||||
${AMQP_SSL_SOCKET_SHIM_PATH}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
COMPONENT rabbitmq-c-development
|
||||
)
|
||||
|
||||
install(FILES
|
||||
../include/rabbitmq-c/amqp.h
|
||||
../include/rabbitmq-c/framing.h
|
||||
../include/rabbitmq-c/tcp_socket.h
|
||||
${AMQP_SSL_SOCKET_H_PATH}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../include/rabbitmq-c/export.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rabbitmq-c
|
||||
COMPONENT rabbitmq-c-development
|
||||
)
|
||||
421
librabbitmq/amqp_api.c
Normal file
421
librabbitmq/amqp_api.c
Normal file
@@ -0,0 +1,421 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* MSVC complains about sprintf being deprecated in favor of sprintf_s */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
/* MSVC complains about strdup being deprecated in favor of _strdup */
|
||||
#define _CRT_NONSTDC_NO_DEPRECATE
|
||||
#endif
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include "amqp_time.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ERROR_MASK (0x00FF)
|
||||
#define ERROR_CATEGORY_MASK (0xFF00)
|
||||
|
||||
enum error_category_enum_ { EC_base = 0, EC_tcp = 1, EC_ssl = 2 };
|
||||
|
||||
static const char *base_error_strings[] = {
|
||||
/* AMQP_STATUS_OK 0x0 */
|
||||
"operation completed successfully",
|
||||
/* AMQP_STATUS_NO_MEMORY -0x0001 */
|
||||
"could not allocate memory",
|
||||
/* AMQP_STATUS_BAD_AQMP_DATA -0x0002 */
|
||||
"invalid AMQP data",
|
||||
/* AMQP_STATUS_UNKNOWN_CLASS -0x0003 */
|
||||
"unknown AMQP class id",
|
||||
/* AMQP_STATUS_UNKNOWN_METHOD -0x0004 */
|
||||
"unknown AMQP method id",
|
||||
/* AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED -0x0005 */
|
||||
"hostname lookup failed",
|
||||
/* AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION -0x0006 */
|
||||
"incompatible AMQP version",
|
||||
/* AMQP_STATUS_CONNECTION_CLOSED -0x0007 */
|
||||
"connection closed unexpectedly",
|
||||
/* AMQP_STATUS_BAD_AMQP_URL -0x0008 */
|
||||
"could not parse AMQP URL",
|
||||
/* AMQP_STATUS_SOCKET_ERROR -0x0009 */
|
||||
"a socket error occurred",
|
||||
/* AMQP_STATUS_INVALID_PARAMETER -0x000A */
|
||||
"invalid parameter",
|
||||
/* AMQP_STATUS_TABLE_TOO_BIG -0x000B */
|
||||
"table too large for buffer",
|
||||
/* AMQP_STATUS_WRONG_METHOD -0x000C */
|
||||
"unexpected method received",
|
||||
/* AMQP_STATUS_TIMEOUT -0x000D */
|
||||
"request timed out",
|
||||
/* AMQP_STATUS_TIMER_FAILED -0x000E */
|
||||
"system timer has failed",
|
||||
/* AMQP_STATUS_HEARTBEAT_TIMEOUT -0x000F */
|
||||
"heartbeat timeout, connection closed",
|
||||
/* AMQP_STATUS_UNEXPECTED STATE -0x0010 */
|
||||
"unexpected protocol state",
|
||||
/* AMQP_STATUS_SOCKET_CLOSED -0x0011 */
|
||||
"socket is closed",
|
||||
/* AMQP_STATUS_SOCKET_INUSE -0x0012 */
|
||||
"socket already open",
|
||||
/* AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD -0x00013 */
|
||||
"unsupported sasl method requested",
|
||||
/* AMQP_STATUS_UNSUPPORTED -0x0014 */
|
||||
"parameter value is unsupported"};
|
||||
|
||||
static const char *tcp_error_strings[] = {
|
||||
/* AMQP_STATUS_TCP_ERROR -0x0100 */
|
||||
"a socket error occurred",
|
||||
/* AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR -0x0101 */
|
||||
"socket library initialization failed"};
|
||||
|
||||
static const char *ssl_error_strings[] = {
|
||||
/* AMQP_STATUS_SSL_ERROR -0x0200 */
|
||||
"a SSL error occurred",
|
||||
/* AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED -0x0201 */
|
||||
"SSL hostname verification failed",
|
||||
/* AMQP_STATUS_SSL_PEER_VERIFY_FAILED -0x0202 */
|
||||
"SSL peer cert verification failed",
|
||||
/* AMQP_STATUS_SSL_CONNECTION_FAILED -0x0203 */
|
||||
"SSL handshake failed",
|
||||
/* AMQP_STATUS_SSL_SET_ENGINE_FAILED -0x0204 */
|
||||
"SSL setting engine failed",
|
||||
/* AMQP_STATUS_SSL_UNIMPLEMENTED -0x0204 */
|
||||
"SSL API is not implemented"};
|
||||
|
||||
static const char *unknown_error_string = "(unknown error)";
|
||||
|
||||
const char *amqp_error_string2(int code) {
|
||||
const char *error_string;
|
||||
size_t category = (((-code) & ERROR_CATEGORY_MASK) >> 8);
|
||||
size_t error = (-code) & ERROR_MASK;
|
||||
|
||||
switch (category) {
|
||||
case EC_base:
|
||||
if (error < (sizeof(base_error_strings) / sizeof(char *))) {
|
||||
error_string = base_error_strings[error];
|
||||
} else {
|
||||
error_string = unknown_error_string;
|
||||
}
|
||||
break;
|
||||
|
||||
case EC_tcp:
|
||||
if (error < (sizeof(tcp_error_strings) / sizeof(char *))) {
|
||||
error_string = tcp_error_strings[error];
|
||||
} else {
|
||||
error_string = unknown_error_string;
|
||||
}
|
||||
break;
|
||||
|
||||
case EC_ssl:
|
||||
if (error < (sizeof(ssl_error_strings) / sizeof(char *))) {
|
||||
error_string = ssl_error_strings[error];
|
||||
} else {
|
||||
error_string = unknown_error_string;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
error_string = unknown_error_string;
|
||||
break;
|
||||
}
|
||||
|
||||
return error_string;
|
||||
}
|
||||
|
||||
char *amqp_error_string(int code) {
|
||||
/* Previously sometimes clients had to flip the sign on a return value from a
|
||||
* function to get the correct error code. Now, all error codes are negative.
|
||||
* To keep people's legacy code running correctly, we map all error codes to
|
||||
* negative values.
|
||||
*
|
||||
* This is only done with this deprecated function.
|
||||
*/
|
||||
if (code > 0) {
|
||||
code = -code;
|
||||
}
|
||||
return strdup(amqp_error_string2(code));
|
||||
}
|
||||
|
||||
void amqp_abort(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fputc('\n', stderr);
|
||||
abort();
|
||||
}
|
||||
|
||||
const amqp_bytes_t amqp_empty_bytes = {0, NULL};
|
||||
const amqp_table_t amqp_empty_table = {0, NULL};
|
||||
const amqp_array_t amqp_empty_array = {0, NULL};
|
||||
|
||||
int amqp_basic_publish(amqp_connection_state_t state, amqp_channel_t channel,
|
||||
amqp_bytes_t exchange, amqp_bytes_t routing_key,
|
||||
amqp_boolean_t mandatory, amqp_boolean_t immediate,
|
||||
amqp_basic_properties_t const *properties,
|
||||
amqp_bytes_t body) {
|
||||
amqp_frame_t f;
|
||||
size_t body_offset;
|
||||
size_t usable_body_payload_size =
|
||||
state->frame_max - (HEADER_SIZE + FOOTER_SIZE);
|
||||
int res;
|
||||
int flagz;
|
||||
|
||||
amqp_basic_publish_t m;
|
||||
amqp_basic_properties_t default_properties;
|
||||
|
||||
m.exchange = exchange;
|
||||
m.routing_key = routing_key;
|
||||
m.mandatory = mandatory;
|
||||
m.immediate = immediate;
|
||||
m.ticket = 0;
|
||||
|
||||
/* TODO(alanxz): this heartbeat check is happening in the wrong place, it
|
||||
* should really be done in amqp_try_send/writev */
|
||||
res = amqp_time_has_past(state->next_recv_heartbeat);
|
||||
if (AMQP_STATUS_TIMER_FAILURE == res) {
|
||||
return res;
|
||||
} else if (AMQP_STATUS_TIMEOUT == res) {
|
||||
res = amqp_try_recv(state);
|
||||
if (AMQP_STATUS_TIMEOUT == res) {
|
||||
return AMQP_STATUS_HEARTBEAT_TIMEOUT;
|
||||
} else if (AMQP_STATUS_OK != res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
res = amqp_send_method_inner(state, channel, AMQP_BASIC_PUBLISH_METHOD, &m,
|
||||
AMQP_SF_MORE, amqp_time_infinite());
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (properties == NULL) {
|
||||
memset(&default_properties, 0, sizeof(default_properties));
|
||||
properties = &default_properties;
|
||||
}
|
||||
|
||||
f.frame_type = AMQP_FRAME_HEADER;
|
||||
f.channel = channel;
|
||||
f.payload.properties.class_id = AMQP_BASIC_CLASS;
|
||||
f.payload.properties.body_size = body.len;
|
||||
f.payload.properties.decoded = (void *)properties;
|
||||
|
||||
if (body.len > 0) {
|
||||
flagz = AMQP_SF_MORE;
|
||||
} else {
|
||||
flagz = AMQP_SF_NONE;
|
||||
}
|
||||
res = amqp_send_frame_inner(state, &f, flagz, amqp_time_infinite());
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
body_offset = 0;
|
||||
while (body_offset < body.len) {
|
||||
size_t remaining = body.len - body_offset;
|
||||
|
||||
if (remaining == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
f.frame_type = AMQP_FRAME_BODY;
|
||||
f.channel = channel;
|
||||
f.payload.body_fragment.bytes = amqp_offset(body.bytes, body_offset);
|
||||
if (remaining >= usable_body_payload_size) {
|
||||
f.payload.body_fragment.len = usable_body_payload_size;
|
||||
flagz = AMQP_SF_MORE;
|
||||
} else {
|
||||
f.payload.body_fragment.len = remaining;
|
||||
flagz = AMQP_SF_NONE;
|
||||
}
|
||||
|
||||
body_offset += f.payload.body_fragment.len;
|
||||
res = amqp_send_frame_inner(state, &f, flagz, amqp_time_infinite());
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
amqp_rpc_reply_t amqp_channel_close(amqp_connection_state_t state,
|
||||
amqp_channel_t channel, int code) {
|
||||
char codestr[13];
|
||||
amqp_method_number_t replies[2] = {AMQP_CHANNEL_CLOSE_OK_METHOD, 0};
|
||||
amqp_channel_close_t req;
|
||||
|
||||
if (code < 0 || code > UINT16_MAX) {
|
||||
return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
req.reply_code = (uint16_t)code;
|
||||
req.reply_text.bytes = codestr;
|
||||
req.reply_text.len = sprintf(codestr, "%d", code);
|
||||
req.class_id = 0;
|
||||
req.method_id = 0;
|
||||
|
||||
return amqp_simple_rpc(state, channel, AMQP_CHANNEL_CLOSE_METHOD, replies,
|
||||
&req);
|
||||
}
|
||||
|
||||
amqp_rpc_reply_t amqp_connection_close(amqp_connection_state_t state,
|
||||
int code) {
|
||||
char codestr[13];
|
||||
amqp_method_number_t replies[2] = {AMQP_CONNECTION_CLOSE_OK_METHOD, 0};
|
||||
amqp_channel_close_t req;
|
||||
|
||||
if (code < 0 || code > UINT16_MAX) {
|
||||
return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
req.reply_code = (uint16_t)code;
|
||||
req.reply_text.bytes = codestr;
|
||||
req.reply_text.len = sprintf(codestr, "%d", code);
|
||||
req.class_id = 0;
|
||||
req.method_id = 0;
|
||||
|
||||
return amqp_simple_rpc(state, 0, AMQP_CONNECTION_CLOSE_METHOD, replies, &req);
|
||||
}
|
||||
|
||||
int amqp_basic_ack(amqp_connection_state_t state, amqp_channel_t channel,
|
||||
uint64_t delivery_tag, amqp_boolean_t multiple) {
|
||||
amqp_basic_ack_t m;
|
||||
m.delivery_tag = delivery_tag;
|
||||
m.multiple = multiple;
|
||||
return amqp_send_method(state, channel, AMQP_BASIC_ACK_METHOD, &m);
|
||||
}
|
||||
|
||||
amqp_rpc_reply_t amqp_basic_get(amqp_connection_state_t state,
|
||||
amqp_channel_t channel, amqp_bytes_t queue,
|
||||
amqp_boolean_t no_ack) {
|
||||
amqp_method_number_t replies[] = {AMQP_BASIC_GET_OK_METHOD,
|
||||
AMQP_BASIC_GET_EMPTY_METHOD, 0};
|
||||
amqp_basic_get_t req;
|
||||
req.ticket = 0;
|
||||
req.queue = queue;
|
||||
req.no_ack = no_ack;
|
||||
|
||||
state->most_recent_api_result =
|
||||
amqp_simple_rpc(state, channel, AMQP_BASIC_GET_METHOD, replies, &req);
|
||||
return state->most_recent_api_result;
|
||||
}
|
||||
|
||||
int amqp_basic_reject(amqp_connection_state_t state, amqp_channel_t channel,
|
||||
uint64_t delivery_tag, amqp_boolean_t requeue) {
|
||||
amqp_basic_reject_t req;
|
||||
req.delivery_tag = delivery_tag;
|
||||
req.requeue = requeue;
|
||||
return amqp_send_method(state, channel, AMQP_BASIC_REJECT_METHOD, &req);
|
||||
}
|
||||
|
||||
int amqp_basic_nack(amqp_connection_state_t state, amqp_channel_t channel,
|
||||
uint64_t delivery_tag, amqp_boolean_t multiple,
|
||||
amqp_boolean_t requeue) {
|
||||
amqp_basic_nack_t req;
|
||||
req.delivery_tag = delivery_tag;
|
||||
req.multiple = multiple;
|
||||
req.requeue = requeue;
|
||||
return amqp_send_method(state, channel, AMQP_BASIC_NACK_METHOD, &req);
|
||||
}
|
||||
|
||||
struct timeval *amqp_get_handshake_timeout(amqp_connection_state_t state) {
|
||||
return state->handshake_timeout;
|
||||
}
|
||||
|
||||
int amqp_set_handshake_timeout(amqp_connection_state_t state,
|
||||
const struct timeval *timeout) {
|
||||
if (timeout) {
|
||||
if (timeout->tv_sec < 0 || timeout->tv_usec < 0) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
state->internal_handshake_timeout = *timeout;
|
||||
state->handshake_timeout = &state->internal_handshake_timeout;
|
||||
} else {
|
||||
state->handshake_timeout = NULL;
|
||||
}
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
struct timeval *amqp_get_rpc_timeout(amqp_connection_state_t state) {
|
||||
return state->rpc_timeout;
|
||||
}
|
||||
|
||||
int amqp_set_rpc_timeout(amqp_connection_state_t state,
|
||||
const struct timeval *timeout) {
|
||||
if (timeout) {
|
||||
if (timeout->tv_sec < 0 || timeout->tv_usec < 0) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
state->rpc_timeout = &state->internal_rpc_timeout;
|
||||
*state->rpc_timeout = *timeout;
|
||||
} else {
|
||||
state->rpc_timeout = NULL;
|
||||
}
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
amqp_rpc_reply_t amqp_publisher_confirm_wait(amqp_connection_state_t state,
|
||||
const struct timeval *timeout,
|
||||
amqp_publisher_confirm_t *result) {
|
||||
int res;
|
||||
amqp_frame_t frame;
|
||||
amqp_rpc_reply_t ret;
|
||||
|
||||
memset(&ret, 0x0, sizeof(ret));
|
||||
memset(result, 0x0, sizeof(amqp_publisher_confirm_t));
|
||||
|
||||
res = amqp_simple_wait_frame_noblock(state, &frame, timeout);
|
||||
|
||||
if (AMQP_STATUS_OK != res) {
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = res;
|
||||
return ret;
|
||||
} else if (AMQP_FRAME_METHOD != frame.frame_type ||
|
||||
(AMQP_BASIC_ACK_METHOD != frame.payload.method.id &&
|
||||
AMQP_BASIC_NACK_METHOD != frame.payload.method.id &&
|
||||
AMQP_BASIC_REJECT_METHOD != frame.payload.method.id)) {
|
||||
amqp_put_back_frame(state, &frame);
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = AMQP_STATUS_UNEXPECTED_STATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (frame.payload.method.id) {
|
||||
case AMQP_BASIC_ACK_METHOD:
|
||||
memcpy(&(result->payload.ack), frame.payload.method.decoded,
|
||||
sizeof(amqp_basic_ack_t));
|
||||
break;
|
||||
|
||||
case AMQP_BASIC_NACK_METHOD:
|
||||
memcpy(&(result->payload.nack), frame.payload.method.decoded,
|
||||
sizeof(amqp_basic_nack_t));
|
||||
break;
|
||||
|
||||
case AMQP_BASIC_REJECT_METHOD:
|
||||
memcpy(&(result->payload.reject), frame.payload.method.decoded,
|
||||
sizeof(amqp_basic_reject_t));
|
||||
break;
|
||||
|
||||
default:
|
||||
amqp_put_back_frame(state, &frame);
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = AMQP_STATUS_UNSUPPORTED;
|
||||
return ret;
|
||||
}
|
||||
result->method = frame.payload.method.id;
|
||||
result->channel = frame.channel;
|
||||
ret.reply_type = AMQP_RESPONSE_NORMAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
571
librabbitmq/amqp_connection.c
Normal file
571
librabbitmq/amqp_connection.c
Normal file
@@ -0,0 +1,571 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include "amqp_time.h"
|
||||
#include "rabbitmq-c/tcp_socket.h"
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef AMQP_INITIAL_FRAME_POOL_PAGE_SIZE
|
||||
#define AMQP_INITIAL_FRAME_POOL_PAGE_SIZE 65536
|
||||
#endif
|
||||
|
||||
#ifndef AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE
|
||||
#define AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE 131072
|
||||
#endif
|
||||
|
||||
#ifndef AMQP_DEFAULT_LOGIN_TIMEOUT_SEC
|
||||
#define AMQP_DEFAULT_LOGIN_TIMEOUT_SEC 12
|
||||
#endif
|
||||
|
||||
#define ENFORCE_STATE(statevec, statenum) \
|
||||
{ \
|
||||
amqp_connection_state_t _check_state = (statevec); \
|
||||
amqp_connection_state_enum _wanted_state = (statenum); \
|
||||
if (_check_state->state != _wanted_state) \
|
||||
amqp_abort( \
|
||||
"Programming error: invalid AMQP connection state: expected %d, " \
|
||||
"got %d", \
|
||||
_wanted_state, _check_state->state); \
|
||||
}
|
||||
|
||||
amqp_connection_state_t amqp_new_connection(void) {
|
||||
int res;
|
||||
amqp_connection_state_t state = (amqp_connection_state_t)calloc(
|
||||
1, sizeof(struct amqp_connection_state_t_));
|
||||
|
||||
if (state == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = amqp_tune_connection(state, 0, AMQP_INITIAL_FRAME_POOL_PAGE_SIZE, 0);
|
||||
if (0 != res) {
|
||||
goto out_nomem;
|
||||
}
|
||||
|
||||
state->inbound_buffer.bytes = state->header_buffer;
|
||||
state->inbound_buffer.len = sizeof(state->header_buffer);
|
||||
|
||||
state->state = CONNECTION_STATE_INITIAL;
|
||||
/* the server protocol version response is 8 bytes, which conveniently
|
||||
is also the minimum frame size */
|
||||
state->target_size = 8;
|
||||
|
||||
state->sock_inbound_buffer.len = AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE;
|
||||
state->sock_inbound_buffer.bytes =
|
||||
malloc(AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE);
|
||||
if (state->sock_inbound_buffer.bytes == NULL) {
|
||||
goto out_nomem;
|
||||
}
|
||||
|
||||
init_amqp_pool(&state->properties_pool, 512);
|
||||
|
||||
/* Use address of the internal_handshake_timeout object by default. */
|
||||
state->internal_handshake_timeout.tv_sec = AMQP_DEFAULT_LOGIN_TIMEOUT_SEC;
|
||||
state->internal_handshake_timeout.tv_usec = 0;
|
||||
state->handshake_timeout = &state->internal_handshake_timeout;
|
||||
|
||||
return state;
|
||||
|
||||
out_nomem:
|
||||
free(state->sock_inbound_buffer.bytes);
|
||||
free(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int amqp_get_sockfd(amqp_connection_state_t state) {
|
||||
return state->socket ? amqp_socket_get_sockfd(state->socket) : -1;
|
||||
}
|
||||
|
||||
void amqp_set_sockfd(amqp_connection_state_t state, int sockfd) {
|
||||
amqp_socket_t *socket = amqp_tcp_socket_new(state);
|
||||
if (!socket) {
|
||||
amqp_abort("%s", strerror(errno));
|
||||
}
|
||||
amqp_tcp_socket_set_sockfd(socket, sockfd);
|
||||
}
|
||||
|
||||
void amqp_set_socket(amqp_connection_state_t state, amqp_socket_t *socket) {
|
||||
amqp_socket_delete(state->socket);
|
||||
state->socket = socket;
|
||||
}
|
||||
|
||||
amqp_socket_t *amqp_get_socket(amqp_connection_state_t state) {
|
||||
return state->socket;
|
||||
}
|
||||
|
||||
int amqp_tune_connection(amqp_connection_state_t state, int channel_max,
|
||||
int frame_max, int heartbeat) {
|
||||
void *newbuf;
|
||||
int res;
|
||||
|
||||
ENFORCE_STATE(state, CONNECTION_STATE_IDLE);
|
||||
|
||||
state->channel_max = channel_max;
|
||||
state->frame_max = frame_max;
|
||||
|
||||
state->heartbeat = heartbeat;
|
||||
if (0 > state->heartbeat) {
|
||||
state->heartbeat = 0;
|
||||
}
|
||||
|
||||
res = amqp_time_s_from_now(&state->next_send_heartbeat,
|
||||
amqp_heartbeat_send(state));
|
||||
if (AMQP_STATUS_OK != res) {
|
||||
return res;
|
||||
}
|
||||
res = amqp_time_s_from_now(&state->next_recv_heartbeat,
|
||||
amqp_heartbeat_recv(state));
|
||||
if (AMQP_STATUS_OK != res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
state->outbound_buffer.len = frame_max;
|
||||
newbuf = realloc(state->outbound_buffer.bytes, frame_max);
|
||||
if (newbuf == NULL) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
state->outbound_buffer.bytes = newbuf;
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
int amqp_get_channel_max(amqp_connection_state_t state) {
|
||||
return state->channel_max;
|
||||
}
|
||||
|
||||
int amqp_get_frame_max(amqp_connection_state_t state) {
|
||||
return state->frame_max;
|
||||
}
|
||||
|
||||
int amqp_get_heartbeat(amqp_connection_state_t state) {
|
||||
return state->heartbeat;
|
||||
}
|
||||
|
||||
int amqp_destroy_connection(amqp_connection_state_t state) {
|
||||
int status = AMQP_STATUS_OK;
|
||||
if (state) {
|
||||
int i;
|
||||
for (i = 0; i < POOL_TABLE_SIZE; ++i) {
|
||||
amqp_pool_table_entry_t *entry = state->pool_table[i];
|
||||
while (NULL != entry) {
|
||||
amqp_pool_table_entry_t *todelete = entry;
|
||||
empty_amqp_pool(&entry->pool);
|
||||
entry = entry->next;
|
||||
free(todelete);
|
||||
}
|
||||
}
|
||||
|
||||
free(state->outbound_buffer.bytes);
|
||||
free(state->sock_inbound_buffer.bytes);
|
||||
amqp_socket_delete(state->socket);
|
||||
empty_amqp_pool(&state->properties_pool);
|
||||
free(state);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void return_to_idle(amqp_connection_state_t state) {
|
||||
state->inbound_buffer.len = sizeof(state->header_buffer);
|
||||
state->inbound_buffer.bytes = state->header_buffer;
|
||||
state->inbound_offset = 0;
|
||||
state->target_size = HEADER_SIZE;
|
||||
state->state = CONNECTION_STATE_IDLE;
|
||||
}
|
||||
|
||||
static size_t consume_data(amqp_connection_state_t state,
|
||||
amqp_bytes_t *received_data) {
|
||||
/* how much data is available and will fit? */
|
||||
size_t bytes_consumed = state->target_size - state->inbound_offset;
|
||||
if (received_data->len < bytes_consumed) {
|
||||
bytes_consumed = received_data->len;
|
||||
}
|
||||
|
||||
memcpy(amqp_offset(state->inbound_buffer.bytes, state->inbound_offset),
|
||||
received_data->bytes, bytes_consumed);
|
||||
state->inbound_offset += bytes_consumed;
|
||||
received_data->bytes = amqp_offset(received_data->bytes, bytes_consumed);
|
||||
received_data->len -= bytes_consumed;
|
||||
|
||||
return bytes_consumed;
|
||||
}
|
||||
|
||||
int amqp_handle_input(amqp_connection_state_t state, amqp_bytes_t received_data,
|
||||
amqp_frame_t *decoded_frame) {
|
||||
size_t bytes_consumed;
|
||||
void *raw_frame;
|
||||
|
||||
/* Returning frame_type of zero indicates either insufficient input,
|
||||
or a complete, ignored frame was read. */
|
||||
decoded_frame->frame_type = 0;
|
||||
|
||||
if (received_data.len == 0) {
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (state->state == CONNECTION_STATE_IDLE) {
|
||||
state->state = CONNECTION_STATE_HEADER;
|
||||
}
|
||||
|
||||
bytes_consumed = consume_data(state, &received_data);
|
||||
|
||||
/* do we have target_size data yet? if not, return with the
|
||||
expectation that more will arrive */
|
||||
if (state->inbound_offset < state->target_size) {
|
||||
return (int)bytes_consumed;
|
||||
}
|
||||
|
||||
raw_frame = state->inbound_buffer.bytes;
|
||||
|
||||
switch (state->state) {
|
||||
case CONNECTION_STATE_INITIAL:
|
||||
/* check for a protocol header from the server */
|
||||
if (memcmp(raw_frame, "AMQP", 4) == 0) {
|
||||
decoded_frame->frame_type = AMQP_PSEUDOFRAME_PROTOCOL_HEADER;
|
||||
decoded_frame->channel = 0;
|
||||
|
||||
decoded_frame->payload.protocol_header.transport_high =
|
||||
amqp_d8(amqp_offset(raw_frame, 4));
|
||||
decoded_frame->payload.protocol_header.transport_low =
|
||||
amqp_d8(amqp_offset(raw_frame, 5));
|
||||
decoded_frame->payload.protocol_header.protocol_version_major =
|
||||
amqp_d8(amqp_offset(raw_frame, 6));
|
||||
decoded_frame->payload.protocol_header.protocol_version_minor =
|
||||
amqp_d8(amqp_offset(raw_frame, 7));
|
||||
|
||||
return_to_idle(state);
|
||||
return (int)bytes_consumed;
|
||||
}
|
||||
|
||||
/* it's not a protocol header; fall through to process it as a
|
||||
regular frame header */
|
||||
|
||||
case CONNECTION_STATE_HEADER: {
|
||||
amqp_channel_t channel;
|
||||
amqp_pool_t *channel_pool;
|
||||
uint32_t frame_size;
|
||||
|
||||
channel = amqp_d16(amqp_offset(raw_frame, 1));
|
||||
|
||||
/* frame length is 3 bytes in */
|
||||
frame_size = amqp_d32(amqp_offset(raw_frame, 3));
|
||||
/* To prevent the target_size calculation below from overflowing, check
|
||||
* that the stated frame_size is smaller than a signed 32-bit. Given
|
||||
* the library only allows configuring frame_max as an int32_t, and
|
||||
* frame_size is uint32_t, the math below is safe from overflow. */
|
||||
if (frame_size >= INT32_MAX) {
|
||||
return AMQP_STATUS_BAD_AMQP_DATA;
|
||||
}
|
||||
|
||||
frame_size = frame_size + HEADER_SIZE + FOOTER_SIZE;
|
||||
if ((size_t)state->frame_max < frame_size) {
|
||||
return AMQP_STATUS_BAD_AMQP_DATA;
|
||||
}
|
||||
|
||||
channel_pool = amqp_get_or_create_channel_pool(state, channel);
|
||||
if (NULL == channel_pool) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
amqp_pool_alloc_bytes(channel_pool, frame_size, &state->inbound_buffer);
|
||||
if (NULL == state->inbound_buffer.bytes) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
memcpy(state->inbound_buffer.bytes, state->header_buffer, HEADER_SIZE);
|
||||
raw_frame = state->inbound_buffer.bytes;
|
||||
|
||||
state->state = CONNECTION_STATE_BODY;
|
||||
state->target_size = frame_size;
|
||||
bytes_consumed += consume_data(state, &received_data);
|
||||
|
||||
/* do we have target_size data yet? if not, return with the
|
||||
expectation that more will arrive */
|
||||
if (state->inbound_offset < state->target_size) {
|
||||
return (int)bytes_consumed;
|
||||
}
|
||||
}
|
||||
/* fall through to process body */
|
||||
|
||||
case CONNECTION_STATE_BODY: {
|
||||
amqp_bytes_t encoded;
|
||||
int res;
|
||||
amqp_pool_t *channel_pool;
|
||||
|
||||
/* Check frame end marker (footer) */
|
||||
if (amqp_d8(amqp_offset(raw_frame, state->target_size - 1)) !=
|
||||
AMQP_FRAME_END) {
|
||||
return AMQP_STATUS_BAD_AMQP_DATA;
|
||||
}
|
||||
|
||||
decoded_frame->frame_type = amqp_d8(amqp_offset(raw_frame, 0));
|
||||
decoded_frame->channel = amqp_d16(amqp_offset(raw_frame, 1));
|
||||
|
||||
channel_pool =
|
||||
amqp_get_or_create_channel_pool(state, decoded_frame->channel);
|
||||
if (NULL == channel_pool) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
switch (decoded_frame->frame_type) {
|
||||
case AMQP_FRAME_METHOD:
|
||||
decoded_frame->payload.method.id =
|
||||
amqp_d32(amqp_offset(raw_frame, HEADER_SIZE));
|
||||
encoded.bytes = amqp_offset(raw_frame, HEADER_SIZE + 4);
|
||||
encoded.len = state->target_size - HEADER_SIZE - 4 - FOOTER_SIZE;
|
||||
|
||||
res = amqp_decode_method(decoded_frame->payload.method.id,
|
||||
channel_pool, encoded,
|
||||
&decoded_frame->payload.method.decoded);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AMQP_FRAME_HEADER:
|
||||
decoded_frame->payload.properties.class_id =
|
||||
amqp_d16(amqp_offset(raw_frame, HEADER_SIZE));
|
||||
/* unused 2-byte weight field goes here */
|
||||
decoded_frame->payload.properties.body_size =
|
||||
amqp_d64(amqp_offset(raw_frame, HEADER_SIZE + 4));
|
||||
encoded.bytes = amqp_offset(raw_frame, HEADER_SIZE + 12);
|
||||
encoded.len = state->target_size - HEADER_SIZE - 12 - FOOTER_SIZE;
|
||||
decoded_frame->payload.properties.raw = encoded;
|
||||
|
||||
res = amqp_decode_properties(
|
||||
decoded_frame->payload.properties.class_id, channel_pool, encoded,
|
||||
&decoded_frame->payload.properties.decoded);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AMQP_FRAME_BODY:
|
||||
decoded_frame->payload.body_fragment.len =
|
||||
state->target_size - HEADER_SIZE - FOOTER_SIZE;
|
||||
decoded_frame->payload.body_fragment.bytes =
|
||||
amqp_offset(raw_frame, HEADER_SIZE);
|
||||
break;
|
||||
|
||||
case AMQP_FRAME_HEARTBEAT:
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Ignore the frame */
|
||||
decoded_frame->frame_type = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return_to_idle(state);
|
||||
return (int)bytes_consumed;
|
||||
}
|
||||
|
||||
default:
|
||||
amqp_abort("Internal error: invalid amqp_connection_state_t->state %d",
|
||||
state->state);
|
||||
}
|
||||
}
|
||||
|
||||
amqp_boolean_t amqp_release_buffers_ok(amqp_connection_state_t state) {
|
||||
return (state->state == CONNECTION_STATE_IDLE);
|
||||
}
|
||||
|
||||
void amqp_release_buffers(amqp_connection_state_t state) {
|
||||
int i;
|
||||
ENFORCE_STATE(state, CONNECTION_STATE_IDLE);
|
||||
|
||||
for (i = 0; i < POOL_TABLE_SIZE; ++i) {
|
||||
amqp_pool_table_entry_t *entry = state->pool_table[i];
|
||||
|
||||
for (; NULL != entry; entry = entry->next) {
|
||||
amqp_maybe_release_buffers_on_channel(state, entry->channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void amqp_maybe_release_buffers(amqp_connection_state_t state) {
|
||||
if (amqp_release_buffers_ok(state)) {
|
||||
amqp_release_buffers(state);
|
||||
}
|
||||
}
|
||||
|
||||
void amqp_maybe_release_buffers_on_channel(amqp_connection_state_t state,
|
||||
amqp_channel_t channel) {
|
||||
amqp_link_t *queued_link;
|
||||
amqp_pool_t *pool;
|
||||
if (CONNECTION_STATE_IDLE != state->state) {
|
||||
return;
|
||||
}
|
||||
|
||||
queued_link = state->first_queued_frame;
|
||||
|
||||
while (NULL != queued_link) {
|
||||
amqp_frame_t *frame = queued_link->data;
|
||||
if (channel == frame->channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
queued_link = queued_link->next;
|
||||
}
|
||||
|
||||
pool = amqp_get_channel_pool(state, channel);
|
||||
|
||||
if (pool != NULL) {
|
||||
recycle_amqp_pool(pool);
|
||||
}
|
||||
}
|
||||
|
||||
static int amqp_frame_to_bytes(const amqp_frame_t *frame, amqp_bytes_t buffer,
|
||||
amqp_bytes_t *encoded) {
|
||||
void *out_frame = buffer.bytes;
|
||||
size_t out_frame_len;
|
||||
int res;
|
||||
|
||||
amqp_e8(frame->frame_type, amqp_offset(out_frame, 0));
|
||||
amqp_e16(frame->channel, amqp_offset(out_frame, 1));
|
||||
|
||||
switch (frame->frame_type) {
|
||||
case AMQP_FRAME_BODY: {
|
||||
const amqp_bytes_t *body = &frame->payload.body_fragment;
|
||||
|
||||
memcpy(amqp_offset(out_frame, HEADER_SIZE), body->bytes, body->len);
|
||||
|
||||
out_frame_len = body->len;
|
||||
break;
|
||||
}
|
||||
case AMQP_FRAME_METHOD: {
|
||||
amqp_bytes_t method_encoded;
|
||||
|
||||
amqp_e32(frame->payload.method.id, amqp_offset(out_frame, HEADER_SIZE));
|
||||
|
||||
method_encoded.bytes = amqp_offset(out_frame, HEADER_SIZE + 4);
|
||||
method_encoded.len = buffer.len - HEADER_SIZE - 4 - FOOTER_SIZE;
|
||||
|
||||
res = amqp_encode_method(frame->payload.method.id,
|
||||
frame->payload.method.decoded, method_encoded);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
out_frame_len = res + 4;
|
||||
break;
|
||||
}
|
||||
|
||||
case AMQP_FRAME_HEADER: {
|
||||
amqp_bytes_t properties_encoded;
|
||||
|
||||
amqp_e16(frame->payload.properties.class_id,
|
||||
amqp_offset(out_frame, HEADER_SIZE));
|
||||
amqp_e16(0, amqp_offset(out_frame, HEADER_SIZE + 2)); /* "weight" */
|
||||
amqp_e64(frame->payload.properties.body_size,
|
||||
amqp_offset(out_frame, HEADER_SIZE + 4));
|
||||
|
||||
properties_encoded.bytes = amqp_offset(out_frame, HEADER_SIZE + 12);
|
||||
properties_encoded.len = buffer.len - HEADER_SIZE - 12 - FOOTER_SIZE;
|
||||
|
||||
res = amqp_encode_properties(frame->payload.properties.class_id,
|
||||
frame->payload.properties.decoded,
|
||||
properties_encoded);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
out_frame_len = res + 12;
|
||||
break;
|
||||
}
|
||||
|
||||
case AMQP_FRAME_HEARTBEAT:
|
||||
out_frame_len = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
amqp_e32((uint32_t)out_frame_len, amqp_offset(out_frame, 3));
|
||||
amqp_e8(AMQP_FRAME_END, amqp_offset(out_frame, HEADER_SIZE + out_frame_len));
|
||||
|
||||
encoded->bytes = out_frame;
|
||||
encoded->len = out_frame_len + HEADER_SIZE + FOOTER_SIZE;
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
int amqp_send_frame(amqp_connection_state_t state, const amqp_frame_t *frame) {
|
||||
return amqp_send_frame_inner(state, frame, AMQP_SF_NONE,
|
||||
amqp_time_infinite());
|
||||
}
|
||||
|
||||
int amqp_send_frame_inner(amqp_connection_state_t state,
|
||||
const amqp_frame_t *frame, int flags,
|
||||
amqp_time_t deadline) {
|
||||
int res;
|
||||
ssize_t sent;
|
||||
amqp_bytes_t encoded;
|
||||
amqp_time_t next_timeout;
|
||||
|
||||
/* TODO: if the AMQP_SF_MORE socket optimization can be shown to work
|
||||
* correctly, then this could be un-done so that body-frames are sent as 3
|
||||
* send calls, getting rid of the copy of the body content, some testing
|
||||
* would need to be done to see if this would actually a win for performance.
|
||||
* */
|
||||
res = amqp_frame_to_bytes(frame, state->outbound_buffer, &encoded);
|
||||
if (AMQP_STATUS_OK != res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
start_send:
|
||||
|
||||
next_timeout = amqp_time_first(deadline, state->next_recv_heartbeat);
|
||||
|
||||
sent = amqp_try_send(state, encoded.bytes, encoded.len, next_timeout, flags);
|
||||
if (0 > sent) {
|
||||
return (int)sent;
|
||||
}
|
||||
|
||||
/* A partial send has occurred, because of a heartbeat timeout (so try recv
|
||||
* something) or common timeout (so return AMQP_STATUS_TIMEOUT) */
|
||||
if ((ssize_t)encoded.len != sent) {
|
||||
if (amqp_time_equal(next_timeout, deadline)) {
|
||||
/* timeout of method was received, so return from method*/
|
||||
return AMQP_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
res = amqp_try_recv(state);
|
||||
|
||||
if (AMQP_STATUS_TIMEOUT == res) {
|
||||
return AMQP_STATUS_HEARTBEAT_TIMEOUT;
|
||||
} else if (AMQP_STATUS_OK != res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
encoded.bytes = (uint8_t *)encoded.bytes + sent;
|
||||
encoded.len -= sent;
|
||||
goto start_send;
|
||||
}
|
||||
|
||||
res = amqp_time_s_from_now(&state->next_send_heartbeat,
|
||||
amqp_heartbeat_send(state));
|
||||
return res;
|
||||
}
|
||||
|
||||
amqp_table_t *amqp_get_server_properties(amqp_connection_state_t state) {
|
||||
return &state->server_properties;
|
||||
}
|
||||
|
||||
amqp_table_t *amqp_get_client_properties(amqp_connection_state_t state) {
|
||||
return &state->client_properties;
|
||||
}
|
||||
282
librabbitmq/amqp_consumer.c
Normal file
282
librabbitmq/amqp_consumer.c
Normal file
@@ -0,0 +1,282 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include "amqp_socket.h"
|
||||
#include "rabbitmq-c/amqp.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int amqp_basic_properties_clone(amqp_basic_properties_t *original,
|
||||
amqp_basic_properties_t *clone,
|
||||
amqp_pool_t *pool) {
|
||||
memset(clone, 0, sizeof(*clone));
|
||||
clone->_flags = original->_flags;
|
||||
|
||||
#define CLONE_BYTES_POOL(original, clone, pool) \
|
||||
if (0 == original.len) { \
|
||||
clone = amqp_empty_bytes; \
|
||||
} else { \
|
||||
amqp_pool_alloc_bytes(pool, original.len, &clone); \
|
||||
if (NULL == clone.bytes) { \
|
||||
return AMQP_STATUS_NO_MEMORY; \
|
||||
} \
|
||||
memcpy(clone.bytes, original.bytes, clone.len); \
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
|
||||
CLONE_BYTES_POOL(original->content_type, clone->content_type, pool)
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) {
|
||||
CLONE_BYTES_POOL(original->content_encoding, clone->content_encoding, pool)
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_HEADERS_FLAG) {
|
||||
int res = amqp_table_clone(&original->headers, &clone->headers, pool);
|
||||
if (AMQP_STATUS_OK != res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_DELIVERY_MODE_FLAG) {
|
||||
clone->delivery_mode = original->delivery_mode;
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_PRIORITY_FLAG) {
|
||||
clone->priority = original->priority;
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_CORRELATION_ID_FLAG) {
|
||||
CLONE_BYTES_POOL(original->correlation_id, clone->correlation_id, pool)
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_REPLY_TO_FLAG) {
|
||||
CLONE_BYTES_POOL(original->reply_to, clone->reply_to, pool)
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_EXPIRATION_FLAG) {
|
||||
CLONE_BYTES_POOL(original->expiration, clone->expiration, pool)
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_MESSAGE_ID_FLAG) {
|
||||
CLONE_BYTES_POOL(original->message_id, clone->message_id, pool)
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_TIMESTAMP_FLAG) {
|
||||
clone->timestamp = original->timestamp;
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_TYPE_FLAG) {
|
||||
CLONE_BYTES_POOL(original->type, clone->type, pool)
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_USER_ID_FLAG) {
|
||||
CLONE_BYTES_POOL(original->user_id, clone->user_id, pool)
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_APP_ID_FLAG) {
|
||||
CLONE_BYTES_POOL(original->app_id, clone->app_id, pool)
|
||||
}
|
||||
|
||||
if (clone->_flags & AMQP_BASIC_CLUSTER_ID_FLAG) {
|
||||
CLONE_BYTES_POOL(original->cluster_id, clone->cluster_id, pool)
|
||||
}
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
#undef CLONE_BYTES_POOL
|
||||
}
|
||||
|
||||
void amqp_destroy_message(amqp_message_t *message) {
|
||||
empty_amqp_pool(&message->pool);
|
||||
amqp_bytes_free(message->body);
|
||||
}
|
||||
|
||||
void amqp_destroy_envelope(amqp_envelope_t *envelope) {
|
||||
amqp_destroy_message(&envelope->message);
|
||||
amqp_bytes_free(envelope->routing_key);
|
||||
amqp_bytes_free(envelope->exchange);
|
||||
amqp_bytes_free(envelope->consumer_tag);
|
||||
}
|
||||
|
||||
static int amqp_bytes_malloc_dup_failed(amqp_bytes_t bytes) {
|
||||
if (bytes.len != 0 && bytes.bytes == NULL) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
amqp_rpc_reply_t amqp_consume_message(amqp_connection_state_t state,
|
||||
amqp_envelope_t *envelope,
|
||||
const struct timeval *timeout,
|
||||
AMQP_UNUSED int flags) {
|
||||
int res;
|
||||
amqp_frame_t frame;
|
||||
amqp_basic_deliver_t *delivery_method;
|
||||
amqp_rpc_reply_t ret;
|
||||
|
||||
memset(&ret, 0, sizeof(ret));
|
||||
memset(envelope, 0, sizeof(*envelope));
|
||||
|
||||
res = amqp_simple_wait_frame_noblock(state, &frame, timeout);
|
||||
if (AMQP_STATUS_OK != res) {
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = res;
|
||||
goto error_out1;
|
||||
}
|
||||
|
||||
if (AMQP_FRAME_METHOD != frame.frame_type ||
|
||||
AMQP_BASIC_DELIVER_METHOD != frame.payload.method.id) {
|
||||
amqp_put_back_frame(state, &frame);
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = AMQP_STATUS_UNEXPECTED_STATE;
|
||||
goto error_out1;
|
||||
}
|
||||
|
||||
delivery_method = frame.payload.method.decoded;
|
||||
|
||||
envelope->channel = frame.channel;
|
||||
envelope->consumer_tag = amqp_bytes_malloc_dup(delivery_method->consumer_tag);
|
||||
envelope->delivery_tag = delivery_method->delivery_tag;
|
||||
envelope->redelivered = delivery_method->redelivered;
|
||||
envelope->exchange = amqp_bytes_malloc_dup(delivery_method->exchange);
|
||||
envelope->routing_key = amqp_bytes_malloc_dup(delivery_method->routing_key);
|
||||
|
||||
if (amqp_bytes_malloc_dup_failed(envelope->consumer_tag) ||
|
||||
amqp_bytes_malloc_dup_failed(envelope->exchange) ||
|
||||
amqp_bytes_malloc_dup_failed(envelope->routing_key)) {
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = AMQP_STATUS_NO_MEMORY;
|
||||
goto error_out2;
|
||||
}
|
||||
|
||||
ret = amqp_read_message(state, envelope->channel, &envelope->message, 0);
|
||||
if (AMQP_RESPONSE_NORMAL != ret.reply_type) {
|
||||
goto error_out2;
|
||||
}
|
||||
|
||||
ret.reply_type = AMQP_RESPONSE_NORMAL;
|
||||
return ret;
|
||||
|
||||
error_out2:
|
||||
amqp_bytes_free(envelope->routing_key);
|
||||
amqp_bytes_free(envelope->exchange);
|
||||
amqp_bytes_free(envelope->consumer_tag);
|
||||
error_out1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
amqp_rpc_reply_t amqp_read_message(amqp_connection_state_t state,
|
||||
amqp_channel_t channel,
|
||||
amqp_message_t *message,
|
||||
AMQP_UNUSED int flags) {
|
||||
amqp_frame_t frame;
|
||||
amqp_rpc_reply_t ret;
|
||||
|
||||
size_t body_read;
|
||||
char *body_read_ptr;
|
||||
int res;
|
||||
|
||||
memset(&ret, 0, sizeof(ret));
|
||||
memset(message, 0, sizeof(*message));
|
||||
|
||||
res = amqp_simple_wait_frame_on_channel(state, channel, &frame);
|
||||
if (AMQP_STATUS_OK != res) {
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = res;
|
||||
|
||||
goto error_out1;
|
||||
}
|
||||
|
||||
if (AMQP_FRAME_HEADER != frame.frame_type) {
|
||||
if (AMQP_FRAME_METHOD == frame.frame_type &&
|
||||
(AMQP_CHANNEL_CLOSE_METHOD == frame.payload.method.id ||
|
||||
AMQP_CONNECTION_CLOSE_METHOD == frame.payload.method.id)) {
|
||||
|
||||
ret.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION;
|
||||
ret.reply = frame.payload.method;
|
||||
|
||||
} else {
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = AMQP_STATUS_UNEXPECTED_STATE;
|
||||
|
||||
amqp_put_back_frame(state, &frame);
|
||||
}
|
||||
goto error_out1;
|
||||
}
|
||||
|
||||
init_amqp_pool(&message->pool, 4096);
|
||||
res = amqp_basic_properties_clone(frame.payload.properties.decoded,
|
||||
&message->properties, &message->pool);
|
||||
|
||||
if (AMQP_STATUS_OK != res) {
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = res;
|
||||
goto error_out3;
|
||||
}
|
||||
|
||||
if (0 == frame.payload.properties.body_size) {
|
||||
message->body = amqp_empty_bytes;
|
||||
} else {
|
||||
if (SIZE_MAX < frame.payload.properties.body_size) {
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = AMQP_STATUS_NO_MEMORY;
|
||||
goto error_out1;
|
||||
}
|
||||
message->body =
|
||||
amqp_bytes_malloc((size_t)frame.payload.properties.body_size);
|
||||
if (NULL == message->body.bytes) {
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = AMQP_STATUS_NO_MEMORY;
|
||||
goto error_out1;
|
||||
}
|
||||
}
|
||||
|
||||
body_read = 0;
|
||||
body_read_ptr = message->body.bytes;
|
||||
|
||||
while (body_read < message->body.len) {
|
||||
res = amqp_simple_wait_frame_on_channel(state, channel, &frame);
|
||||
if (AMQP_STATUS_OK != res) {
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = res;
|
||||
goto error_out2;
|
||||
}
|
||||
if (AMQP_FRAME_BODY != frame.frame_type) {
|
||||
if (AMQP_FRAME_METHOD == frame.frame_type &&
|
||||
(AMQP_CHANNEL_CLOSE_METHOD == frame.payload.method.id ||
|
||||
AMQP_CONNECTION_CLOSE_METHOD == frame.payload.method.id)) {
|
||||
|
||||
ret.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION;
|
||||
ret.reply = frame.payload.method;
|
||||
} else {
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = AMQP_STATUS_BAD_AMQP_DATA;
|
||||
}
|
||||
goto error_out2;
|
||||
}
|
||||
|
||||
if (body_read + frame.payload.body_fragment.len > message->body.len) {
|
||||
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
ret.library_error = AMQP_STATUS_BAD_AMQP_DATA;
|
||||
goto error_out2;
|
||||
}
|
||||
|
||||
memcpy(body_read_ptr, frame.payload.body_fragment.bytes,
|
||||
frame.payload.body_fragment.len);
|
||||
|
||||
body_read += frame.payload.body_fragment.len;
|
||||
body_read_ptr += frame.payload.body_fragment.len;
|
||||
}
|
||||
|
||||
ret.reply_type = AMQP_RESPONSE_NORMAL;
|
||||
return ret;
|
||||
|
||||
error_out2:
|
||||
amqp_bytes_free(message->body);
|
||||
error_out3:
|
||||
empty_amqp_pool(&message->pool);
|
||||
error_out1:
|
||||
return ret;
|
||||
}
|
||||
2919
librabbitmq/amqp_framing.c
Normal file
2919
librabbitmq/amqp_framing.c
Normal file
File diff suppressed because it is too large
Load Diff
210
librabbitmq/amqp_mem.c
Normal file
210
librabbitmq/amqp_mem.c
Normal file
@@ -0,0 +1,210 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
char const *amqp_version(void) { return AMQP_VERSION_STRING; }
|
||||
|
||||
uint32_t amqp_version_number(void) { return AMQP_VERSION; }
|
||||
|
||||
void init_amqp_pool(amqp_pool_t *pool, size_t pagesize) {
|
||||
pool->pagesize = pagesize ? pagesize : 4096;
|
||||
|
||||
pool->pages.num_blocks = 0;
|
||||
pool->pages.blocklist = NULL;
|
||||
|
||||
pool->large_blocks.num_blocks = 0;
|
||||
pool->large_blocks.blocklist = NULL;
|
||||
|
||||
pool->next_page = 0;
|
||||
pool->alloc_block = NULL;
|
||||
pool->alloc_used = 0;
|
||||
}
|
||||
|
||||
static void empty_blocklist(amqp_pool_blocklist_t *x) {
|
||||
int i;
|
||||
|
||||
if (x->blocklist != NULL) {
|
||||
for (i = 0; i < x->num_blocks; i++) {
|
||||
free(x->blocklist[i]);
|
||||
}
|
||||
free(x->blocklist);
|
||||
}
|
||||
x->num_blocks = 0;
|
||||
x->blocklist = NULL;
|
||||
}
|
||||
|
||||
void recycle_amqp_pool(amqp_pool_t *pool) {
|
||||
empty_blocklist(&pool->large_blocks);
|
||||
pool->next_page = 0;
|
||||
pool->alloc_block = NULL;
|
||||
pool->alloc_used = 0;
|
||||
}
|
||||
|
||||
void empty_amqp_pool(amqp_pool_t *pool) {
|
||||
recycle_amqp_pool(pool);
|
||||
empty_blocklist(&pool->pages);
|
||||
}
|
||||
|
||||
/* Returns 1 on success, 0 on failure */
|
||||
static int record_pool_block(amqp_pool_blocklist_t *x, void *block) {
|
||||
size_t blocklistlength = sizeof(void *) * (x->num_blocks + 1);
|
||||
|
||||
if (x->blocklist == NULL) {
|
||||
x->blocklist = malloc(blocklistlength);
|
||||
if (x->blocklist == NULL) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
void *newbl = realloc(x->blocklist, blocklistlength);
|
||||
if (newbl == NULL) {
|
||||
return 0;
|
||||
}
|
||||
x->blocklist = newbl;
|
||||
}
|
||||
|
||||
x->blocklist[x->num_blocks] = block;
|
||||
x->num_blocks++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *amqp_pool_alloc(amqp_pool_t *pool, size_t amount) {
|
||||
if (amount == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
amount = (amount + 7) & (~7); /* round up to nearest 8-byte boundary */
|
||||
|
||||
if (amount > pool->pagesize) {
|
||||
void *result = calloc(1, amount);
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (!record_pool_block(&pool->large_blocks, result)) {
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (pool->alloc_block != NULL) {
|
||||
assert(pool->alloc_used <= pool->pagesize);
|
||||
|
||||
if (pool->alloc_used + amount <= pool->pagesize) {
|
||||
void *result = pool->alloc_block + pool->alloc_used;
|
||||
pool->alloc_used += amount;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (pool->next_page >= pool->pages.num_blocks) {
|
||||
pool->alloc_block = calloc(1, pool->pagesize);
|
||||
if (pool->alloc_block == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (!record_pool_block(&pool->pages, pool->alloc_block)) {
|
||||
return NULL;
|
||||
}
|
||||
pool->next_page = pool->pages.num_blocks;
|
||||
} else {
|
||||
pool->alloc_block = pool->pages.blocklist[pool->next_page];
|
||||
pool->next_page++;
|
||||
}
|
||||
|
||||
pool->alloc_used = amount;
|
||||
|
||||
return pool->alloc_block;
|
||||
}
|
||||
|
||||
void amqp_pool_alloc_bytes(amqp_pool_t *pool, size_t amount,
|
||||
amqp_bytes_t *output) {
|
||||
output->len = amount;
|
||||
output->bytes = amqp_pool_alloc(pool, amount);
|
||||
}
|
||||
|
||||
amqp_bytes_t amqp_cstring_bytes(char const *cstr) {
|
||||
amqp_bytes_t result;
|
||||
result.len = strlen(cstr);
|
||||
result.bytes = (void *)cstr;
|
||||
return result;
|
||||
}
|
||||
|
||||
amqp_bytes_t amqp_bytes_malloc_dup(amqp_bytes_t src) {
|
||||
amqp_bytes_t result;
|
||||
result.len = src.len;
|
||||
result.bytes = malloc(src.len);
|
||||
if (result.bytes != NULL) {
|
||||
memcpy(result.bytes, src.bytes, src.len);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
amqp_bytes_t amqp_bytes_malloc(size_t amount) {
|
||||
amqp_bytes_t result;
|
||||
result.len = amount;
|
||||
result.bytes = malloc(amount); /* will return NULL if it fails */
|
||||
return result;
|
||||
}
|
||||
|
||||
void amqp_bytes_free(amqp_bytes_t bytes) { free(bytes.bytes); }
|
||||
|
||||
amqp_pool_t *amqp_get_or_create_channel_pool(amqp_connection_state_t state,
|
||||
amqp_channel_t channel) {
|
||||
amqp_pool_table_entry_t *entry;
|
||||
size_t index = channel % POOL_TABLE_SIZE;
|
||||
|
||||
entry = state->pool_table[index];
|
||||
|
||||
for (; NULL != entry; entry = entry->next) {
|
||||
if (channel == entry->channel) {
|
||||
return &entry->pool;
|
||||
}
|
||||
}
|
||||
|
||||
entry = malloc(sizeof(amqp_pool_table_entry_t));
|
||||
if (NULL == entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry->channel = channel;
|
||||
entry->next = state->pool_table[index];
|
||||
state->pool_table[index] = entry;
|
||||
|
||||
init_amqp_pool(&entry->pool, state->frame_max);
|
||||
|
||||
return &entry->pool;
|
||||
}
|
||||
|
||||
amqp_pool_t *amqp_get_channel_pool(amqp_connection_state_t state,
|
||||
amqp_channel_t channel) {
|
||||
amqp_pool_table_entry_t *entry;
|
||||
size_t index = channel % POOL_TABLE_SIZE;
|
||||
|
||||
entry = state->pool_table[index];
|
||||
|
||||
for (; NULL != entry; entry = entry->next) {
|
||||
if (channel == entry->channel) {
|
||||
return &entry->pool;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int amqp_bytes_equal(amqp_bytes_t r, amqp_bytes_t l) {
|
||||
if (r.len == l.len &&
|
||||
(r.bytes == l.bytes || 0 == memcmp(r.bytes, l.bytes, r.len))) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
669
librabbitmq/amqp_openssl.c
Normal file
669
librabbitmq/amqp_openssl.c
Normal file
@@ -0,0 +1,669 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
// Use OpenSSL v1.1.1 API.
|
||||
#define OPENSSL_API_COMPAT 10101
|
||||
|
||||
#include "amqp_openssl_bio.h"
|
||||
#include "amqp_private.h"
|
||||
#include "amqp_socket.h"
|
||||
#include "amqp_time.h"
|
||||
#include "rabbitmq-c/ssl_socket.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/conf.h>
|
||||
#ifdef ENABLE_SSL_ENGINE_API
|
||||
#include <openssl/engine.h>
|
||||
#endif
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int initialize_ssl_and_increment_connections(void);
|
||||
static int decrement_ssl_connections(void);
|
||||
|
||||
static pthread_mutex_t openssl_init_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static amqp_boolean_t openssl_bio_initialized = 0;
|
||||
static int openssl_connections = 0;
|
||||
#ifdef ENABLE_SSL_ENGINE_API
|
||||
static ENGINE *openssl_engine = NULL;
|
||||
#endif
|
||||
|
||||
#define CHECK_SUCCESS(condition) \
|
||||
do { \
|
||||
int check_success_ret = (condition); \
|
||||
if (check_success_ret) { \
|
||||
amqp_abort("Check %s failed <%d>: %s", #condition, check_success_ret, \
|
||||
strerror(check_success_ret)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct amqp_ssl_socket_t {
|
||||
const struct amqp_socket_class_t *klass;
|
||||
SSL_CTX *ctx;
|
||||
int sockfd;
|
||||
SSL *ssl;
|
||||
amqp_boolean_t verify_peer;
|
||||
amqp_boolean_t verify_hostname;
|
||||
int internal_error;
|
||||
};
|
||||
|
||||
static ssize_t amqp_ssl_socket_send(void *base, const void *buf, size_t len,
|
||||
AMQP_UNUSED int flags) {
|
||||
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
|
||||
int res;
|
||||
if (-1 == self->sockfd) {
|
||||
return AMQP_STATUS_SOCKET_CLOSED;
|
||||
}
|
||||
|
||||
/* SSL_write takes an int for length of buffer, protect against len being
|
||||
* larger than larger than what SSL_write can take */
|
||||
if (len > INT_MAX) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
ERR_clear_error();
|
||||
self->internal_error = 0;
|
||||
|
||||
/* This will only return on error, or once the whole buffer has been
|
||||
* written to the SSL stream. See SSL_MODE_ENABLE_PARTIAL_WRITE */
|
||||
res = SSL_write(self->ssl, buf, (int)len);
|
||||
if (0 >= res) {
|
||||
self->internal_error = SSL_get_error(self->ssl, res);
|
||||
/* TODO: Close connection if it isn't already? */
|
||||
/* TODO: Possibly be more intelligent in reporting WHAT went wrong */
|
||||
switch (self->internal_error) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
res = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
res = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
res = AMQP_STATUS_CONNECTION_CLOSED;
|
||||
break;
|
||||
default:
|
||||
res = AMQP_STATUS_SSL_ERROR;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
self->internal_error = 0;
|
||||
}
|
||||
|
||||
return (ssize_t)res;
|
||||
}
|
||||
|
||||
static ssize_t amqp_ssl_socket_recv(void *base, void *buf, size_t len,
|
||||
AMQP_UNUSED int flags) {
|
||||
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
|
||||
int received;
|
||||
if (-1 == self->sockfd) {
|
||||
return AMQP_STATUS_SOCKET_CLOSED;
|
||||
}
|
||||
|
||||
/* SSL_read takes an int for length of buffer, protect against len being
|
||||
* larger than larger than what SSL_read can take */
|
||||
if (len > INT_MAX) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
ERR_clear_error();
|
||||
self->internal_error = 0;
|
||||
|
||||
received = SSL_read(self->ssl, buf, (int)len);
|
||||
if (0 >= received) {
|
||||
self->internal_error = SSL_get_error(self->ssl, received);
|
||||
switch (self->internal_error) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
received = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
received = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
received = AMQP_STATUS_CONNECTION_CLOSED;
|
||||
break;
|
||||
default:
|
||||
received = AMQP_STATUS_SSL_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (ssize_t)received;
|
||||
}
|
||||
|
||||
static int amqp_ssl_socket_open(void *base, const char *host, int port,
|
||||
const struct timeval *timeout) {
|
||||
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
|
||||
long result;
|
||||
int status;
|
||||
amqp_time_t deadline;
|
||||
X509 *cert;
|
||||
BIO *bio;
|
||||
if (-1 != self->sockfd) {
|
||||
return AMQP_STATUS_SOCKET_INUSE;
|
||||
}
|
||||
ERR_clear_error();
|
||||
|
||||
self->ssl = SSL_new(self->ctx);
|
||||
if (!self->ssl) {
|
||||
self->internal_error = ERR_peek_error();
|
||||
status = AMQP_STATUS_SSL_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
status = amqp_time_from_now(&deadline, timeout);
|
||||
if (AMQP_STATUS_OK != status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
self->sockfd = amqp_open_socket_inner(host, port, deadline);
|
||||
if (0 > self->sockfd) {
|
||||
status = self->sockfd;
|
||||
self->internal_error = amqp_os_socket_error();
|
||||
self->sockfd = -1;
|
||||
goto error_out1;
|
||||
}
|
||||
|
||||
bio = BIO_new(amqp_openssl_bio());
|
||||
if (!bio) {
|
||||
status = AMQP_STATUS_NO_MEMORY;
|
||||
goto error_out2;
|
||||
}
|
||||
|
||||
BIO_set_fd(bio, self->sockfd, BIO_NOCLOSE);
|
||||
SSL_set_bio(self->ssl, bio, bio);
|
||||
|
||||
status = SSL_set_tlsext_host_name(self->ssl, host);
|
||||
if (!status) {
|
||||
self->internal_error = SSL_get_error(self->ssl, status);
|
||||
status = AMQP_STATUS_SSL_ERROR;
|
||||
goto error_out2;
|
||||
}
|
||||
|
||||
start_connect:
|
||||
status = SSL_connect(self->ssl);
|
||||
if (status != 1) {
|
||||
self->internal_error = SSL_get_error(self->ssl, status);
|
||||
switch (self->internal_error) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
status = amqp_poll(self->sockfd, AMQP_SF_POLLIN, deadline);
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
status = amqp_poll(self->sockfd, AMQP_SF_POLLOUT, deadline);
|
||||
break;
|
||||
default:
|
||||
status = AMQP_STATUS_SSL_CONNECTION_FAILED;
|
||||
}
|
||||
if (AMQP_STATUS_OK == status) {
|
||||
goto start_connect;
|
||||
}
|
||||
goto error_out2;
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
cert = SSL_get_peer_certificate(self->ssl);
|
||||
#else
|
||||
cert = SSL_get1_peer_certificate(self->ssl);
|
||||
#endif
|
||||
|
||||
if (self->verify_peer) {
|
||||
if (!cert) {
|
||||
self->internal_error = 0;
|
||||
status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED;
|
||||
goto error_out3;
|
||||
}
|
||||
|
||||
result = SSL_get_verify_result(self->ssl);
|
||||
if (X509_V_OK != result) {
|
||||
self->internal_error = result;
|
||||
status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED;
|
||||
goto error_out4;
|
||||
}
|
||||
}
|
||||
if (self->verify_hostname) {
|
||||
if (!cert) {
|
||||
self->internal_error = 0;
|
||||
status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED;
|
||||
goto error_out3;
|
||||
}
|
||||
|
||||
if (1 != X509_check_host(cert, host, strlen(host),
|
||||
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS, NULL)) {
|
||||
self->internal_error = 0;
|
||||
status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED;
|
||||
goto error_out4;
|
||||
}
|
||||
}
|
||||
|
||||
X509_free(cert);
|
||||
self->internal_error = 0;
|
||||
status = AMQP_STATUS_OK;
|
||||
|
||||
exit:
|
||||
return status;
|
||||
|
||||
error_out4:
|
||||
X509_free(cert);
|
||||
error_out3:
|
||||
SSL_shutdown(self->ssl);
|
||||
error_out2:
|
||||
amqp_os_socket_close(self->sockfd);
|
||||
self->sockfd = -1;
|
||||
error_out1:
|
||||
SSL_free(self->ssl);
|
||||
self->ssl = NULL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
static int amqp_ssl_socket_close(void *base, amqp_socket_close_enum force) {
|
||||
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
|
||||
|
||||
if (-1 == self->sockfd) {
|
||||
return AMQP_STATUS_SOCKET_CLOSED;
|
||||
}
|
||||
|
||||
if (AMQP_SC_NONE == force) {
|
||||
/* don't try too hard to shutdown the connection */
|
||||
SSL_shutdown(self->ssl);
|
||||
}
|
||||
|
||||
SSL_free(self->ssl);
|
||||
self->ssl = NULL;
|
||||
|
||||
if (amqp_os_socket_close(self->sockfd)) {
|
||||
return AMQP_STATUS_SOCKET_ERROR;
|
||||
}
|
||||
self->sockfd = -1;
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
static int amqp_ssl_socket_get_sockfd(void *base) {
|
||||
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
|
||||
return self->sockfd;
|
||||
}
|
||||
|
||||
static void amqp_ssl_socket_delete(void *base) {
|
||||
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
|
||||
|
||||
if (self) {
|
||||
amqp_ssl_socket_close(self, AMQP_SC_NONE);
|
||||
|
||||
SSL_CTX_free(self->ctx);
|
||||
free(self);
|
||||
}
|
||||
decrement_ssl_connections();
|
||||
}
|
||||
|
||||
static const struct amqp_socket_class_t amqp_ssl_socket_class = {
|
||||
amqp_ssl_socket_send, /* send */
|
||||
amqp_ssl_socket_recv, /* recv */
|
||||
amqp_ssl_socket_open, /* open */
|
||||
amqp_ssl_socket_close, /* close */
|
||||
amqp_ssl_socket_get_sockfd, /* get_sockfd */
|
||||
amqp_ssl_socket_delete /* delete */
|
||||
};
|
||||
|
||||
amqp_socket_t *amqp_ssl_socket_new(amqp_connection_state_t state) {
|
||||
struct amqp_ssl_socket_t *self = calloc(1, sizeof(*self));
|
||||
int status;
|
||||
if (!self) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->sockfd = -1;
|
||||
self->klass = &amqp_ssl_socket_class;
|
||||
self->verify_peer = 1;
|
||||
self->verify_hostname = 1;
|
||||
|
||||
status = initialize_ssl_and_increment_connections();
|
||||
if (status) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
self->ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (!self->ctx) {
|
||||
goto error;
|
||||
}
|
||||
status = amqp_ssl_socket_set_ssl_versions((amqp_socket_t *)self, AMQP_TLSv1_2,
|
||||
AMQP_TLSvLATEST);
|
||||
if (status != AMQP_STATUS_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
SSL_CTX_set_mode(self->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||
/* OpenSSL v1.1.1 turns this on by default, which makes the non-blocking
|
||||
* logic not behave as expected, so turn this back off */
|
||||
SSL_CTX_clear_mode(self->ctx, SSL_MODE_AUTO_RETRY);
|
||||
|
||||
amqp_set_socket(state, (amqp_socket_t *)self);
|
||||
|
||||
return (amqp_socket_t *)self;
|
||||
error:
|
||||
amqp_ssl_socket_delete((amqp_socket_t *)self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *amqp_ssl_socket_get_context(amqp_socket_t *base) {
|
||||
if (base->klass != &amqp_ssl_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
|
||||
}
|
||||
return ((struct amqp_ssl_socket_t *)base)->ctx;
|
||||
}
|
||||
|
||||
int amqp_ssl_socket_enable_default_verify_paths(amqp_socket_t *base) {
|
||||
int status;
|
||||
struct amqp_ssl_socket_t *self;
|
||||
if (base->klass != &amqp_ssl_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
|
||||
}
|
||||
self = (struct amqp_ssl_socket_t *)base;
|
||||
status = SSL_CTX_set_default_verify_paths(self->ctx);
|
||||
if (1 != status) {
|
||||
return AMQP_STATUS_SSL_ERROR;
|
||||
}
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
int amqp_ssl_socket_set_cacert(amqp_socket_t *base, const char *cacert) {
|
||||
int status;
|
||||
struct amqp_ssl_socket_t *self;
|
||||
if (base->klass != &amqp_ssl_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
|
||||
}
|
||||
self = (struct amqp_ssl_socket_t *)base;
|
||||
status = SSL_CTX_load_verify_locations(self->ctx, cacert, NULL);
|
||||
if (1 != status) {
|
||||
return AMQP_STATUS_SSL_ERROR;
|
||||
}
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
int amqp_ssl_socket_set_key(amqp_socket_t *base, const char *cert,
|
||||
const char *key) {
|
||||
int status;
|
||||
struct amqp_ssl_socket_t *self;
|
||||
if (base->klass != &amqp_ssl_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
|
||||
}
|
||||
self = (struct amqp_ssl_socket_t *)base;
|
||||
status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
|
||||
if (1 != status) {
|
||||
return AMQP_STATUS_SSL_ERROR;
|
||||
}
|
||||
status = SSL_CTX_use_PrivateKey_file(self->ctx, key, SSL_FILETYPE_PEM);
|
||||
if (1 != status) {
|
||||
return AMQP_STATUS_SSL_ERROR;
|
||||
}
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
int amqp_ssl_socket_set_key_engine(amqp_socket_t *base, const char *cert,
|
||||
const char *key) {
|
||||
#ifdef ENABLE_SSL_ENGINE_API
|
||||
int status;
|
||||
struct amqp_ssl_socket_t *self;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
if (base->klass != &amqp_ssl_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
|
||||
}
|
||||
self = (struct amqp_ssl_socket_t *)base;
|
||||
status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
|
||||
if (1 != status) {
|
||||
return AMQP_STATUS_SSL_ERROR;
|
||||
}
|
||||
|
||||
pkey = ENGINE_load_private_key(openssl_engine, key, NULL, NULL);
|
||||
if (pkey == NULL) {
|
||||
return AMQP_STATUS_SSL_ERROR;
|
||||
}
|
||||
|
||||
status = SSL_CTX_use_PrivateKey(self->ctx, pkey);
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
if (1 != status) {
|
||||
return AMQP_STATUS_SSL_ERROR;
|
||||
}
|
||||
return AMQP_STATUS_OK;
|
||||
#else
|
||||
return AMQP_STATUS_SSL_UNIMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int password_cb(AMQP_UNUSED char *buffer, AMQP_UNUSED int length,
|
||||
AMQP_UNUSED int rwflag, AMQP_UNUSED void *user_data) {
|
||||
amqp_abort("rabbitmq-c does not support password protected keys");
|
||||
}
|
||||
|
||||
int amqp_ssl_socket_set_key_buffer(amqp_socket_t *base, const char *cert,
|
||||
const void *key, size_t n) {
|
||||
int status = AMQP_STATUS_OK;
|
||||
BIO *buf = NULL;
|
||||
RSA *rsa = NULL;
|
||||
struct amqp_ssl_socket_t *self;
|
||||
if (base->klass != &amqp_ssl_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
|
||||
}
|
||||
if (n > INT_MAX) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
self = (struct amqp_ssl_socket_t *)base;
|
||||
status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
|
||||
if (1 != status) {
|
||||
return AMQP_STATUS_SSL_ERROR;
|
||||
}
|
||||
buf = BIO_new_mem_buf((void *)key, (int)n);
|
||||
if (!buf) {
|
||||
goto error;
|
||||
}
|
||||
rsa = PEM_read_bio_RSAPrivateKey(buf, NULL, password_cb, NULL);
|
||||
if (!rsa) {
|
||||
goto error;
|
||||
}
|
||||
status = SSL_CTX_use_RSAPrivateKey(self->ctx, rsa);
|
||||
if (1 != status) {
|
||||
goto error;
|
||||
}
|
||||
status = AMQP_STATUS_OK;
|
||||
exit:
|
||||
BIO_vfree(buf);
|
||||
RSA_free(rsa);
|
||||
return status;
|
||||
error:
|
||||
status = AMQP_STATUS_SSL_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
int amqp_ssl_socket_set_cert(amqp_socket_t *base, const char *cert) {
|
||||
int status;
|
||||
struct amqp_ssl_socket_t *self;
|
||||
if (base->klass != &amqp_ssl_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
|
||||
}
|
||||
self = (struct amqp_ssl_socket_t *)base;
|
||||
status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
|
||||
if (1 != status) {
|
||||
return AMQP_STATUS_SSL_ERROR;
|
||||
}
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
void amqp_ssl_socket_set_key_passwd(amqp_socket_t *base, const char *passwd) {
|
||||
struct amqp_ssl_socket_t *self;
|
||||
if (base->klass != &amqp_ssl_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
|
||||
}
|
||||
self = (struct amqp_ssl_socket_t *)base;
|
||||
SSL_CTX_set_default_passwd_cb_userdata(self->ctx, (void *)passwd);
|
||||
}
|
||||
|
||||
void amqp_ssl_socket_set_verify(amqp_socket_t *base, amqp_boolean_t verify) {
|
||||
amqp_ssl_socket_set_verify_peer(base, verify);
|
||||
amqp_ssl_socket_set_verify_hostname(base, verify);
|
||||
}
|
||||
|
||||
void amqp_ssl_socket_set_verify_peer(amqp_socket_t *base,
|
||||
amqp_boolean_t verify) {
|
||||
struct amqp_ssl_socket_t *self;
|
||||
if (base->klass != &amqp_ssl_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
|
||||
}
|
||||
self = (struct amqp_ssl_socket_t *)base;
|
||||
self->verify_peer = verify;
|
||||
}
|
||||
|
||||
void amqp_ssl_socket_set_verify_hostname(amqp_socket_t *base,
|
||||
amqp_boolean_t verify) {
|
||||
struct amqp_ssl_socket_t *self;
|
||||
if (base->klass != &amqp_ssl_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
|
||||
}
|
||||
self = (struct amqp_ssl_socket_t *)base;
|
||||
self->verify_hostname = verify;
|
||||
}
|
||||
|
||||
static int get_tls_version(amqp_tls_version_t ver, int *tls_version) {
|
||||
switch (ver) {
|
||||
case AMQP_TLSv1_2:
|
||||
*tls_version = TLS1_2_VERSION;
|
||||
break;
|
||||
case AMQP_TLSv1_3:
|
||||
case AMQP_TLSvLATEST:
|
||||
*tls_version = TLS1_3_VERSION;
|
||||
break;
|
||||
default:
|
||||
return AMQP_STATUS_UNSUPPORTED;
|
||||
}
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
int amqp_ssl_socket_set_ssl_versions(amqp_socket_t *base,
|
||||
amqp_tls_version_t min,
|
||||
amqp_tls_version_t max) {
|
||||
struct amqp_ssl_socket_t *self;
|
||||
int min_ver;
|
||||
int max_ver;
|
||||
int status;
|
||||
if (base->klass != &amqp_ssl_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
|
||||
}
|
||||
self = (struct amqp_ssl_socket_t *)base;
|
||||
|
||||
if (max < min) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
status = get_tls_version(min, &min_ver);
|
||||
if (status != AMQP_STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = get_tls_version(max, &max_ver);
|
||||
if (status != AMQP_STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!SSL_CTX_set_min_proto_version(self->ctx, min_ver)) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if (!SSL_CTX_set_max_proto_version(self->ctx, max_ver)) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
void amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize) {
|
||||
(void)do_initialize;
|
||||
return;
|
||||
}
|
||||
|
||||
int amqp_initialize_ssl_library(void) { return AMQP_STATUS_OK; }
|
||||
|
||||
int amqp_set_ssl_engine(const char *engine) {
|
||||
#ifdef ENABLE_SSL_ENGINE_API
|
||||
int status = AMQP_STATUS_OK;
|
||||
CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
|
||||
|
||||
if (openssl_engine != NULL) {
|
||||
ENGINE_free(openssl_engine);
|
||||
openssl_engine = NULL;
|
||||
}
|
||||
|
||||
if (engine == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ENGINE_load_builtin_engines();
|
||||
openssl_engine = ENGINE_by_id(engine);
|
||||
if (openssl_engine == NULL) {
|
||||
status = AMQP_STATUS_SSL_SET_ENGINE_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ENGINE_set_default(openssl_engine, ENGINE_METHOD_ALL) == 0) {
|
||||
ENGINE_free(openssl_engine);
|
||||
openssl_engine = NULL;
|
||||
status = AMQP_STATUS_SSL_SET_ENGINE_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
|
||||
return status;
|
||||
#else
|
||||
return AMQP_STATUS_SSL_UNIMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int initialize_ssl_and_increment_connections(void) {
|
||||
int status;
|
||||
CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
|
||||
|
||||
if (!openssl_bio_initialized) {
|
||||
status = amqp_openssl_bio_init();
|
||||
if (status) {
|
||||
goto exit;
|
||||
}
|
||||
openssl_bio_initialized = 1;
|
||||
}
|
||||
|
||||
openssl_connections += 1;
|
||||
status = AMQP_STATUS_OK;
|
||||
exit:
|
||||
CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
|
||||
return status;
|
||||
}
|
||||
|
||||
static int decrement_ssl_connections(void) {
|
||||
CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
|
||||
|
||||
if (openssl_connections > 0) {
|
||||
openssl_connections--;
|
||||
}
|
||||
|
||||
if (openssl_connections == 0) {
|
||||
amqp_openssl_bio_destroy();
|
||||
openssl_bio_initialized = 0;
|
||||
}
|
||||
|
||||
CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
int amqp_uninitialize_ssl_library(void) { return AMQP_STATUS_OK; }
|
||||
155
librabbitmq/amqp_openssl_bio.c
Normal file
155
librabbitmq/amqp_openssl_bio.c
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#include "amqp_openssl_bio.h"
|
||||
#include "amqp_socket.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef MSG_NOSIGNAL
|
||||
#define AMQP_USE_AMQP_BIO
|
||||
#endif
|
||||
|
||||
static int amqp_ssl_bio_initialized = 0;
|
||||
|
||||
#ifdef AMQP_USE_AMQP_BIO
|
||||
|
||||
static BIO_METHOD *amqp_bio_method;
|
||||
|
||||
static int amqp_openssl_bio_should_retry(int res) {
|
||||
if (res == -1) {
|
||||
int err = amqp_os_socket_error();
|
||||
if (
|
||||
#ifdef EWOULDBLOCK
|
||||
err == EWOULDBLOCK ||
|
||||
#endif
|
||||
#ifdef WSAEWOULDBLOCK
|
||||
err == WSAEWOULDBLOCK ||
|
||||
#endif
|
||||
#ifdef ENOTCONN
|
||||
err == ENOTCONN ||
|
||||
#endif
|
||||
#ifdef EINTR
|
||||
err == EINTR ||
|
||||
#endif
|
||||
#ifdef EAGAIN
|
||||
err == EAGAIN ||
|
||||
#endif
|
||||
#ifdef EPROTO
|
||||
err == EPROTO ||
|
||||
#endif
|
||||
#ifdef EINPROGRESS
|
||||
err == EINPROGRESS ||
|
||||
#endif
|
||||
#ifdef EALREADY
|
||||
err == EALREADY ||
|
||||
#endif
|
||||
0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amqp_openssl_bio_write(BIO *b, const char *in, int inl) {
|
||||
int flags = 0;
|
||||
int fd;
|
||||
int res;
|
||||
|
||||
#ifdef MSG_NOSIGNAL
|
||||
flags |= MSG_NOSIGNAL;
|
||||
#endif
|
||||
|
||||
BIO_get_fd(b, &fd);
|
||||
res = send(fd, in, inl, flags);
|
||||
|
||||
BIO_clear_retry_flags(b);
|
||||
if (res <= 0 && amqp_openssl_bio_should_retry(res)) {
|
||||
BIO_set_retry_write(b);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int amqp_openssl_bio_read(BIO *b, char *out, int outl) {
|
||||
int flags = 0;
|
||||
int fd;
|
||||
int res;
|
||||
|
||||
#ifdef MSG_NOSIGNAL
|
||||
flags |= MSG_NOSIGNAL;
|
||||
#endif
|
||||
|
||||
BIO_get_fd(b, &fd);
|
||||
res = recv(fd, out, outl, flags);
|
||||
|
||||
BIO_clear_retry_flags(b);
|
||||
if (res <= 0 && amqp_openssl_bio_should_retry(res)) {
|
||||
BIO_set_retry_read(b);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif /* AMQP_USE_AMQP_BIO */
|
||||
|
||||
int amqp_openssl_bio_init(void) {
|
||||
assert(!amqp_ssl_bio_initialized);
|
||||
#ifdef AMQP_USE_AMQP_BIO
|
||||
if (!(amqp_bio_method = BIO_meth_new(BIO_TYPE_SOCKET, "amqp_bio_method"))) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
BIO_meth_set_create(amqp_bio_method, BIO_s_socket()->create);
|
||||
BIO_meth_set_destroy(amqp_bio_method, BIO_s_socket()->destroy);
|
||||
BIO_meth_set_ctrl(amqp_bio_method, BIO_s_socket()->ctrl);
|
||||
BIO_meth_set_read(amqp_bio_method, BIO_s_socket()->bread);
|
||||
BIO_meth_set_write(amqp_bio_method, BIO_s_socket()->bwrite);
|
||||
BIO_meth_set_gets(amqp_bio_method, BIO_s_socket()->bgets);
|
||||
BIO_meth_set_puts(amqp_bio_method, BIO_s_socket()->bputs);
|
||||
#else
|
||||
BIO_meth_set_create(amqp_bio_method, BIO_meth_get_create(BIO_s_socket()));
|
||||
BIO_meth_set_destroy(amqp_bio_method, BIO_meth_get_destroy(BIO_s_socket()));
|
||||
BIO_meth_set_ctrl(amqp_bio_method, BIO_meth_get_ctrl(BIO_s_socket()));
|
||||
BIO_meth_set_callback_ctrl(amqp_bio_method,
|
||||
BIO_meth_get_callback_ctrl(BIO_s_socket()));
|
||||
BIO_meth_set_read(amqp_bio_method, BIO_meth_get_read(BIO_s_socket()));
|
||||
BIO_meth_set_write(amqp_bio_method, BIO_meth_get_write(BIO_s_socket()));
|
||||
BIO_meth_set_gets(amqp_bio_method, BIO_meth_get_gets(BIO_s_socket()));
|
||||
BIO_meth_set_puts(amqp_bio_method, BIO_meth_get_puts(BIO_s_socket()));
|
||||
#endif
|
||||
|
||||
BIO_meth_set_write(amqp_bio_method, amqp_openssl_bio_write);
|
||||
BIO_meth_set_read(amqp_bio_method, amqp_openssl_bio_read);
|
||||
#endif
|
||||
|
||||
amqp_ssl_bio_initialized = 1;
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
void amqp_openssl_bio_destroy(void) {
|
||||
assert(amqp_ssl_bio_initialized);
|
||||
#ifdef AMQP_USE_AMQP_BIO
|
||||
BIO_meth_free(amqp_bio_method);
|
||||
amqp_bio_method = NULL;
|
||||
#endif
|
||||
amqp_ssl_bio_initialized = 0;
|
||||
}
|
||||
|
||||
BIO_METHOD_PTR amqp_openssl_bio(void) {
|
||||
assert(amqp_ssl_bio_initialized);
|
||||
#ifdef AMQP_USE_AMQP_BIO
|
||||
return amqp_bio_method;
|
||||
#else
|
||||
return BIO_s_socket();
|
||||
#endif
|
||||
}
|
||||
20
librabbitmq/amqp_openssl_bio.h
Normal file
20
librabbitmq/amqp_openssl_bio.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifndef AMQP_OPENSSL_BIO
|
||||
#define AMQP_OPENSSL_BIO
|
||||
|
||||
// Use OpenSSL v1.1.1 API.
|
||||
#define OPENSSL_API_COMPAT 10101
|
||||
|
||||
#include <openssl/bio.h>
|
||||
|
||||
int amqp_openssl_bio_init(void);
|
||||
|
||||
void amqp_openssl_bio_destroy(void);
|
||||
|
||||
typedef const BIO_METHOD *BIO_METHOD_PTR;
|
||||
|
||||
BIO_METHOD_PTR amqp_openssl_bio(void);
|
||||
|
||||
#endif /* ifndef AMQP_OPENSSL_BIO */
|
||||
337
librabbitmq/amqp_private.h
Normal file
337
librabbitmq/amqp_private.h
Normal file
@@ -0,0 +1,337 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifndef librabbitmq_amqp_private_h
|
||||
#define librabbitmq_amqp_private_h
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#define AMQ_COPYRIGHT \
|
||||
"Copyright (c) 2007-2014 VMWare Inc, Tony Garnock-Jones," \
|
||||
" and Alan Antonuk."
|
||||
|
||||
#include "rabbitmq-c/amqp.h"
|
||||
#include "rabbitmq-c/framing.h"
|
||||
#include <string.h>
|
||||
|
||||
#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
|
||||
#ifndef WINVER
|
||||
/* WINVER 0x0502 is WinXP SP2+, Windows Server 2003 SP1+
|
||||
* See:
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa383745(v=vs.85).aspx#macros_for_conditional_declarations
|
||||
*/
|
||||
#define WINVER 0x0502
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
/* GCC attributes */
|
||||
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
|
||||
#define AMQP_NORETURN __attribute__((__noreturn__))
|
||||
#define AMQP_UNUSED __attribute__((__unused__))
|
||||
#elif defined(_MSC_VER)
|
||||
#define AMQP_NORETURN __declspec(noreturn)
|
||||
#define AMQP_UNUSED __pragma(warning(suppress : 4100))
|
||||
#else
|
||||
#define AMQP_NORETURN
|
||||
#define AMQP_UNUSED
|
||||
#endif
|
||||
|
||||
#if (defined(_MSC_VER) && (_MSC_VER <= 1800)) || \
|
||||
(defined(__BORLANDC__) && (__BORLANDC__ <= 0x0564))
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
char *amqp_os_error_string(int err);
|
||||
|
||||
#include "amqp_socket.h"
|
||||
#include "amqp_time.h"
|
||||
|
||||
/*
|
||||
* Connection states: XXX FIX THIS
|
||||
*
|
||||
* - CONNECTION_STATE_INITIAL: The initial state, when we cannot be
|
||||
* sure if the next thing we will get is the first AMQP frame, or a
|
||||
* protocol header from the server.
|
||||
*
|
||||
* - CONNECTION_STATE_IDLE: The normal state between
|
||||
* frames. Connections may only be reconfigured, and the
|
||||
* connection's pools recycled, when in this state. Whenever we're
|
||||
* in this state, the inbound_buffer's bytes pointer must be NULL;
|
||||
* any other state, and it must point to a block of memory allocated
|
||||
* from the frame_pool.
|
||||
*
|
||||
* - CONNECTION_STATE_HEADER: Some bytes of an incoming frame have
|
||||
* been seen, but not a complete frame header's worth.
|
||||
*
|
||||
* - CONNECTION_STATE_BODY: A complete frame header has been seen, but
|
||||
* the frame is not yet complete. When it is completed, it will be
|
||||
* returned, and the connection will return to IDLE state.
|
||||
*
|
||||
*/
|
||||
typedef enum amqp_connection_state_enum_ {
|
||||
CONNECTION_STATE_IDLE = 0,
|
||||
CONNECTION_STATE_INITIAL,
|
||||
CONNECTION_STATE_HEADER,
|
||||
CONNECTION_STATE_BODY
|
||||
} amqp_connection_state_enum;
|
||||
|
||||
typedef enum amqp_status_private_enum_ {
|
||||
/* 0x00xx -> AMQP_STATUS_*/
|
||||
/* 0x01xx -> AMQP_STATUS_TCP_* */
|
||||
/* 0x02xx -> AMQP_STATUS_SSL_* */
|
||||
AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD = -0x1301,
|
||||
AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE = -0x1302
|
||||
} amqp_status_private_enum;
|
||||
|
||||
/* 7 bytes up front, then payload, then 1 byte footer */
|
||||
#define HEADER_SIZE 7
|
||||
#define FOOTER_SIZE 1
|
||||
|
||||
#define AMQP_PSEUDOFRAME_PROTOCOL_HEADER 'A'
|
||||
|
||||
typedef struct amqp_link_t_ {
|
||||
struct amqp_link_t_ *next;
|
||||
void *data;
|
||||
} amqp_link_t;
|
||||
|
||||
#define POOL_TABLE_SIZE 16
|
||||
|
||||
typedef struct amqp_pool_table_entry_t_ {
|
||||
struct amqp_pool_table_entry_t_ *next;
|
||||
amqp_pool_t pool;
|
||||
amqp_channel_t channel;
|
||||
} amqp_pool_table_entry_t;
|
||||
|
||||
struct amqp_connection_state_t_ {
|
||||
amqp_pool_table_entry_t *pool_table[POOL_TABLE_SIZE];
|
||||
|
||||
amqp_connection_state_enum state;
|
||||
|
||||
int channel_max;
|
||||
int frame_max;
|
||||
|
||||
/* Heartbeat interval in seconds. If this is <= 0, then heartbeats are not
|
||||
* enabled, and next_recv_heartbeat and next_send_heartbeat are set to
|
||||
* infinite */
|
||||
int heartbeat;
|
||||
amqp_time_t next_recv_heartbeat;
|
||||
amqp_time_t next_send_heartbeat;
|
||||
|
||||
/* buffer for holding frame headers. Allows us to delay allocating
|
||||
* the raw frame buffer until the type, channel, and size are all known
|
||||
*/
|
||||
char header_buffer[HEADER_SIZE + 1];
|
||||
amqp_bytes_t inbound_buffer;
|
||||
|
||||
size_t inbound_offset;
|
||||
size_t target_size;
|
||||
|
||||
amqp_bytes_t outbound_buffer;
|
||||
|
||||
amqp_socket_t *socket;
|
||||
|
||||
amqp_bytes_t sock_inbound_buffer;
|
||||
size_t sock_inbound_offset;
|
||||
size_t sock_inbound_limit;
|
||||
|
||||
amqp_link_t *first_queued_frame;
|
||||
amqp_link_t *last_queued_frame;
|
||||
|
||||
amqp_rpc_reply_t most_recent_api_result;
|
||||
|
||||
amqp_table_t server_properties;
|
||||
amqp_table_t client_properties;
|
||||
amqp_pool_t properties_pool;
|
||||
|
||||
struct timeval *handshake_timeout;
|
||||
struct timeval internal_handshake_timeout;
|
||||
struct timeval *rpc_timeout;
|
||||
struct timeval internal_rpc_timeout;
|
||||
};
|
||||
|
||||
amqp_pool_t *amqp_get_or_create_channel_pool(amqp_connection_state_t connection,
|
||||
amqp_channel_t channel);
|
||||
amqp_pool_t *amqp_get_channel_pool(amqp_connection_state_t state,
|
||||
amqp_channel_t channel);
|
||||
|
||||
static inline int amqp_heartbeat_send(amqp_connection_state_t state) {
|
||||
return state->heartbeat;
|
||||
}
|
||||
|
||||
static inline int amqp_heartbeat_recv(amqp_connection_state_t state) {
|
||||
return 2 * state->heartbeat;
|
||||
}
|
||||
|
||||
int amqp_try_recv(amqp_connection_state_t state);
|
||||
|
||||
static inline void *amqp_offset(void *data, size_t offset) {
|
||||
return (char *)data + offset;
|
||||
}
|
||||
|
||||
/* This macro defines the encoding and decoding functions associated with a
|
||||
simple type. */
|
||||
|
||||
#define DECLARE_CODEC_BASE_TYPE(bits) \
|
||||
\
|
||||
static inline int amqp_encode_##bits(amqp_bytes_t encoded, size_t *offset, \
|
||||
uint##bits##_t input) { \
|
||||
size_t o = *offset; \
|
||||
if ((*offset = o + bits / 8) <= encoded.len) { \
|
||||
amqp_e##bits(input, amqp_offset(encoded.bytes, o)); \
|
||||
return 1; \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
static inline int amqp_decode_##bits(amqp_bytes_t encoded, size_t *offset, \
|
||||
uint##bits##_t *output) { \
|
||||
size_t o = *offset; \
|
||||
if ((*offset = o + bits / 8) <= encoded.len) { \
|
||||
*output = amqp_d##bits(amqp_offset(encoded.bytes, o)); \
|
||||
return 1; \
|
||||
} \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
static inline int is_bigendian(void) {
|
||||
union {
|
||||
uint32_t i;
|
||||
char c[4];
|
||||
} bint = {0x01020304};
|
||||
return bint.c[0] == 1;
|
||||
}
|
||||
|
||||
static inline void amqp_e8(uint8_t val, void *data) {
|
||||
memcpy(data, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline uint8_t amqp_d8(void *data) {
|
||||
uint8_t val;
|
||||
memcpy(&val, data, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void amqp_e16(uint16_t val, void *data) {
|
||||
if (!is_bigendian()) {
|
||||
val = ((val & 0xFF00u) >> 8u) | ((val & 0x00FFu) << 8u);
|
||||
}
|
||||
memcpy(data, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline uint16_t amqp_d16(void *data) {
|
||||
uint16_t val;
|
||||
memcpy(&val, data, sizeof(val));
|
||||
if (!is_bigendian()) {
|
||||
val = ((val & 0xFF00u) >> 8u) | ((val & 0x00FFu) << 8u);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void amqp_e32(uint32_t val, void *data) {
|
||||
if (!is_bigendian()) {
|
||||
val = ((val & 0xFF000000u) >> 24u) | ((val & 0x00FF0000u) >> 8u) |
|
||||
((val & 0x0000FF00u) << 8u) | ((val & 0x000000FFu) << 24u);
|
||||
}
|
||||
memcpy(data, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline uint32_t amqp_d32(void *data) {
|
||||
uint32_t val;
|
||||
memcpy(&val, data, sizeof(val));
|
||||
if (!is_bigendian()) {
|
||||
val = ((val & 0xFF000000u) >> 24u) | ((val & 0x00FF0000u) >> 8u) |
|
||||
((val & 0x0000FF00u) << 8u) | ((val & 0x000000FFu) << 24u);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void amqp_e64(uint64_t val, void *data) {
|
||||
if (!is_bigendian()) {
|
||||
val = ((val & 0xFF00000000000000u) >> 56u) |
|
||||
((val & 0x00FF000000000000u) >> 40u) |
|
||||
((val & 0x0000FF0000000000u) >> 24u) |
|
||||
((val & 0x000000FF00000000u) >> 8u) |
|
||||
((val & 0x00000000FF000000u) << 8u) |
|
||||
((val & 0x0000000000FF0000u) << 24u) |
|
||||
((val & 0x000000000000FF00u) << 40u) |
|
||||
((val & 0x00000000000000FFu) << 56u);
|
||||
}
|
||||
memcpy(data, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline uint64_t amqp_d64(void *data) {
|
||||
uint64_t val;
|
||||
memcpy(&val, data, sizeof(val));
|
||||
if (!is_bigendian()) {
|
||||
val = ((val & 0xFF00000000000000u) >> 56u) |
|
||||
((val & 0x00FF000000000000u) >> 40u) |
|
||||
((val & 0x0000FF0000000000u) >> 24u) |
|
||||
((val & 0x000000FF00000000u) >> 8u) |
|
||||
((val & 0x00000000FF000000u) << 8u) |
|
||||
((val & 0x0000000000FF0000u) << 24u) |
|
||||
((val & 0x000000000000FF00u) << 40u) |
|
||||
((val & 0x00000000000000FFu) << 56u);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
DECLARE_CODEC_BASE_TYPE(8)
|
||||
DECLARE_CODEC_BASE_TYPE(16)
|
||||
DECLARE_CODEC_BASE_TYPE(32)
|
||||
DECLARE_CODEC_BASE_TYPE(64)
|
||||
|
||||
static inline int amqp_encode_bytes(amqp_bytes_t encoded, size_t *offset,
|
||||
amqp_bytes_t input) {
|
||||
size_t o = *offset;
|
||||
/* The memcpy below has undefined behavior if the input is NULL. It is valid
|
||||
* for a 0-length amqp_bytes_t to have .bytes == NULL. Thus we should check
|
||||
* before encoding.
|
||||
*/
|
||||
if (input.len == 0) {
|
||||
return 1;
|
||||
}
|
||||
if ((*offset = o + input.len) <= encoded.len) {
|
||||
memcpy(amqp_offset(encoded.bytes, o), input.bytes, input.len);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int amqp_decode_bytes(amqp_bytes_t encoded, size_t *offset,
|
||||
amqp_bytes_t *output, size_t len) {
|
||||
size_t o = *offset;
|
||||
if ((*offset = o + len) <= encoded.len) {
|
||||
output->bytes = amqp_offset(encoded.bytes, o);
|
||||
output->len = len;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
AMQP_NORETURN
|
||||
void amqp_abort(const char *fmt, ...);
|
||||
|
||||
int amqp_bytes_equal(amqp_bytes_t r, amqp_bytes_t l);
|
||||
|
||||
static inline amqp_rpc_reply_t amqp_rpc_reply_error(amqp_status_enum status) {
|
||||
amqp_rpc_reply_t reply;
|
||||
reply.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
|
||||
reply.library_error = status;
|
||||
return reply;
|
||||
}
|
||||
|
||||
int amqp_send_frame_inner(amqp_connection_state_t state,
|
||||
const amqp_frame_t *frame, int flags,
|
||||
amqp_time_t deadline);
|
||||
#endif
|
||||
1469
librabbitmq/amqp_socket.c
Normal file
1469
librabbitmq/amqp_socket.c
Normal file
File diff suppressed because it is too large
Load Diff
166
librabbitmq/amqp_socket.h
Normal file
166
librabbitmq/amqp_socket.h
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
/**
|
||||
* An abstract socket interface.
|
||||
*/
|
||||
|
||||
#ifndef AMQP_SOCKET_H
|
||||
#define AMQP_SOCKET_H
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include "amqp_time.h"
|
||||
|
||||
AMQP_BEGIN_DECLS
|
||||
|
||||
typedef enum {
|
||||
AMQP_SF_NONE = 0,
|
||||
AMQP_SF_MORE = 1,
|
||||
AMQP_SF_POLLIN = 2,
|
||||
AMQP_SF_POLLOUT = 4,
|
||||
AMQP_SF_POLLERR = 8
|
||||
} amqp_socket_flag_enum;
|
||||
|
||||
typedef enum { AMQP_SC_NONE = 0, AMQP_SC_FORCE = 1 } amqp_socket_close_enum;
|
||||
|
||||
int amqp_os_socket_error(void);
|
||||
|
||||
int amqp_os_socket_close(int sockfd);
|
||||
|
||||
/* Socket callbacks. */
|
||||
typedef ssize_t (*amqp_socket_send_fn)(void *, const void *, size_t, int);
|
||||
typedef ssize_t (*amqp_socket_recv_fn)(void *, void *, size_t, int);
|
||||
typedef int (*amqp_socket_open_fn)(void *, const char *, int,
|
||||
const struct timeval *);
|
||||
typedef int (*amqp_socket_close_fn)(void *, amqp_socket_close_enum);
|
||||
typedef int (*amqp_socket_get_sockfd_fn)(void *);
|
||||
typedef void (*amqp_socket_delete_fn)(void *);
|
||||
|
||||
/** V-table for amqp_socket_t */
|
||||
struct amqp_socket_class_t {
|
||||
amqp_socket_send_fn send;
|
||||
amqp_socket_recv_fn recv;
|
||||
amqp_socket_open_fn open;
|
||||
amqp_socket_close_fn close;
|
||||
amqp_socket_get_sockfd_fn get_sockfd;
|
||||
amqp_socket_delete_fn delete;
|
||||
};
|
||||
|
||||
/** Abstract base class for amqp_socket_t */
|
||||
struct amqp_socket_t_ {
|
||||
const struct amqp_socket_class_t *klass;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set set the socket object for a connection
|
||||
*
|
||||
* This assigns a socket object to the connection, closing and deleting any
|
||||
* existing socket
|
||||
*
|
||||
* \param [in] state The connection object to add the socket to
|
||||
* \param [in] socket The socket object to assign to the connection
|
||||
*/
|
||||
void amqp_set_socket(amqp_connection_state_t state, amqp_socket_t *socket);
|
||||
|
||||
/**
|
||||
* Send a message from a socket.
|
||||
*
|
||||
* This function wraps send(2) functionality.
|
||||
*
|
||||
* This function will only return on error, or when all of the bytes in buf
|
||||
* have been sent, or when an error occurs.
|
||||
*
|
||||
* \param [in,out] self A socket object.
|
||||
* \param [in] buf A buffer to read from.
|
||||
* \param [in] len The number of bytes in \e buf.
|
||||
* \param [in]
|
||||
*
|
||||
* \return AMQP_STATUS_OK on success. amqp_status_enum value otherwise
|
||||
*/
|
||||
ssize_t amqp_socket_send(amqp_socket_t *self, const void *buf, size_t len,
|
||||
int flags);
|
||||
|
||||
ssize_t amqp_try_send(amqp_connection_state_t state, const void *buf,
|
||||
size_t len, amqp_time_t deadline, int flags);
|
||||
|
||||
/**
|
||||
* Receive a message from a socket.
|
||||
*
|
||||
* This function wraps recv(2) functionality.
|
||||
*
|
||||
* \param [in,out] self A socket object.
|
||||
* \param [out] buf A buffer to write to.
|
||||
* \param [in] len The number of bytes at \e buf.
|
||||
* \param [in] flags Receive flags, implementation specific.
|
||||
*
|
||||
* \return The number of bytes received, or < 0 on error (\ref amqp_status_enum)
|
||||
*/
|
||||
ssize_t amqp_socket_recv(amqp_socket_t *self, void *buf, size_t len, int flags);
|
||||
|
||||
/**
|
||||
* Close a socket connection and free resources.
|
||||
*
|
||||
* This function closes a socket connection and releases any resources used by
|
||||
* the object. After calling this function the specified socket should no
|
||||
* longer be referenced.
|
||||
*
|
||||
* \param [in,out] self A socket object.
|
||||
* \param [in] force, if set, just close the socket, don't attempt a TLS
|
||||
* shutdown.
|
||||
*
|
||||
* \return Zero upon success, non-zero otherwise.
|
||||
*/
|
||||
int amqp_socket_close(amqp_socket_t *self, amqp_socket_close_enum force);
|
||||
|
||||
/**
|
||||
* Destroy a socket object
|
||||
*
|
||||
* \param [in] self the socket object to delete
|
||||
*/
|
||||
void amqp_socket_delete(amqp_socket_t *self);
|
||||
|
||||
/**
|
||||
* Open a socket connection.
|
||||
*
|
||||
* This function opens a socket connection returned from amqp_tcp_socket_new()
|
||||
* or amqp_ssl_socket_new(). This function should be called after setting
|
||||
* socket options and prior to assigning the socket to an AMQP connection with
|
||||
* amqp_set_socket().
|
||||
*
|
||||
* \param [in] host Connect to this host.
|
||||
* \param [in] port Connect on this remote port.
|
||||
* \param [in] timeout Max allowed time to spent on opening. If NULL - run in
|
||||
* blocking mode
|
||||
*
|
||||
* \return File descriptor upon success, non-zero negative error code otherwise.
|
||||
*/
|
||||
int amqp_open_socket_noblock(char const *hostname, int portnumber,
|
||||
const struct timeval *timeout);
|
||||
|
||||
int amqp_open_socket_inner(char const *hostname, int portnumber,
|
||||
amqp_time_t deadline);
|
||||
|
||||
/* Wait up to dealline for fd to become readable or writeable depending on
|
||||
* event (AMQP_SF_POLLIN, AMQP_SF_POLLOUT) */
|
||||
int amqp_poll(int fd, int event, amqp_time_t deadline);
|
||||
|
||||
int amqp_send_method_inner(amqp_connection_state_t state,
|
||||
amqp_channel_t channel, amqp_method_number_t id,
|
||||
void *decoded, int flags, amqp_time_t deadline);
|
||||
|
||||
int amqp_queue_frame(amqp_connection_state_t state, amqp_frame_t *frame);
|
||||
|
||||
int amqp_put_back_frame(amqp_connection_state_t state, amqp_frame_t *frame);
|
||||
|
||||
int amqp_simple_wait_frame_on_channel(amqp_connection_state_t state,
|
||||
amqp_channel_t channel,
|
||||
amqp_frame_t *decoded_frame);
|
||||
|
||||
int sasl_mechanism_in_list(amqp_bytes_t mechanisms,
|
||||
amqp_sasl_method_enum method);
|
||||
|
||||
int amqp_merge_capabilities(const amqp_table_t *base, const amqp_table_t *add,
|
||||
amqp_table_t *result, amqp_pool_t *pool);
|
||||
AMQP_END_DECLS
|
||||
|
||||
#endif /* AMQP_SOCKET_H */
|
||||
652
librabbitmq/amqp_table.c
Normal file
652
librabbitmq/amqp_table.c
Normal file
@@ -0,0 +1,652 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include "amqp_table.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define INITIAL_ARRAY_SIZE 16
|
||||
#define INITIAL_TABLE_SIZE 16
|
||||
#define TABLE_DEPTH_LIMIT 100
|
||||
|
||||
static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool,
|
||||
amqp_field_value_t *entry, size_t *offset,
|
||||
int depth);
|
||||
|
||||
static int amqp_encode_field_value(amqp_bytes_t encoded,
|
||||
amqp_field_value_t *entry, size_t *offset);
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
static int amqp_decode_array(amqp_bytes_t encoded, amqp_pool_t *pool,
|
||||
amqp_array_t *output, size_t *offset, int depth) {
|
||||
uint32_t arraysize;
|
||||
int num_entries = 0;
|
||||
int allocated_entries = INITIAL_ARRAY_SIZE;
|
||||
amqp_field_value_t *entries;
|
||||
size_t limit;
|
||||
int res;
|
||||
|
||||
if (!amqp_decode_32(encoded, offset, &arraysize)) {
|
||||
return AMQP_STATUS_BAD_AMQP_DATA;
|
||||
}
|
||||
|
||||
if (arraysize + *offset > encoded.len) {
|
||||
return AMQP_STATUS_BAD_AMQP_DATA;
|
||||
}
|
||||
|
||||
entries = malloc(allocated_entries * sizeof(amqp_field_value_t));
|
||||
if (entries == NULL) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
limit = *offset + arraysize;
|
||||
while (*offset < limit) {
|
||||
if (num_entries >= allocated_entries) {
|
||||
void *newentries;
|
||||
allocated_entries = allocated_entries * 2;
|
||||
newentries =
|
||||
realloc(entries, allocated_entries * sizeof(amqp_field_value_t));
|
||||
res = AMQP_STATUS_NO_MEMORY;
|
||||
if (newentries == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
entries = newentries;
|
||||
}
|
||||
|
||||
res = amqp_decode_field_value(encoded, pool, &entries[num_entries], offset,
|
||||
depth);
|
||||
if (res < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
num_entries++;
|
||||
}
|
||||
|
||||
output->num_entries = num_entries;
|
||||
output->entries =
|
||||
amqp_pool_alloc(pool, num_entries * sizeof(amqp_field_value_t));
|
||||
/* NULL is legitimate if we requested a zero-length block. */
|
||||
if (output->entries == NULL) {
|
||||
if (num_entries == 0) {
|
||||
res = AMQP_STATUS_OK;
|
||||
} else {
|
||||
res = AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(output->entries, entries, num_entries * sizeof(amqp_field_value_t));
|
||||
res = AMQP_STATUS_OK;
|
||||
|
||||
out:
|
||||
free(entries);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int amqp_decode_table_internal(amqp_bytes_t encoded, amqp_pool_t *pool,
|
||||
amqp_table_t *output, size_t *offset,
|
||||
int depth) {
|
||||
uint32_t tablesize;
|
||||
int num_entries = 0;
|
||||
amqp_table_entry_t *entries;
|
||||
int allocated_entries = INITIAL_TABLE_SIZE;
|
||||
size_t limit;
|
||||
int res;
|
||||
|
||||
if (!amqp_decode_32(encoded, offset, &tablesize)) {
|
||||
return AMQP_STATUS_BAD_AMQP_DATA;
|
||||
}
|
||||
|
||||
if (tablesize + *offset > encoded.len) {
|
||||
return AMQP_STATUS_BAD_AMQP_DATA;
|
||||
}
|
||||
|
||||
entries = malloc(allocated_entries * sizeof(amqp_table_entry_t));
|
||||
if (entries == NULL) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
limit = *offset + tablesize;
|
||||
while (*offset < limit) {
|
||||
uint8_t keylen;
|
||||
|
||||
res = AMQP_STATUS_BAD_AMQP_DATA;
|
||||
if (!amqp_decode_8(encoded, offset, &keylen)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (num_entries >= allocated_entries) {
|
||||
void *newentries;
|
||||
allocated_entries = allocated_entries * 2;
|
||||
newentries =
|
||||
realloc(entries, allocated_entries * sizeof(amqp_table_entry_t));
|
||||
res = AMQP_STATUS_NO_MEMORY;
|
||||
if (newentries == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
entries = newentries;
|
||||
}
|
||||
|
||||
res = AMQP_STATUS_BAD_AMQP_DATA;
|
||||
if (!amqp_decode_bytes(encoded, offset, &entries[num_entries].key,
|
||||
keylen)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = amqp_decode_field_value(encoded, pool, &entries[num_entries].value,
|
||||
offset, depth);
|
||||
if (res < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
num_entries++;
|
||||
}
|
||||
|
||||
output->num_entries = num_entries;
|
||||
output->entries =
|
||||
amqp_pool_alloc(pool, num_entries * sizeof(amqp_table_entry_t));
|
||||
/* NULL is legitimate if we requested a zero-length block. */
|
||||
if (output->entries == NULL) {
|
||||
if (num_entries == 0) {
|
||||
res = AMQP_STATUS_OK;
|
||||
} else {
|
||||
res = AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(output->entries, entries, num_entries * sizeof(amqp_table_entry_t));
|
||||
res = AMQP_STATUS_OK;
|
||||
|
||||
out:
|
||||
free(entries);
|
||||
return res;
|
||||
}
|
||||
|
||||
int amqp_decode_table(amqp_bytes_t encoded, amqp_pool_t *pool,
|
||||
amqp_table_t *output, size_t *offset) {
|
||||
return amqp_decode_table_internal(encoded, pool, output, offset, 0);
|
||||
}
|
||||
|
||||
static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool,
|
||||
amqp_field_value_t *entry, size_t *offset,
|
||||
int depth) {
|
||||
int res = AMQP_STATUS_BAD_AMQP_DATA;
|
||||
|
||||
if (depth > TABLE_DEPTH_LIMIT) {
|
||||
return AMQP_STATUS_BAD_AMQP_DATA;
|
||||
}
|
||||
|
||||
if (!amqp_decode_8(encoded, offset, &entry->kind)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
#define TRIVIAL_FIELD_DECODER(bits) \
|
||||
if (!amqp_decode_##bits(encoded, offset, &entry->value.u##bits)) goto out; \
|
||||
break
|
||||
#define SIMPLE_FIELD_DECODER(bits, dest, how) \
|
||||
{ \
|
||||
uint##bits##_t val; \
|
||||
if (!amqp_decode_##bits(encoded, offset, &val)) goto out; \
|
||||
entry->value.dest = how; \
|
||||
} \
|
||||
break
|
||||
|
||||
switch (entry->kind) {
|
||||
case AMQP_FIELD_KIND_BOOLEAN:
|
||||
SIMPLE_FIELD_DECODER(8, boolean, val ? 1 : 0);
|
||||
|
||||
case AMQP_FIELD_KIND_I8:
|
||||
SIMPLE_FIELD_DECODER(8, i8, (int8_t)val);
|
||||
case AMQP_FIELD_KIND_U8:
|
||||
TRIVIAL_FIELD_DECODER(8);
|
||||
|
||||
case AMQP_FIELD_KIND_I16:
|
||||
SIMPLE_FIELD_DECODER(16, i16, (int16_t)val);
|
||||
case AMQP_FIELD_KIND_U16:
|
||||
TRIVIAL_FIELD_DECODER(16);
|
||||
|
||||
case AMQP_FIELD_KIND_I32:
|
||||
SIMPLE_FIELD_DECODER(32, i32, (int32_t)val);
|
||||
case AMQP_FIELD_KIND_U32:
|
||||
TRIVIAL_FIELD_DECODER(32);
|
||||
|
||||
case AMQP_FIELD_KIND_I64:
|
||||
SIMPLE_FIELD_DECODER(64, i64, (int64_t)val);
|
||||
case AMQP_FIELD_KIND_U64:
|
||||
TRIVIAL_FIELD_DECODER(64);
|
||||
|
||||
case AMQP_FIELD_KIND_F32:
|
||||
TRIVIAL_FIELD_DECODER(32);
|
||||
/* and by punning, f32 magically gets the right value...! */
|
||||
|
||||
case AMQP_FIELD_KIND_F64:
|
||||
TRIVIAL_FIELD_DECODER(64);
|
||||
/* and by punning, f64 magically gets the right value...! */
|
||||
|
||||
case AMQP_FIELD_KIND_DECIMAL:
|
||||
if (!amqp_decode_8(encoded, offset, &entry->value.decimal.decimals) ||
|
||||
!amqp_decode_32(encoded, offset, &entry->value.decimal.value)) {
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_UTF8:
|
||||
/* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the
|
||||
same implementation, but different interpretations. */
|
||||
/* fall through */
|
||||
case AMQP_FIELD_KIND_BYTES: {
|
||||
uint32_t len;
|
||||
if (!amqp_decode_32(encoded, offset, &len) ||
|
||||
!amqp_decode_bytes(encoded, offset, &entry->value.bytes, len)) {
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AMQP_FIELD_KIND_ARRAY:
|
||||
res = amqp_decode_array(encoded, pool, &(entry->value.array), offset,
|
||||
depth + 1);
|
||||
goto out;
|
||||
|
||||
case AMQP_FIELD_KIND_TIMESTAMP:
|
||||
TRIVIAL_FIELD_DECODER(64);
|
||||
|
||||
case AMQP_FIELD_KIND_TABLE:
|
||||
res = amqp_decode_table_internal(encoded, pool, &(entry->value.table),
|
||||
offset, depth + 1);
|
||||
goto out;
|
||||
|
||||
case AMQP_FIELD_KIND_VOID:
|
||||
break;
|
||||
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = AMQP_STATUS_OK;
|
||||
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
static int amqp_encode_array(amqp_bytes_t encoded, amqp_array_t *input,
|
||||
size_t *offset) {
|
||||
size_t start = *offset;
|
||||
int i, res;
|
||||
|
||||
*offset += 4; /* size of the array gets filled in later on */
|
||||
|
||||
for (i = 0; i < input->num_entries; i++) {
|
||||
res = amqp_encode_field_value(encoded, &input->entries[i], offset);
|
||||
if (res < 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!amqp_encode_32(encoded, &start, (uint32_t)(*offset - start - 4))) {
|
||||
res = AMQP_STATUS_TABLE_TOO_BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = AMQP_STATUS_OK;
|
||||
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
int amqp_encode_table(amqp_bytes_t encoded, amqp_table_t *input,
|
||||
size_t *offset) {
|
||||
size_t start = *offset;
|
||||
int i, res;
|
||||
|
||||
*offset += 4; /* size of the table gets filled in later on */
|
||||
|
||||
for (i = 0; i < input->num_entries; i++) {
|
||||
if (!amqp_encode_8(encoded, offset, (uint8_t)input->entries[i].key.len)) {
|
||||
res = AMQP_STATUS_TABLE_TOO_BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!amqp_encode_bytes(encoded, offset, input->entries[i].key)) {
|
||||
res = AMQP_STATUS_TABLE_TOO_BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = amqp_encode_field_value(encoded, &input->entries[i].value, offset);
|
||||
if (res < 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!amqp_encode_32(encoded, &start, (uint32_t)(*offset - start - 4))) {
|
||||
res = AMQP_STATUS_TABLE_TOO_BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = AMQP_STATUS_OK;
|
||||
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static int amqp_encode_field_value(amqp_bytes_t encoded,
|
||||
amqp_field_value_t *entry, size_t *offset) {
|
||||
int res = AMQP_STATUS_BAD_AMQP_DATA;
|
||||
|
||||
if (!amqp_encode_8(encoded, offset, entry->kind)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
#define FIELD_ENCODER(bits, val) \
|
||||
if (!amqp_encode_##bits(encoded, offset, val)) { \
|
||||
res = AMQP_STATUS_TABLE_TOO_BIG; \
|
||||
goto out; \
|
||||
} \
|
||||
break
|
||||
|
||||
switch (entry->kind) {
|
||||
case AMQP_FIELD_KIND_BOOLEAN:
|
||||
FIELD_ENCODER(8, entry->value.boolean ? 1 : 0);
|
||||
|
||||
case AMQP_FIELD_KIND_I8:
|
||||
FIELD_ENCODER(8, entry->value.i8);
|
||||
case AMQP_FIELD_KIND_U8:
|
||||
FIELD_ENCODER(8, entry->value.u8);
|
||||
|
||||
case AMQP_FIELD_KIND_I16:
|
||||
FIELD_ENCODER(16, entry->value.i16);
|
||||
case AMQP_FIELD_KIND_U16:
|
||||
FIELD_ENCODER(16, entry->value.u16);
|
||||
|
||||
case AMQP_FIELD_KIND_I32:
|
||||
FIELD_ENCODER(32, entry->value.i32);
|
||||
case AMQP_FIELD_KIND_U32:
|
||||
FIELD_ENCODER(32, entry->value.u32);
|
||||
|
||||
case AMQP_FIELD_KIND_I64:
|
||||
FIELD_ENCODER(64, entry->value.i64);
|
||||
case AMQP_FIELD_KIND_U64:
|
||||
FIELD_ENCODER(64, entry->value.u64);
|
||||
|
||||
case AMQP_FIELD_KIND_F32:
|
||||
/* by punning, u32 magically gets the right value...! */
|
||||
FIELD_ENCODER(32, entry->value.u32);
|
||||
|
||||
case AMQP_FIELD_KIND_F64:
|
||||
/* by punning, u64 magically gets the right value...! */
|
||||
FIELD_ENCODER(64, entry->value.u64);
|
||||
|
||||
case AMQP_FIELD_KIND_DECIMAL:
|
||||
if (!amqp_encode_8(encoded, offset, entry->value.decimal.decimals) ||
|
||||
!amqp_encode_32(encoded, offset, entry->value.decimal.value)) {
|
||||
res = AMQP_STATUS_TABLE_TOO_BIG;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_UTF8:
|
||||
/* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the
|
||||
same implementation, but different interpretations. */
|
||||
/* fall through */
|
||||
case AMQP_FIELD_KIND_BYTES:
|
||||
if (!amqp_encode_32(encoded, offset, (uint32_t)entry->value.bytes.len) ||
|
||||
!amqp_encode_bytes(encoded, offset, entry->value.bytes)) {
|
||||
res = AMQP_STATUS_TABLE_TOO_BIG;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_ARRAY:
|
||||
res = amqp_encode_array(encoded, &entry->value.array, offset);
|
||||
goto out;
|
||||
|
||||
case AMQP_FIELD_KIND_TIMESTAMP:
|
||||
FIELD_ENCODER(64, entry->value.u64);
|
||||
|
||||
case AMQP_FIELD_KIND_TABLE:
|
||||
res = amqp_encode_table(encoded, &entry->value.table, offset);
|
||||
goto out;
|
||||
|
||||
case AMQP_FIELD_KIND_VOID:
|
||||
break;
|
||||
|
||||
default:
|
||||
res = AMQP_STATUS_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = AMQP_STATUS_OK;
|
||||
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
int amqp_table_entry_cmp(void const *entry1, void const *entry2) {
|
||||
amqp_table_entry_t const *p1 = (amqp_table_entry_t const *)entry1;
|
||||
amqp_table_entry_t const *p2 = (amqp_table_entry_t const *)entry2;
|
||||
|
||||
int d;
|
||||
size_t minlen;
|
||||
|
||||
minlen = p1->key.len;
|
||||
if (p2->key.len < minlen) {
|
||||
minlen = p2->key.len;
|
||||
}
|
||||
|
||||
d = memcmp(p1->key.bytes, p2->key.bytes, minlen);
|
||||
if (d != 0) {
|
||||
return d;
|
||||
}
|
||||
|
||||
return (int)p1->key.len - (int)p2->key.len;
|
||||
}
|
||||
|
||||
static int amqp_field_value_clone(const amqp_field_value_t *original,
|
||||
amqp_field_value_t *clone,
|
||||
amqp_pool_t *pool) {
|
||||
int i;
|
||||
int res;
|
||||
clone->kind = original->kind;
|
||||
|
||||
switch (clone->kind) {
|
||||
case AMQP_FIELD_KIND_BOOLEAN:
|
||||
clone->value.boolean = original->value.boolean;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_I8:
|
||||
clone->value.i8 = original->value.i8;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_U8:
|
||||
clone->value.u8 = original->value.u8;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_I16:
|
||||
clone->value.i16 = original->value.i16;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_U16:
|
||||
clone->value.u16 = original->value.u16;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_I32:
|
||||
clone->value.i32 = original->value.i32;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_U32:
|
||||
clone->value.u32 = original->value.u32;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_I64:
|
||||
clone->value.i64 = original->value.i64;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_U64:
|
||||
case AMQP_FIELD_KIND_TIMESTAMP:
|
||||
clone->value.u64 = original->value.u64;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_F32:
|
||||
clone->value.f32 = original->value.f32;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_F64:
|
||||
clone->value.f64 = original->value.f64;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_DECIMAL:
|
||||
clone->value.decimal = original->value.decimal;
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_UTF8:
|
||||
case AMQP_FIELD_KIND_BYTES:
|
||||
if (0 == original->value.bytes.len) {
|
||||
clone->value.bytes = amqp_empty_bytes;
|
||||
} else {
|
||||
amqp_pool_alloc_bytes(pool, original->value.bytes.len,
|
||||
&clone->value.bytes);
|
||||
if (NULL == clone->value.bytes.bytes) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
memcpy(clone->value.bytes.bytes, original->value.bytes.bytes,
|
||||
clone->value.bytes.len);
|
||||
}
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_ARRAY:
|
||||
if (0 == original->value.array.entries) {
|
||||
clone->value.array = amqp_empty_array;
|
||||
} else {
|
||||
clone->value.array.num_entries = original->value.array.num_entries;
|
||||
clone->value.array.entries = amqp_pool_alloc(
|
||||
pool, clone->value.array.num_entries * sizeof(amqp_field_value_t));
|
||||
if (NULL == clone->value.array.entries) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (i = 0; i < clone->value.array.num_entries; ++i) {
|
||||
res = amqp_field_value_clone(&original->value.array.entries[i],
|
||||
&clone->value.array.entries[i], pool);
|
||||
if (AMQP_STATUS_OK != res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AMQP_FIELD_KIND_TABLE:
|
||||
return amqp_table_clone(&original->value.table, &clone->value.table,
|
||||
pool);
|
||||
|
||||
case AMQP_FIELD_KIND_VOID:
|
||||
break;
|
||||
|
||||
default:
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
static int amqp_table_entry_clone(const amqp_table_entry_t *original,
|
||||
amqp_table_entry_t *clone,
|
||||
amqp_pool_t *pool) {
|
||||
if (0 == original->key.len) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
amqp_pool_alloc_bytes(pool, original->key.len, &clone->key);
|
||||
if (NULL == clone->key.bytes) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(clone->key.bytes, original->key.bytes, clone->key.len);
|
||||
|
||||
return amqp_field_value_clone(&original->value, &clone->value, pool);
|
||||
}
|
||||
|
||||
int amqp_table_clone(const amqp_table_t *original, amqp_table_t *clone,
|
||||
amqp_pool_t *pool) {
|
||||
int i;
|
||||
int res;
|
||||
clone->num_entries = original->num_entries;
|
||||
if (0 == clone->num_entries) {
|
||||
*clone = amqp_empty_table;
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
clone->entries =
|
||||
amqp_pool_alloc(pool, clone->num_entries * sizeof(amqp_table_entry_t));
|
||||
|
||||
if (NULL == clone->entries) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (i = 0; i < clone->num_entries; ++i) {
|
||||
res =
|
||||
amqp_table_entry_clone(&original->entries[i], &clone->entries[i], pool);
|
||||
if (AMQP_STATUS_OK != res) {
|
||||
goto error_out1;
|
||||
}
|
||||
}
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
|
||||
error_out1:
|
||||
return res;
|
||||
}
|
||||
|
||||
amqp_table_entry_t amqp_table_construct_utf8_entry(const char *key,
|
||||
const char *value) {
|
||||
amqp_table_entry_t ret;
|
||||
ret.key = amqp_cstring_bytes(key);
|
||||
ret.value.kind = AMQP_FIELD_KIND_UTF8;
|
||||
ret.value.value.bytes = amqp_cstring_bytes(value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
amqp_table_entry_t amqp_table_construct_table_entry(const char *key,
|
||||
const amqp_table_t *value) {
|
||||
amqp_table_entry_t ret;
|
||||
ret.key = amqp_cstring_bytes(key);
|
||||
ret.value.kind = AMQP_FIELD_KIND_TABLE;
|
||||
ret.value.value.table = *value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
amqp_table_entry_t amqp_table_construct_bool_entry(const char *key,
|
||||
const int value) {
|
||||
amqp_table_entry_t ret;
|
||||
ret.key = amqp_cstring_bytes(key);
|
||||
ret.value.kind = AMQP_FIELD_KIND_BOOLEAN;
|
||||
ret.value.value.boolean = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
amqp_table_entry_t *amqp_table_get_entry_by_key(const amqp_table_t *table,
|
||||
const amqp_bytes_t key) {
|
||||
int i;
|
||||
assert(table != NULL);
|
||||
for (i = 0; i < table->num_entries; ++i) {
|
||||
if (amqp_bytes_equal(table->entries[i].key, key)) {
|
||||
return &table->entries[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
56
librabbitmq/amqp_table.h
Normal file
56
librabbitmq/amqp_table.h
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifndef AMQP_TABLE_H
|
||||
#define AMQP_TABLE_H
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include "rabbitmq-c/amqp.h"
|
||||
|
||||
/**
|
||||
* Initializes a table entry with utf-8 string type value.
|
||||
*
|
||||
* \param [in] key the table entry key. The string must remain valid for the
|
||||
* life of the resulting amqp_table_entry_t.
|
||||
* \param [in] value the string value. The string must remain valid for the life
|
||||
* of the resulting amqp_table_entry_t.
|
||||
* \returns An initialized table entry.
|
||||
*/
|
||||
amqp_table_entry_t amqp_table_construct_utf8_entry(const char *key,
|
||||
const char *value);
|
||||
|
||||
/**
|
||||
* Initializes a table entry with table type value.
|
||||
*
|
||||
* \param [in] key the table entry key. The string must remain value for the
|
||||
* life of the resulting amqp_table_entry_t.
|
||||
* \param [in] value the amqp_table_t value. The table must remain valid for the
|
||||
* life of the resulting amqp_table_entry_t.
|
||||
* \returns An initialized table entry.
|
||||
*/
|
||||
amqp_table_entry_t amqp_table_construct_table_entry(const char *key,
|
||||
const amqp_table_t *value);
|
||||
|
||||
/**
|
||||
* Initializes a table entry with boolean type value.
|
||||
*
|
||||
* \param [in] key the table entry key. The string must remain value for the
|
||||
* life of the resulting amqp_table_entry_t.
|
||||
* \param [in] value the boolean value. 0 means false, any other value is true.
|
||||
* \returns An initialized table entry.
|
||||
*/
|
||||
amqp_table_entry_t amqp_table_construct_bool_entry(const char *key,
|
||||
const int value);
|
||||
|
||||
/**
|
||||
* Searches a table for an entry with a matching key.
|
||||
*
|
||||
* \param [in] table the table to search.
|
||||
* \param [in] key the string to search with.
|
||||
* \returns a pointer to the table entry in the table if a matching key can be
|
||||
* found, NULL otherwise.
|
||||
*/
|
||||
amqp_table_entry_t *amqp_table_get_entry_by_key(const amqp_table_t *table,
|
||||
const amqp_bytes_t key);
|
||||
|
||||
#endif /* AMQP_TABLE_H */
|
||||
219
librabbitmq/amqp_tcp_socket.c
Normal file
219
librabbitmq/amqp_tcp_socket.c
Normal file
@@ -0,0 +1,219 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include "rabbitmq-c/tcp_socket.h"
|
||||
|
||||
#include <errno.h>
|
||||
#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct amqp_tcp_socket_t {
|
||||
const struct amqp_socket_class_t *klass;
|
||||
int sockfd;
|
||||
int internal_error;
|
||||
int state;
|
||||
};
|
||||
|
||||
static ssize_t amqp_tcp_socket_send(void *base, const void *buf, size_t len,
|
||||
int flags) {
|
||||
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
|
||||
ssize_t res;
|
||||
int flagz = 0;
|
||||
|
||||
if (-1 == self->sockfd) {
|
||||
return AMQP_STATUS_SOCKET_CLOSED;
|
||||
}
|
||||
|
||||
#ifdef MSG_NOSIGNAL
|
||||
flagz |= MSG_NOSIGNAL;
|
||||
#endif
|
||||
|
||||
#if defined(MSG_MORE)
|
||||
if (flags & AMQP_SF_MORE) {
|
||||
flagz |= MSG_MORE;
|
||||
}
|
||||
/* Cygwin defines TCP_NOPUSH, but trying to use it will return not
|
||||
* implemented. Disable it here. */
|
||||
#elif defined(TCP_NOPUSH) && !defined(__CYGWIN__)
|
||||
if (flags & AMQP_SF_MORE && !(self->state & AMQP_SF_MORE)) {
|
||||
int one = 1;
|
||||
res = setsockopt(self->sockfd, IPPROTO_TCP, TCP_NOPUSH, &one, sizeof(one));
|
||||
if (0 != res) {
|
||||
self->internal_error = res;
|
||||
return AMQP_STATUS_SOCKET_ERROR;
|
||||
}
|
||||
self->state |= AMQP_SF_MORE;
|
||||
} else if (!(flags & AMQP_SF_MORE) && self->state & AMQP_SF_MORE) {
|
||||
int zero = 0;
|
||||
res =
|
||||
setsockopt(self->sockfd, IPPROTO_TCP, TCP_NOPUSH, &zero, sizeof(&zero));
|
||||
if (0 != res) {
|
||||
self->internal_error = res;
|
||||
res = AMQP_STATUS_SOCKET_ERROR;
|
||||
} else {
|
||||
self->state &= ~AMQP_SF_MORE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
start:
|
||||
#ifdef _WIN32
|
||||
res = send(self->sockfd, buf, (int)len, flagz);
|
||||
#else
|
||||
res = send(self->sockfd, buf, len, flagz);
|
||||
#endif
|
||||
|
||||
if (res < 0) {
|
||||
self->internal_error = amqp_os_socket_error();
|
||||
switch (self->internal_error) {
|
||||
case EINTR:
|
||||
goto start;
|
||||
#ifdef _WIN32
|
||||
case WSAEWOULDBLOCK:
|
||||
#else
|
||||
case EWOULDBLOCK:
|
||||
#endif
|
||||
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
|
||||
case EAGAIN:
|
||||
#endif
|
||||
res = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
|
||||
break;
|
||||
default:
|
||||
res = AMQP_STATUS_SOCKET_ERROR;
|
||||
}
|
||||
} else {
|
||||
self->internal_error = 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t amqp_tcp_socket_recv(void *base, void *buf, size_t len,
|
||||
int flags) {
|
||||
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
|
||||
ssize_t ret;
|
||||
if (-1 == self->sockfd) {
|
||||
return AMQP_STATUS_SOCKET_CLOSED;
|
||||
}
|
||||
|
||||
start:
|
||||
#ifdef _WIN32
|
||||
ret = recv(self->sockfd, buf, (int)len, flags);
|
||||
#else
|
||||
ret = recv(self->sockfd, buf, len, flags);
|
||||
#endif
|
||||
|
||||
if (0 > ret) {
|
||||
self->internal_error = amqp_os_socket_error();
|
||||
switch (self->internal_error) {
|
||||
case EINTR:
|
||||
goto start;
|
||||
#ifdef _WIN32
|
||||
case WSAEWOULDBLOCK:
|
||||
#else
|
||||
case EWOULDBLOCK:
|
||||
#endif
|
||||
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
|
||||
case EAGAIN:
|
||||
#endif
|
||||
ret = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
|
||||
break;
|
||||
default:
|
||||
ret = AMQP_STATUS_SOCKET_ERROR;
|
||||
}
|
||||
} else if (0 == ret) {
|
||||
ret = AMQP_STATUS_CONNECTION_CLOSED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int amqp_tcp_socket_open(void *base, const char *host, int port,
|
||||
const struct timeval *timeout) {
|
||||
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
|
||||
if (-1 != self->sockfd) {
|
||||
return AMQP_STATUS_SOCKET_INUSE;
|
||||
}
|
||||
self->sockfd = amqp_open_socket_noblock(host, port, timeout);
|
||||
if (0 > self->sockfd) {
|
||||
int err = self->sockfd;
|
||||
self->sockfd = -1;
|
||||
return err;
|
||||
}
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
static int amqp_tcp_socket_close(void *base,
|
||||
AMQP_UNUSED amqp_socket_close_enum force) {
|
||||
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
|
||||
if (-1 == self->sockfd) {
|
||||
return AMQP_STATUS_SOCKET_CLOSED;
|
||||
}
|
||||
|
||||
if (amqp_os_socket_close(self->sockfd)) {
|
||||
return AMQP_STATUS_SOCKET_ERROR;
|
||||
}
|
||||
self->sockfd = -1;
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
static int amqp_tcp_socket_get_sockfd(void *base) {
|
||||
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
|
||||
return self->sockfd;
|
||||
}
|
||||
|
||||
static void amqp_tcp_socket_delete(void *base) {
|
||||
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
|
||||
|
||||
if (self) {
|
||||
amqp_tcp_socket_close(self, AMQP_SC_NONE);
|
||||
free(self);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct amqp_socket_class_t amqp_tcp_socket_class = {
|
||||
amqp_tcp_socket_send, /* send */
|
||||
amqp_tcp_socket_recv, /* recv */
|
||||
amqp_tcp_socket_open, /* open */
|
||||
amqp_tcp_socket_close, /* close */
|
||||
amqp_tcp_socket_get_sockfd, /* get_sockfd */
|
||||
amqp_tcp_socket_delete /* delete */
|
||||
};
|
||||
|
||||
amqp_socket_t *amqp_tcp_socket_new(amqp_connection_state_t state) {
|
||||
struct amqp_tcp_socket_t *self = calloc(1, sizeof(*self));
|
||||
if (!self) {
|
||||
return NULL;
|
||||
}
|
||||
self->klass = &amqp_tcp_socket_class;
|
||||
self->sockfd = -1;
|
||||
|
||||
amqp_set_socket(state, (amqp_socket_t *)self);
|
||||
|
||||
return (amqp_socket_t *)self;
|
||||
}
|
||||
|
||||
void amqp_tcp_socket_set_sockfd(amqp_socket_t *base, int sockfd) {
|
||||
struct amqp_tcp_socket_t *self;
|
||||
if (base->klass != &amqp_tcp_socket_class) {
|
||||
amqp_abort("<%p> is not of type amqp_tcp_socket_t", base);
|
||||
}
|
||||
self = (struct amqp_tcp_socket_t *)base;
|
||||
self->sockfd = sockfd;
|
||||
}
|
||||
236
librabbitmq/amqp_time.c
Normal file
236
librabbitmq/amqp_time.c
Normal file
@@ -0,0 +1,236 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#include "amqp_time.h"
|
||||
#include "rabbitmq-c/amqp.h"
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || \
|
||||
defined(__MINGW32__) || defined(__MINGW64__))
|
||||
#define AMQP_WIN_TIMER_API
|
||||
#elif (defined(machintosh) || defined(__APPLE__) || defined(__APPLE_CC__))
|
||||
#define AMQP_MAC_TIMER_API
|
||||
#else
|
||||
#define AMQP_POSIX_TIMER_API
|
||||
#endif
|
||||
|
||||
#ifdef AMQP_WIN_TIMER_API
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
uint64_t amqp_get_monotonic_timestamp(void) {
|
||||
static double NS_PER_COUNT = 0;
|
||||
LARGE_INTEGER perf_count;
|
||||
|
||||
if (0 == NS_PER_COUNT) {
|
||||
LARGE_INTEGER perf_frequency;
|
||||
if (!QueryPerformanceFrequency(&perf_frequency)) {
|
||||
return 0;
|
||||
}
|
||||
NS_PER_COUNT = (double)AMQP_NS_PER_S / perf_frequency.QuadPart;
|
||||
}
|
||||
|
||||
if (!QueryPerformanceCounter(&perf_count)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uint64_t)(perf_count.QuadPart * NS_PER_COUNT);
|
||||
}
|
||||
#endif /* AMQP_WIN_TIMER_API */
|
||||
|
||||
#ifdef AMQP_MAC_TIMER_API
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
uint64_t amqp_get_monotonic_timestamp(void) {
|
||||
static mach_timebase_info_data_t s_timebase = {0, 0};
|
||||
uint64_t timestamp;
|
||||
|
||||
timestamp = mach_absolute_time();
|
||||
|
||||
if (s_timebase.denom == 0) {
|
||||
mach_timebase_info(&s_timebase);
|
||||
if (0 == s_timebase.denom) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
timestamp *= (uint64_t)s_timebase.numer;
|
||||
timestamp /= (uint64_t)s_timebase.denom;
|
||||
|
||||
return timestamp;
|
||||
}
|
||||
#endif /* AMQP_MAC_TIMER_API */
|
||||
|
||||
#ifdef AMQP_POSIX_TIMER_API
|
||||
#include <time.h>
|
||||
|
||||
uint64_t amqp_get_monotonic_timestamp(void) {
|
||||
#ifdef __hpux
|
||||
return (uint64_t)gethrtime();
|
||||
#else
|
||||
struct timespec tp;
|
||||
if (-1 == clock_gettime(CLOCK_MONOTONIC, &tp)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ((uint64_t)tp.tv_sec * AMQP_NS_PER_S + (uint64_t)tp.tv_nsec);
|
||||
#endif
|
||||
}
|
||||
#endif /* AMQP_POSIX_TIMER_API */
|
||||
|
||||
int amqp_time_from_now(amqp_time_t *time, const struct timeval *timeout) {
|
||||
uint64_t now_ns;
|
||||
uint64_t delta_ns;
|
||||
|
||||
assert(NULL != time);
|
||||
|
||||
if (NULL == timeout) {
|
||||
*time = amqp_time_infinite();
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (timeout->tv_sec < 0 || timeout->tv_usec < 0) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
delta_ns = (uint64_t)timeout->tv_sec * AMQP_NS_PER_S +
|
||||
(uint64_t)timeout->tv_usec * AMQP_NS_PER_US;
|
||||
|
||||
now_ns = amqp_get_monotonic_timestamp();
|
||||
if (0 == now_ns) {
|
||||
return AMQP_STATUS_TIMER_FAILURE;
|
||||
}
|
||||
|
||||
time->time_point_ns = now_ns + delta_ns;
|
||||
if (now_ns > time->time_point_ns || delta_ns > time->time_point_ns) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
int amqp_time_s_from_now(amqp_time_t *time, int seconds) {
|
||||
uint64_t now_ns;
|
||||
uint64_t delta_ns;
|
||||
assert(NULL != time);
|
||||
|
||||
if (0 >= seconds) {
|
||||
*time = amqp_time_infinite();
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
now_ns = amqp_get_monotonic_timestamp();
|
||||
if (0 == now_ns) {
|
||||
return AMQP_STATUS_TIMER_FAILURE;
|
||||
}
|
||||
|
||||
delta_ns = (uint64_t)seconds * AMQP_NS_PER_S;
|
||||
time->time_point_ns = now_ns + delta_ns;
|
||||
if (now_ns > time->time_point_ns || delta_ns > time->time_point_ns) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
amqp_time_t amqp_time_infinite(void) {
|
||||
amqp_time_t time;
|
||||
time.time_point_ns = UINT64_MAX;
|
||||
return time;
|
||||
}
|
||||
|
||||
int amqp_time_ms_until(amqp_time_t time) {
|
||||
uint64_t now_ns;
|
||||
uint64_t delta_ns;
|
||||
int left_ms;
|
||||
|
||||
if (UINT64_MAX == time.time_point_ns) {
|
||||
return -1;
|
||||
}
|
||||
if (0 == time.time_point_ns) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
now_ns = amqp_get_monotonic_timestamp();
|
||||
if (0 == now_ns) {
|
||||
return AMQP_STATUS_TIMER_FAILURE;
|
||||
}
|
||||
|
||||
if (now_ns >= time.time_point_ns) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
delta_ns = time.time_point_ns - now_ns;
|
||||
left_ms = (int)(delta_ns / AMQP_NS_PER_MS);
|
||||
|
||||
return left_ms;
|
||||
}
|
||||
|
||||
int amqp_time_tv_until(amqp_time_t time, struct timeval *in,
|
||||
struct timeval **out) {
|
||||
uint64_t now_ns;
|
||||
uint64_t delta_ns;
|
||||
|
||||
assert(in != NULL);
|
||||
if (UINT64_MAX == time.time_point_ns) {
|
||||
*out = NULL;
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
if (0 == time.time_point_ns) {
|
||||
in->tv_sec = 0;
|
||||
in->tv_usec = 0;
|
||||
*out = in;
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
now_ns = amqp_get_monotonic_timestamp();
|
||||
if (0 == now_ns) {
|
||||
return AMQP_STATUS_TIMER_FAILURE;
|
||||
}
|
||||
|
||||
if (now_ns >= time.time_point_ns) {
|
||||
in->tv_sec = 0;
|
||||
in->tv_usec = 0;
|
||||
*out = in;
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
delta_ns = time.time_point_ns - now_ns;
|
||||
in->tv_sec = (int)(delta_ns / AMQP_NS_PER_S);
|
||||
in->tv_usec = (int)((delta_ns % AMQP_NS_PER_S) / AMQP_NS_PER_US);
|
||||
*out = in;
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
int amqp_time_has_past(amqp_time_t time) {
|
||||
uint64_t now_ns;
|
||||
if (UINT64_MAX == time.time_point_ns) {
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
now_ns = amqp_get_monotonic_timestamp();
|
||||
if (0 == now_ns) {
|
||||
return AMQP_STATUS_TIMER_FAILURE;
|
||||
}
|
||||
|
||||
if (now_ns > time.time_point_ns) {
|
||||
return AMQP_STATUS_TIMEOUT;
|
||||
}
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
amqp_time_t amqp_time_first(amqp_time_t l, amqp_time_t r) {
|
||||
if (l.time_point_ns < r.time_point_ns) {
|
||||
return l;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int amqp_time_equal(amqp_time_t l, amqp_time_t r) {
|
||||
return l.time_point_ns == r.time_point_ns;
|
||||
}
|
||||
109
librabbitmq/amqp_time.h
Normal file
109
librabbitmq/amqp_time.h
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifndef AMQP_TIMER_H
|
||||
#define AMQP_TIMER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
|
||||
#ifndef WINVER
|
||||
#define WINVER 0x0502
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#define AMQP_MS_PER_S 1000
|
||||
#define AMQP_US_PER_MS 1000
|
||||
#define AMQP_NS_PER_S 1000000000
|
||||
#define AMQP_NS_PER_MS 1000000
|
||||
#define AMQP_NS_PER_US 1000
|
||||
|
||||
/* This represents a point in time in reference to a monotonic clock.
|
||||
*
|
||||
* The internal representation is ns, relative to the monotonic clock.
|
||||
*
|
||||
* There are two 'special' values:
|
||||
* - 0: means 'this instant', its meant for polls with a 0-timeout, or
|
||||
* non-blocking option
|
||||
* - UINT64_MAX: means 'at infinity', its mean for polls with an infinite
|
||||
* timeout
|
||||
*/
|
||||
typedef struct amqp_time_t_ {
|
||||
uint64_t time_point_ns;
|
||||
} amqp_time_t;
|
||||
|
||||
/* Gets a monotonic timestamp. This will return 0 if the underlying call to the
|
||||
* system fails.
|
||||
*/
|
||||
uint64_t amqp_get_monotonic_timestamp(void);
|
||||
|
||||
/* Get a amqp_time_t that is timeout from now.
|
||||
* If timeout is NULL, an amqp_time_infinite() is created.
|
||||
*
|
||||
* Returns AMQP_STATUS_OK on success.
|
||||
* AMQP_STATUS_INVALID_PARAMETER if timeout is invalid
|
||||
* AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current timestamp
|
||||
* fails.
|
||||
*/
|
||||
int amqp_time_from_now(amqp_time_t *time, const struct timeval *timeout);
|
||||
|
||||
/* Get a amqp_time_t that is seconds from now.
|
||||
* If seconds <= 0, then amqp_time_infinite() is created.
|
||||
*
|
||||
* Returns AMQP_STATUS_OK on success.
|
||||
* AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current timestamp
|
||||
* fails.
|
||||
*/
|
||||
int amqp_time_s_from_now(amqp_time_t *time, int seconds);
|
||||
|
||||
/* Create an infinite amqp_time_t */
|
||||
amqp_time_t amqp_time_infinite(void);
|
||||
|
||||
/* Gets the number of ms until the amqp_time_t, suitable for the timeout
|
||||
* parameter in poll().
|
||||
*
|
||||
* -1 will be returned for amqp_time_infinite values.
|
||||
* 0 will be returned for amqp_time_immediate values.
|
||||
* AMQP_STATUS_TIMEOUT will be returned if time was in the past.
|
||||
* AMQP_STATUS_TIMER_FAILURE will be returned if the underlying call to get the
|
||||
* current timestamp fails.
|
||||
*/
|
||||
int amqp_time_ms_until(amqp_time_t time);
|
||||
|
||||
/* Gets a timeval filled in with the time until amqp_time_t. Suitable for the
|
||||
* parameter in select().
|
||||
*
|
||||
* The in parameter specifies a storage location for *out.
|
||||
* If time is an inf timeout, then *out = NULL.
|
||||
* If time is a 0-timeout or the timer has expired, then *out = {0, 0}
|
||||
* Otherwise *out is set to the time left on the time.
|
||||
*
|
||||
* AMQP_STATUS_OK will be returned if successfully filled.
|
||||
* AMQP_STATUS_TIMER_FAILURE is returned when the underlying call to get the
|
||||
* current timestamp fails.
|
||||
*/
|
||||
int amqp_time_tv_until(amqp_time_t time, struct timeval *in,
|
||||
struct timeval **out);
|
||||
|
||||
/* Test whether current time is past the provided time.
|
||||
*
|
||||
* TODO: this isn't a great interface to use. Fix this.
|
||||
*
|
||||
* Return AMQP_STATUS_OK if time has not past
|
||||
* Return AMQP_STATUS_TIMEOUT if time has past
|
||||
* Return AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current
|
||||
* timestamp fails.
|
||||
*/
|
||||
int amqp_time_has_past(amqp_time_t time);
|
||||
|
||||
/* Return the time value that happens first */
|
||||
amqp_time_t amqp_time_first(amqp_time_t l, amqp_time_t r);
|
||||
|
||||
int amqp_time_equal(amqp_time_t l, amqp_time_t r);
|
||||
#endif /* AMQP_TIMER_H */
|
||||
188
librabbitmq/amqp_url.c
Normal file
188
librabbitmq/amqp_url.c
Normal file
@@ -0,0 +1,188 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void amqp_default_connection_info(struct amqp_connection_info *ci) {
|
||||
/* Apply defaults */
|
||||
ci->user = "guest";
|
||||
ci->password = "guest";
|
||||
ci->host = "localhost";
|
||||
ci->port = 5672;
|
||||
ci->vhost = "/";
|
||||
ci->ssl = 0;
|
||||
}
|
||||
|
||||
/* Scan for the next delimiter, handling percent-encodings on the way. */
|
||||
static char find_delim(char **pp, int colon_and_at_sign_are_delims) {
|
||||
char *from = *pp;
|
||||
char *to = from;
|
||||
|
||||
for (;;) {
|
||||
char ch = *from++;
|
||||
|
||||
switch (ch) {
|
||||
case ':':
|
||||
case '@':
|
||||
if (!colon_and_at_sign_are_delims) {
|
||||
*to++ = ch;
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
case 0:
|
||||
case '/':
|
||||
case '?':
|
||||
case '#':
|
||||
case '[':
|
||||
case ']':
|
||||
*to = 0;
|
||||
*pp = from;
|
||||
return ch;
|
||||
|
||||
case '%': {
|
||||
unsigned int val;
|
||||
int chars;
|
||||
int res = sscanf(from, "%2x%n", &val, &chars);
|
||||
|
||||
if (res == EOF || res < 1 || chars != 2 || val > CHAR_MAX)
|
||||
/* Return a surprising delimiter to
|
||||
force an error. */
|
||||
{
|
||||
return '%';
|
||||
}
|
||||
|
||||
*to++ = (char)val;
|
||||
from += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
*to++ = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse an AMQP URL into its component parts. */
|
||||
int amqp_parse_url(char *url, struct amqp_connection_info *parsed) {
|
||||
int res = AMQP_STATUS_BAD_URL;
|
||||
char delim;
|
||||
char *start;
|
||||
char *host;
|
||||
char *port = NULL;
|
||||
|
||||
amqp_default_connection_info(parsed);
|
||||
|
||||
/* check the prefix */
|
||||
if (!strncmp(url, "amqp://", 7)) {
|
||||
/* do nothing */
|
||||
} else if (!strncmp(url, "amqps://", 8)) {
|
||||
parsed->port = 5671;
|
||||
parsed->ssl = 1;
|
||||
} else {
|
||||
goto out;
|
||||
}
|
||||
|
||||
host = start = url += (parsed->ssl ? 8 : 7);
|
||||
delim = find_delim(&url, 1);
|
||||
|
||||
if (delim == ':') {
|
||||
/* The colon could be introducing the port or the
|
||||
password part of the userinfo. We don't know yet,
|
||||
so stash the preceding component. */
|
||||
port = start = url;
|
||||
delim = find_delim(&url, 1);
|
||||
}
|
||||
|
||||
if (delim == '@') {
|
||||
/* What might have been the host and port were in fact
|
||||
the username and password */
|
||||
parsed->user = host;
|
||||
if (port) {
|
||||
parsed->password = port;
|
||||
}
|
||||
|
||||
port = NULL;
|
||||
host = start = url;
|
||||
delim = find_delim(&url, 1);
|
||||
}
|
||||
|
||||
if (delim == '[') {
|
||||
/* IPv6 address. The bracket should be the first
|
||||
character in the host. */
|
||||
if (host != start || *host != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
start = url;
|
||||
delim = find_delim(&url, 0);
|
||||
|
||||
if (delim != ']') {
|
||||
goto out;
|
||||
}
|
||||
|
||||
parsed->host = start;
|
||||
start = url;
|
||||
delim = find_delim(&url, 1);
|
||||
|
||||
/* Closing bracket should be the last character in the
|
||||
host. */
|
||||
if (*start != 0) {
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* If we haven't seen the host yet, this is it. */
|
||||
if (*host != 0) {
|
||||
parsed->host = host;
|
||||
}
|
||||
}
|
||||
|
||||
if (delim == ':') {
|
||||
port = url;
|
||||
delim = find_delim(&url, 1);
|
||||
}
|
||||
|
||||
if (port) {
|
||||
char *end;
|
||||
long portnum = strtol(port, &end, 10);
|
||||
|
||||
if (port == end || *end != 0 || portnum < 0 || portnum > 65535) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
parsed->port = portnum;
|
||||
}
|
||||
|
||||
if (delim == '/') {
|
||||
start = url;
|
||||
delim = find_delim(&url, 1);
|
||||
|
||||
if (delim != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
parsed->vhost = start;
|
||||
res = AMQP_STATUS_OK;
|
||||
} else if (delim == 0) {
|
||||
res = AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Any other delimiter is bad, and we will return AMQP_STATUS_BAD_AMQP_URL. */
|
||||
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
692
librabbitmq/codegen.py
Normal file
692
librabbitmq/codegen.py
Normal file
@@ -0,0 +1,692 @@
|
||||
# Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
# SPDX-License-Identifier: mit
|
||||
|
||||
|
||||
from amqp_codegen import *
|
||||
import string
|
||||
import re
|
||||
|
||||
|
||||
class Emitter(object):
|
||||
"""An object the trivially emits generated code lines.
|
||||
|
||||
This largely exists to be wrapped by more sophisticated emitter
|
||||
classes.
|
||||
"""
|
||||
|
||||
def __init__(self, prefix):
|
||||
self.prefix = prefix
|
||||
|
||||
def emit(self, line):
|
||||
"""Emit a line of generated code."""
|
||||
print(self.prefix + line)
|
||||
|
||||
|
||||
class BitDecoder(object):
|
||||
"""An emitter object that keeps track of the state involved in
|
||||
decoding the AMQP bit type."""
|
||||
|
||||
def __init__(self, emitter):
|
||||
self.emitter = emitter
|
||||
self.bit = 0
|
||||
|
||||
def emit(self, line):
|
||||
self.bit = 0
|
||||
self.emitter.emit(line)
|
||||
|
||||
def decode_bit(self, lvalue):
|
||||
"""Generate code to decode a value of the AMQP bit type into
|
||||
the given lvalue."""
|
||||
if self.bit == 0:
|
||||
self.emitter.emit("if (!amqp_decode_8(encoded, &offset, &bit_buffer)) return AMQP_STATUS_BAD_AMQP_DATA;")
|
||||
|
||||
self.emitter.emit("%s = (bit_buffer & (1 << %d)) ? 1 : 0;"
|
||||
% (lvalue, self.bit))
|
||||
self.bit += 1
|
||||
if self.bit == 8:
|
||||
self.bit = 0
|
||||
|
||||
|
||||
class BitEncoder(object):
|
||||
"""An emitter object that keeps track of the state involved in
|
||||
encoding the AMQP bit type."""
|
||||
|
||||
def __init__(self, emitter):
|
||||
self.emitter = emitter
|
||||
self.bit = 0
|
||||
|
||||
def flush(self):
|
||||
"""Flush the state associated with AMQP bit types."""
|
||||
if self.bit:
|
||||
self.emitter.emit("if (!amqp_encode_8(encoded, &offset, bit_buffer)) return AMQP_STATUS_BAD_AMQP_DATA;")
|
||||
self.bit = 0
|
||||
|
||||
def emit(self, line):
|
||||
self.flush()
|
||||
self.emitter.emit(line)
|
||||
|
||||
def encode_bit(self, value):
|
||||
"""Generate code to encode a value of the AMQP bit type from
|
||||
the given value."""
|
||||
if self.bit == 0:
|
||||
self.emitter.emit("bit_buffer = 0;")
|
||||
|
||||
self.emitter.emit("if (%s) bit_buffer |= (1 << %d);"
|
||||
% (value, self.bit))
|
||||
self.bit += 1
|
||||
if self.bit == 8:
|
||||
self.flush()
|
||||
|
||||
|
||||
class SimpleType(object):
|
||||
"""A AMQP type that corresponds to a simple scalar C value of a
|
||||
certain width."""
|
||||
|
||||
def __init__(self, bits):
|
||||
self.bits = bits
|
||||
self.ctype = "uint%d_t" % (bits,)
|
||||
|
||||
def decode(self, emitter, lvalue):
|
||||
emitter.emit("if (!amqp_decode_%d(encoded, &offset, &%s)) return AMQP_STATUS_BAD_AMQP_DATA;" % (self.bits, lvalue))
|
||||
|
||||
def encode(self, emitter, value):
|
||||
emitter.emit("if (!amqp_encode_%d(encoded, &offset, %s)) return AMQP_STATUS_BAD_AMQP_DATA;" % (self.bits, value))
|
||||
|
||||
def literal(self, value):
|
||||
return value
|
||||
|
||||
class StrType(object):
|
||||
"""The AMQP shortstr or longstr types."""
|
||||
|
||||
def __init__(self, lenbits):
|
||||
self.lenbits = lenbits
|
||||
self.ctype = "amqp_bytes_t"
|
||||
|
||||
def decode(self, emitter, lvalue):
|
||||
emitter.emit("{")
|
||||
emitter.emit(" uint%d_t len;" % (self.lenbits,))
|
||||
emitter.emit(" if (!amqp_decode_%d(encoded, &offset, &len)" % (self.lenbits,))
|
||||
emitter.emit(" || !amqp_decode_bytes(encoded, &offset, &%s, len))" % (lvalue,))
|
||||
emitter.emit(" return AMQP_STATUS_BAD_AMQP_DATA;")
|
||||
emitter.emit("}")
|
||||
|
||||
def encode(self, emitter, value):
|
||||
emitter.emit("if (UINT%d_MAX < %s.len" % (self.lenbits, value))
|
||||
emitter.emit(" || !amqp_encode_%d(encoded, &offset, (uint%d_t)%s.len)" %
|
||||
(self.lenbits, self.lenbits, value))
|
||||
emitter.emit(" || !amqp_encode_bytes(encoded, &offset, %s))" % (value,))
|
||||
emitter.emit(" return AMQP_STATUS_BAD_AMQP_DATA;")
|
||||
|
||||
def literal(self, value):
|
||||
if value != '':
|
||||
raise NotImplementedError()
|
||||
|
||||
return "amqp_empty_bytes"
|
||||
|
||||
class BitType(object):
|
||||
"""The AMQP bit type."""
|
||||
|
||||
def __init__(self):
|
||||
self.ctype = "amqp_boolean_t"
|
||||
|
||||
def decode(self, emitter, lvalue):
|
||||
emitter.decode_bit(lvalue)
|
||||
|
||||
def encode(self, emitter, value):
|
||||
emitter.encode_bit(value)
|
||||
|
||||
def literal(self, value):
|
||||
return {True: 1, False: 0}[value]
|
||||
|
||||
class TableType(object):
|
||||
"""The AMQP table type."""
|
||||
|
||||
def __init__(self):
|
||||
self.ctype = "amqp_table_t"
|
||||
|
||||
def decode(self, emitter, lvalue):
|
||||
emitter.emit("{")
|
||||
emitter.emit(" int res = amqp_decode_table(encoded, pool, &(%s), &offset);" % (lvalue,))
|
||||
emitter.emit(" if (res < 0) return res;")
|
||||
emitter.emit("}")
|
||||
|
||||
def encode(self, emitter, value):
|
||||
emitter.emit("{")
|
||||
emitter.emit(" int res = amqp_encode_table(encoded, &(%s), &offset);" % (value,))
|
||||
emitter.emit(" if (res < 0) return res;")
|
||||
emitter.emit("}")
|
||||
|
||||
def literal(self, value):
|
||||
raise NotImplementedError()
|
||||
|
||||
types = {
|
||||
'octet': SimpleType(8),
|
||||
'short': SimpleType(16),
|
||||
'long': SimpleType(32),
|
||||
'longlong': SimpleType(64),
|
||||
'shortstr': StrType(8),
|
||||
'longstr': StrType(32),
|
||||
'bit': BitType(),
|
||||
'table': TableType(),
|
||||
'timestamp': SimpleType(64),
|
||||
}
|
||||
|
||||
def typeFor(spec, f):
|
||||
"""Get a representation of the AMQP type of a field."""
|
||||
return types[spec.resolveDomain(f.domain)]
|
||||
|
||||
def c_ize(s):
|
||||
s = s.replace('-', '_')
|
||||
s = s.replace(' ', '_')
|
||||
return s
|
||||
|
||||
# When generating API functions corresponding to synchronous methods,
|
||||
# we need some information that isn't in the protocol def: Some
|
||||
# methods should not be exposed, indicated here by a False value.
|
||||
# Some methods should be exposed but certain fields should not be
|
||||
# exposed as parameters.
|
||||
apiMethodInfo = {
|
||||
"amqp_connection_start": False, # application code should not use this
|
||||
"amqp_connection_secure": False, # application code should not use this
|
||||
"amqp_connection_tune": False, # application code should not use this
|
||||
"amqp_connection_open": False, # application code should not use this
|
||||
"amqp_connection_close": False, # needs special handling
|
||||
"amqp_channel_open": ["out_of_band"],
|
||||
"amqp_channel_close": False, # needs special handling
|
||||
"amqp_access_request": False, # huh?
|
||||
"amqp_basic_get": False, # get-ok has content
|
||||
}
|
||||
|
||||
# When generating API functions corresponding to synchronous methods,
|
||||
# some fields should be suppressed everywhere. This dict names those
|
||||
# fields, and the fixed values to use for them.
|
||||
apiMethodsSuppressArgs = {"ticket": 0, "nowait": False}
|
||||
|
||||
AmqpMethod.defName = lambda m: cConstantName(c_ize(m.klass.name) + '_' + c_ize(m.name) + "_method")
|
||||
AmqpMethod.fullName = lambda m: "amqp_%s_%s" % (c_ize(m.klass.name), c_ize(m.name))
|
||||
AmqpMethod.structName = lambda m: m.fullName() + "_t"
|
||||
|
||||
AmqpClass.structName = lambda c: "amqp_" + c_ize(c.name) + "_properties_t"
|
||||
|
||||
def methodApiPrototype(m):
|
||||
fn = m.fullName()
|
||||
info = apiMethodInfo.get(fn, [])
|
||||
|
||||
docs = "/**\n * %s\n *\n" % (fn)
|
||||
docs += " * @param [in] state connection state\n"
|
||||
docs += " * @param [in] channel the channel to do the RPC on\n"
|
||||
|
||||
args = []
|
||||
for f in m.arguments:
|
||||
n = c_ize(f.name)
|
||||
if n in apiMethodsSuppressArgs or n in info:
|
||||
continue
|
||||
|
||||
args.append(", ")
|
||||
args.append(typeFor(m.klass.spec, f).ctype)
|
||||
args.append(" ")
|
||||
args.append(n)
|
||||
docs += " * @param [in] %s %s\n" % (n, n)
|
||||
|
||||
docs += " * @returns %s_ok_t\n" % (fn)
|
||||
docs += " */\n"
|
||||
|
||||
return "%sAMQP_EXPORT\n%s_ok_t *\nAMQP_CALL %s(amqp_connection_state_t state, amqp_channel_t channel%s)" % (docs, fn, fn, ''.join(args))
|
||||
|
||||
AmqpMethod.apiPrototype = methodApiPrototype
|
||||
|
||||
def cConstantName(s):
|
||||
return 'AMQP_' + '_'.join(re.split('[- ]', s.upper()))
|
||||
|
||||
def cFlagName(c, f):
|
||||
return cConstantName(c.name + '_' + f.name) + '_FLAG'
|
||||
|
||||
def genErl(spec):
|
||||
def fieldTempList(fields):
|
||||
return '[' + ', '.join(['F' + str(f.index) for f in fields]) + ']'
|
||||
|
||||
def fieldMapList(fields):
|
||||
return ', '.join([c_ize(f.name) + " = F" + str(f.index) for f in fields])
|
||||
|
||||
def genLookupMethodName(m):
|
||||
print(' case %s: return "%s";' % (m.defName(), m.defName()))
|
||||
|
||||
def genDecodeMethodFields(m):
|
||||
print(" case %s: {" % (m.defName(),))
|
||||
print(" %s *m = (%s *) amqp_pool_alloc(pool, sizeof(%s));" % \
|
||||
(m.structName(), m.structName(), m.structName()))
|
||||
print(" if (m == NULL) { return AMQP_STATUS_NO_MEMORY; }")
|
||||
|
||||
emitter = BitDecoder(Emitter(" "))
|
||||
for f in m.arguments:
|
||||
typeFor(spec, f).decode(emitter, "m->"+c_ize(f.name))
|
||||
|
||||
print(" *decoded = m;")
|
||||
print(" return 0;")
|
||||
print(" }")
|
||||
|
||||
def genDecodeProperties(c):
|
||||
print(" case %d: {" % (c.index,))
|
||||
print(" %s *p = (%s *) amqp_pool_alloc(pool, sizeof(%s));" % \
|
||||
(c.structName(), c.structName(), c.structName()))
|
||||
print(" if (p == NULL) { return AMQP_STATUS_NO_MEMORY; }")
|
||||
print(" p->_flags = flags;")
|
||||
|
||||
emitter = Emitter(" ")
|
||||
for f in c.fields:
|
||||
emitter.emit("if (flags & %s) {" % (cFlagName(c, f),))
|
||||
typeFor(spec, f).decode(emitter, "p->"+c_ize(f.name))
|
||||
emitter.emit("}")
|
||||
|
||||
print(" *decoded = p;")
|
||||
print(" return 0;")
|
||||
print(" }")
|
||||
|
||||
def genEncodeMethodFields(m):
|
||||
print(" case %s: {" % (m.defName(),))
|
||||
if m.arguments:
|
||||
print(" %s *m = (%s *) decoded;" % (m.structName(), m.structName()))
|
||||
|
||||
emitter = BitEncoder(Emitter(" "))
|
||||
for f in m.arguments:
|
||||
typeFor(spec, f).encode(emitter, "m->"+c_ize(f.name))
|
||||
emitter.flush()
|
||||
|
||||
print(" return (int)offset;")
|
||||
print(" }")
|
||||
|
||||
def genEncodeProperties(c):
|
||||
print(" case %d: {" % (c.index,))
|
||||
if c.fields:
|
||||
print(" %s *p = (%s *) decoded;" % (c.structName(), c.structName()))
|
||||
|
||||
emitter = Emitter(" ")
|
||||
for f in c.fields:
|
||||
emitter.emit(" if (flags & %s) {" % (cFlagName(c, f),))
|
||||
typeFor(spec, f).encode(emitter, "p->"+c_ize(f.name))
|
||||
emitter.emit("}")
|
||||
|
||||
print(" return (int)offset;")
|
||||
print(" }")
|
||||
|
||||
methods = spec.allMethods()
|
||||
|
||||
print("""// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
/* Generated code. Do not edit. Edit and re-run codegen.py instead. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
""")
|
||||
|
||||
print("""
|
||||
char const *amqp_constant_name(int constantNumber) {
|
||||
switch (constantNumber) {""")
|
||||
for (c,_,_) in spec.constants:
|
||||
print(" case %s: return \"%s\";" % (cConstantName(c), cConstantName(c)))
|
||||
print(""" default: return "(unknown)";
|
||||
}
|
||||
}""")
|
||||
|
||||
print("""
|
||||
amqp_boolean_t amqp_constant_is_hard_error(int constantNumber) {
|
||||
switch (constantNumber) {""")
|
||||
for (c,_,cls) in spec.constants:
|
||||
if cls == 'hard-error':
|
||||
print(" case %s: return 1;" % (cConstantName(c),))
|
||||
print(""" default: return 0;
|
||||
}
|
||||
}""")
|
||||
|
||||
print("""
|
||||
char const *amqp_method_name(amqp_method_number_t methodNumber) {
|
||||
switch (methodNumber) {""")
|
||||
for m in methods: genLookupMethodName(m)
|
||||
print(""" default: return NULL;
|
||||
}
|
||||
}""")
|
||||
|
||||
print("""
|
||||
amqp_boolean_t amqp_method_has_content(amqp_method_number_t methodNumber) {
|
||||
switch (methodNumber) {""")
|
||||
for m in methods:
|
||||
if m.hasContent:
|
||||
print(' case %s: return 1;' % (m.defName()))
|
||||
print(""" default: return 0;
|
||||
}
|
||||
}""")
|
||||
|
||||
print("""
|
||||
int amqp_decode_method(amqp_method_number_t methodNumber,
|
||||
amqp_pool_t *pool,
|
||||
amqp_bytes_t encoded,
|
||||
void **decoded)
|
||||
{
|
||||
size_t offset = 0;
|
||||
uint8_t bit_buffer;
|
||||
|
||||
switch (methodNumber) {""")
|
||||
for m in methods: genDecodeMethodFields(m)
|
||||
print(""" default: return AMQP_STATUS_UNKNOWN_METHOD;
|
||||
}
|
||||
}""")
|
||||
|
||||
print("""
|
||||
int amqp_decode_properties(uint16_t class_id,
|
||||
amqp_pool_t *pool,
|
||||
amqp_bytes_t encoded,
|
||||
void **decoded)
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
amqp_flags_t flags = 0;
|
||||
int flagword_index = 0;
|
||||
uint16_t partial_flags;
|
||||
|
||||
do {
|
||||
if (!amqp_decode_16(encoded, &offset, &partial_flags))
|
||||
return AMQP_STATUS_BAD_AMQP_DATA;
|
||||
flags |= (partial_flags << (flagword_index * 16));
|
||||
flagword_index++;
|
||||
} while (partial_flags & 1);
|
||||
|
||||
switch (class_id) {""")
|
||||
for c in spec.allClasses(): genDecodeProperties(c)
|
||||
print(""" default: return AMQP_STATUS_UNKNOWN_CLASS;
|
||||
}
|
||||
}""")
|
||||
|
||||
print("""
|
||||
int amqp_encode_method(amqp_method_number_t methodNumber,
|
||||
void *decoded,
|
||||
amqp_bytes_t encoded)
|
||||
{
|
||||
size_t offset = 0;
|
||||
uint8_t bit_buffer;
|
||||
|
||||
switch (methodNumber) {""")
|
||||
for m in methods: genEncodeMethodFields(m)
|
||||
print(""" default: return AMQP_STATUS_UNKNOWN_METHOD;
|
||||
}
|
||||
}""")
|
||||
|
||||
print("""
|
||||
int amqp_encode_properties(uint16_t class_id,
|
||||
void *decoded,
|
||||
amqp_bytes_t encoded)
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
/* Cheat, and get the flags out generically, relying on the
|
||||
similarity of structure between classes */
|
||||
amqp_flags_t flags = * (amqp_flags_t *) decoded; /* cheating! */
|
||||
|
||||
{
|
||||
/* We take a copy of flags to avoid destroying it, as it is used
|
||||
in the autogenerated code below. */
|
||||
amqp_flags_t remaining_flags = flags;
|
||||
do {
|
||||
amqp_flags_t remainder = remaining_flags >> 16;
|
||||
uint16_t partial_flags = remaining_flags & 0xFFFE;
|
||||
if (remainder != 0) { partial_flags |= 1; }
|
||||
if (!amqp_encode_16(encoded, &offset, partial_flags))
|
||||
return AMQP_STATUS_BAD_AMQP_DATA;
|
||||
remaining_flags = remainder;
|
||||
} while (remaining_flags != 0);
|
||||
}
|
||||
|
||||
switch (class_id) {""")
|
||||
for c in spec.allClasses(): genEncodeProperties(c)
|
||||
print(""" default: return AMQP_STATUS_UNKNOWN_CLASS;
|
||||
}
|
||||
}""")
|
||||
|
||||
for m in methods:
|
||||
if not m.isSynchronous:
|
||||
continue
|
||||
|
||||
info = apiMethodInfo.get(m.fullName(), [])
|
||||
if info is False:
|
||||
continue
|
||||
|
||||
print("")
|
||||
print(m.apiPrototype())
|
||||
print("{")
|
||||
print(" %s req;" % (m.structName(),))
|
||||
|
||||
for f in m.arguments:
|
||||
n = c_ize(f.name)
|
||||
|
||||
val = apiMethodsSuppressArgs.get(n)
|
||||
if val is None and n in info:
|
||||
val = f.defaultvalue
|
||||
|
||||
if val is None:
|
||||
val = n
|
||||
else:
|
||||
val = typeFor(spec, f).literal(val)
|
||||
|
||||
|
||||
print(" req.%s = %s;" % (n, val))
|
||||
|
||||
reply = cConstantName(c_ize(m.klass.name) + '_' + c_ize(m.name)
|
||||
+ "_ok_method")
|
||||
print("""
|
||||
return amqp_simple_rpc_decoded(state, channel, %s, %s, &req);
|
||||
}
|
||||
""" % (m.defName(), reply))
|
||||
|
||||
def genHrl(spec):
|
||||
def fieldDeclList(fields):
|
||||
if fields:
|
||||
return ''.join([" %s %s; /**< %s */\n" % (typeFor(spec, f).ctype,
|
||||
c_ize(f.name), f.name)
|
||||
for f in fields])
|
||||
else:
|
||||
return " char dummy; /**< Dummy field to avoid empty struct */\n"
|
||||
|
||||
def propDeclList(fields):
|
||||
return ''.join([" %s %s;\n" % (typeFor(spec, f).ctype, c_ize(f.name))
|
||||
for f in fields
|
||||
if spec.resolveDomain(f.domain) != 'bit'])
|
||||
|
||||
methods = spec.allMethods()
|
||||
|
||||
print("""// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
/* Generated code. Do not edit. Edit and re-run codegen.py instead. */
|
||||
|
||||
/** @file rabbitmq-c/framing.h */
|
||||
#ifndef RABBITMQ_C_FRAMING_H
|
||||
#define RABBITMQ_C_FRAMING_H
|
||||
|
||||
#include <rabbitmq-c/amqp.h>
|
||||
#include <rabbitmq-c/export.h>
|
||||
|
||||
AMQP_BEGIN_DECLS
|
||||
""")
|
||||
print("#define AMQP_PROTOCOL_VERSION_MAJOR %d /**< AMQP protocol version major */" % (spec.major))
|
||||
print("#define AMQP_PROTOCOL_VERSION_MINOR %d /**< AMQP protocol version minor */" % (spec.minor))
|
||||
print("#define AMQP_PROTOCOL_VERSION_REVISION %d /**< AMQP protocol version revision */" % (spec.revision))
|
||||
print("#define AMQP_PROTOCOL_PORT %d /**< Default AMQP Port */" % (spec.port))
|
||||
|
||||
for (c,v,cls) in spec.constants:
|
||||
print("#define %s %s /**< Constant: %s */" % (cConstantName(c), v, c))
|
||||
print("")
|
||||
|
||||
print("""/* Function prototypes. */
|
||||
|
||||
/**
|
||||
* Get constant name string from constant
|
||||
*
|
||||
* @param [in] constantNumber constant to get the name of
|
||||
* @returns string describing the constant. String is managed by
|
||||
* the library and should not be free()'d by the program
|
||||
*/
|
||||
AMQP_EXPORT
|
||||
char const *
|
||||
AMQP_CALL amqp_constant_name(int constantNumber);
|
||||
|
||||
/**
|
||||
* Checks to see if a constant is a hard error
|
||||
*
|
||||
* A hard error occurs when something severe enough
|
||||
* happens that the connection must be closed.
|
||||
*
|
||||
* @param [in] constantNumber the error constant
|
||||
* @returns true if its a hard error, false otherwise
|
||||
*/
|
||||
AMQP_EXPORT
|
||||
amqp_boolean_t
|
||||
AMQP_CALL amqp_constant_is_hard_error(int constantNumber);
|
||||
|
||||
/**
|
||||
* Get method name string from method number
|
||||
*
|
||||
* @param [in] methodNumber the method number
|
||||
* @returns method name string. String is managed by the library
|
||||
* and should not be freed()'d by the program
|
||||
*/
|
||||
AMQP_EXPORT
|
||||
char const *
|
||||
AMQP_CALL amqp_method_name(amqp_method_number_t methodNumber);
|
||||
|
||||
/**
|
||||
* Check whether a method has content
|
||||
*
|
||||
* A method that has content will receive the method frame
|
||||
* a properties frame, then 1 to N body frames
|
||||
*
|
||||
* @param [in] methodNumber the method number
|
||||
* @returns true if method has content, false otherwise
|
||||
*/
|
||||
AMQP_EXPORT
|
||||
amqp_boolean_t
|
||||
AMQP_CALL amqp_method_has_content(amqp_method_number_t methodNumber);
|
||||
|
||||
/**
|
||||
* Decodes a method from AMQP wireformat
|
||||
*
|
||||
* @param [in] methodNumber the method number for the decoded parameter
|
||||
* @param [in] pool the memory pool to allocate the decoded method from
|
||||
* @param [in] encoded the encoded byte string buffer
|
||||
* @param [out] decoded pointer to the decoded method struct
|
||||
* @returns 0 on success, an error code otherwise
|
||||
*/
|
||||
AMQP_EXPORT
|
||||
int
|
||||
AMQP_CALL amqp_decode_method(amqp_method_number_t methodNumber,
|
||||
amqp_pool_t *pool,
|
||||
amqp_bytes_t encoded,
|
||||
void **decoded);
|
||||
|
||||
/**
|
||||
* Decodes a header frame properties structure from AMQP wireformat
|
||||
*
|
||||
* @param [in] class_id the class id for the decoded parameter
|
||||
* @param [in] pool the memory pool to allocate the decoded properties from
|
||||
* @param [in] encoded the encoded byte string buffer
|
||||
* @param [out] decoded pointer to the decoded properties struct
|
||||
* @returns 0 on success, an error code otherwise
|
||||
*/
|
||||
AMQP_EXPORT
|
||||
int
|
||||
AMQP_CALL amqp_decode_properties(uint16_t class_id,
|
||||
amqp_pool_t *pool,
|
||||
amqp_bytes_t encoded,
|
||||
void **decoded);
|
||||
|
||||
/**
|
||||
* Encodes a method structure in AMQP wireformat
|
||||
*
|
||||
* @param [in] methodNumber the method number for the decoded parameter
|
||||
* @param [in] decoded the method structure (e.g., amqp_connection_start_t)
|
||||
* @param [in] encoded an allocated byte buffer for the encoded method
|
||||
* structure to be written to. If the buffer isn't large enough
|
||||
* to hold the encoded method, an error code will be returned.
|
||||
* @returns 0 on success, an error code otherwise.
|
||||
*/
|
||||
AMQP_EXPORT
|
||||
int
|
||||
AMQP_CALL amqp_encode_method(amqp_method_number_t methodNumber,
|
||||
void *decoded,
|
||||
amqp_bytes_t encoded);
|
||||
|
||||
/**
|
||||
* Encodes a properties structure in AMQP wireformat
|
||||
*
|
||||
* @param [in] class_id the class id for the decoded parameter
|
||||
* @param [in] decoded the properties structure (e.g., amqp_basic_properties_t)
|
||||
* @param [in] encoded an allocated byte buffer for the encoded properties to written to.
|
||||
* If the buffer isn't large enough to hold the encoded method, an
|
||||
* an error code will be returned
|
||||
* @returns 0 on success, an error code otherwise.
|
||||
*/
|
||||
AMQP_EXPORT
|
||||
int
|
||||
AMQP_CALL amqp_encode_properties(uint16_t class_id,
|
||||
void *decoded,
|
||||
amqp_bytes_t encoded);
|
||||
""")
|
||||
|
||||
print("/* Method field records. */\n")
|
||||
for m in methods:
|
||||
methodid = m.klass.index << 16 | m.index
|
||||
print("#define %s ((amqp_method_number_t) 0x%.08X) /**< %s.%s method id @internal %d, %d; %d */" % \
|
||||
(m.defName(),
|
||||
methodid,
|
||||
m.klass.name,
|
||||
m.name,
|
||||
m.klass.index,
|
||||
m.index,
|
||||
methodid))
|
||||
print("/** %s.%s method fields */\ntypedef struct %s_ {\n%s} %s;\n" % \
|
||||
(m.klass.name, m.name, m.structName(), fieldDeclList(m.arguments), m.structName()))
|
||||
|
||||
print("/* Class property records. */")
|
||||
for c in spec.allClasses():
|
||||
print("#define %s (0x%.04X) /**< %s class id @internal %d */" % \
|
||||
(cConstantName(c.name + "_class"), c.index, c.name, c.index))
|
||||
index = 0
|
||||
for f in c.fields:
|
||||
if index % 16 == 15:
|
||||
index = index + 1
|
||||
shortnum = index // 16
|
||||
partialindex = 15 - (index % 16)
|
||||
bitindex = shortnum * 16 + partialindex
|
||||
print('#define %s (1 << %d) /**< %s.%s property flag */' % (cFlagName(c, f), bitindex, c.name, f.name))
|
||||
index = index + 1
|
||||
print("/** %s class properties */\ntypedef struct %s_ {\n amqp_flags_t _flags; /**< bit-mask of set fields */\n%s} %s;\n" % \
|
||||
(c.name,
|
||||
c.structName(),
|
||||
fieldDeclList(c.fields),
|
||||
c.structName()))
|
||||
|
||||
print("/* API functions for methods */\n")
|
||||
|
||||
for m in methods:
|
||||
if m.isSynchronous and apiMethodInfo.get(m.fullName()) is not False:
|
||||
print("%s;" % (m.apiPrototype(),))
|
||||
|
||||
print("""
|
||||
AMQP_END_DECLS
|
||||
|
||||
#endif /* RABBITMQ_C_FRAMING_H */""")
|
||||
|
||||
def generateErl(specPath):
|
||||
genErl(AmqpSpec(specPath))
|
||||
|
||||
def generateHrl(specPath):
|
||||
genHrl(AmqpSpec(specPath))
|
||||
|
||||
if __name__ == "__main__":
|
||||
do_main(generateHrl, generateErl)
|
||||
9
librabbitmq/unix/threads.h
Normal file
9
librabbitmq/unix/threads.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifndef AMQP_THREADS_H
|
||||
#define AMQP_THREADS_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#endif /* AMQP_THREADS_H */
|
||||
37
librabbitmq/win32/threads.c
Normal file
37
librabbitmq/win32/threads.c
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#include "threads.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
DWORD pthread_self(void) { return GetCurrentThreadId(); }
|
||||
|
||||
int pthread_mutex_init(pthread_mutex_t *mutex, void *attr) {
|
||||
if (!mutex) {
|
||||
return 1;
|
||||
}
|
||||
InitializeSRWLock(mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||
if (!mutex) {
|
||||
return 1;
|
||||
}
|
||||
AcquireSRWLockExclusive(mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||
if (!mutex) {
|
||||
return 1;
|
||||
}
|
||||
ReleaseSRWLockExclusive(mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutex_destroy(pthread_mutex_t *mutex) {
|
||||
/* SRW's do not require destruction. */
|
||||
return 0;
|
||||
}
|
||||
29
librabbitmq/win32/threads.h
Normal file
29
librabbitmq/win32/threads.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
|
||||
// SPDX-License-Identifier: mit
|
||||
|
||||
#ifndef AMQP_THREAD_H
|
||||
#define AMQP_THREAD_H
|
||||
|
||||
#if !defined(WINVER) || defined(__MINGW32__) || defined(__MINGW64__)
|
||||
#ifdef WINVER
|
||||
#undef WINVER
|
||||
#endif
|
||||
/* Windows Vista or newer */
|
||||
#define WINVER 0x0600
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
typedef SRWLOCK pthread_mutex_t;
|
||||
#define PTHREAD_MUTEX_INITIALIZER SRWLOCK_INIT;
|
||||
|
||||
DWORD pthread_self(void);
|
||||
|
||||
int pthread_mutex_init(pthread_mutex_t *, void *attr);
|
||||
int pthread_mutex_lock(pthread_mutex_t *);
|
||||
int pthread_mutex_unlock(pthread_mutex_t *);
|
||||
int pthread_mutex_destroy(pthread_mutex_t *);
|
||||
|
||||
#endif /* AMQP_THREAD_H */
|
||||
Reference in New Issue
Block a user