mirror of
https://github.com/ZeroTier/ZeroTierOne
synced 2025-08-20 13:24:09 -07:00
Beginning CMake configuration for ZT
Only tested on Windows so far
This commit is contained in:
parent
af5d3a7f0b
commit
0b3b5f6174
111 changed files with 19586 additions and 36 deletions
186
ext/librabbitmq/librabbitmq/CMakeLists.txt
Normal file
186
ext/librabbitmq/librabbitmq/CMakeLists.txt
Normal file
|
@ -0,0 +1,186 @@
|
|||
project(librabbitmq "C")
|
||||
|
||||
if (REGENERATE_AMQP_FRAMING)
|
||||
set(AMQP_CODEGEN_PY "${CMAKE_CURRENT_BINARY_DIR}/amqp_codegen.py")
|
||||
set(CODEGEN_PY "${CMAKE_CURRENT_BINARY_DIR}/codegen.py")
|
||||
set(AMQP_SPEC_JSON_PATH "${AMQP_CODEGEN_DIR}/amqp-rabbitmq-0.9.1.json")
|
||||
set(AMQP_FRAMING_H_PATH ${CMAKE_CURRENT_BINARY_DIR}/amqp_framing.h)
|
||||
set(AMQP_FRAMING_C_PATH ${CMAKE_CURRENT_BINARY_DIR}/amqp_framing.c)
|
||||
|
||||
if (PYTHON_VERSION_MAJOR GREATER 2)
|
||||
set(CONVERT_CODEGEN ${PYTHON_2TO3_EXECUTABLE} -w ${CODEGEN_PY} > codegen_2to3.out)
|
||||
set(CONVERT_AMQP_CODEGEN ${PYTHON_2TO3_EXECUTABLE} -w ${AMQP_CODEGEN_PY} > amqp_codegen_2to3.out)
|
||||
else ()
|
||||
set(CONVERT_CODEGEN "")
|
||||
set(CONVERT_AMQP_CODEGEN "")
|
||||
endif ()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CODEGEN_PY}
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy ${CMAKE_CURRENT_SOURCE_DIR}/codegen.py ${CODEGEN_PY}
|
||||
COMMAND ${CONVERT_CODEGEN}
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/codegen.py
|
||||
VERBATIM)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${AMQP_CODEGEN_PY}
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy ${AMQP_CODEGEN_DIR}/amqp_codegen.py ${AMQP_CODEGEN_PY}
|
||||
COMMAND ${CONVERT_AMQP_CODEGEN}
|
||||
DEPENDS ${AMQP_CODEGEN_DIR}/amqp_codegen.py ${AMQP_CODEGEN_TARGET}
|
||||
VERBATIM)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${AMQP_FRAMING_H_PATH}
|
||||
COMMAND ${PYTHON_EXECUTABLE} ARGS ${CODEGEN_PY} header ${AMQP_SPEC_JSON_PATH} ${AMQP_FRAMING_H_PATH}
|
||||
DEPENDS ${AMQP_SPEC_JSON_PATH} ${CODEGEN_PY} ${AMQP_CODEGEN_PY}
|
||||
VERBATIM)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${AMQP_FRAMING_C_PATH}
|
||||
COMMAND ${PYTHON_EXECUTABLE} ARGS ${CODEGEN_PY} body ${AMQP_SPEC_JSON_PATH} ${AMQP_FRAMING_C_PATH}
|
||||
DEPENDS ${AMQP_SPEC_JSON_PATH} ${CODEGEN_PY} ${AMQP_CODEGEN_PY}
|
||||
VERBATIM)
|
||||
else (REGENERATE_AMQP_FRAMING)
|
||||
set(AMQP_FRAMING_H_PATH ${CMAKE_CURRENT_SOURCE_DIR}/amqp_framing.h)
|
||||
set(AMQP_FRAMING_C_PATH ${CMAKE_CURRENT_SOURCE_DIR}/amqp_framing.c)
|
||||
endif (REGENERATE_AMQP_FRAMING)
|
||||
|
||||
if(WIN32)
|
||||
set(SOCKET_IMPL "win32")
|
||||
else(WIN32)
|
||||
set(SOCKET_IMPL "unix")
|
||||
endif(WIN32)
|
||||
|
||||
if(MSVC)
|
||||
if(MSVC_VERSION LESS 1600)
|
||||
set(MSINTTYPES_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/win32/msinttypes")
|
||||
set(STDINT_H_INSTALL_FILE "${CMAKE_CURRENT_SOURCE_DIR}/win32/msinttypes/stdint.h")
|
||||
endif(MSVC_VERSION LESS 1600)
|
||||
endif(MSVC)
|
||||
|
||||
# NOTE: order is important here: if we generate amqp_framing.h/.c it'll be in the
|
||||
# binary directory, and should shadow whats in the source directory
|
||||
set(LIBRABBITMQ_INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${SOCKET_IMPL}
|
||||
${MSINTTYPES_INCLUDE}
|
||||
)
|
||||
|
||||
include_directories(${LIBRABBITMQ_INCLUDE_DIRS})
|
||||
|
||||
set(LIBRABBITMQ_INCLUDE_DIRS
|
||||
${LIBRABBITMQ_INCLUDE_DIRS}
|
||||
PARENT_SCOPE)
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
|
||||
if (ENABLE_SSL_SUPPORT)
|
||||
add_definitions(-DWITH_SSL=1)
|
||||
set(AMQP_SSL_SOCKET_H_PATH amqp_ssl_socket.h)
|
||||
|
||||
set(AMQP_SSL_SRCS ${AMQP_SSL_SOCKET_H_PATH}
|
||||
amqp_openssl.c
|
||||
amqp_openssl_hostname_validation.c
|
||||
amqp_openssl_hostname_validation.h
|
||||
amqp_hostcheck.c
|
||||
amqp_hostcheck.h
|
||||
amqp_openssl_bio.c
|
||||
amqp_openssl_bio.h
|
||||
)
|
||||
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||
set(AMQP_SSL_LIBS ${OPENSSL_LIBRARIES})
|
||||
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)
|
||||
set(AMQP_SSL_SRCS ${AMQP_SSL_SRCS} win32/threads.h win32/threads.c)
|
||||
else()
|
||||
set(AMQP_SSL_SRCS ${AMQP_SSL_SRCS} unix/threads.h)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(RABBITMQ_SOURCES
|
||||
${AMQP_FRAMING_H_PATH}
|
||||
${AMQP_FRAMING_C_PATH}
|
||||
amqp_api.c amqp.h amqp_connection.c amqp_mem.c amqp_private.h amqp_socket.c
|
||||
amqp_table.c amqp_url.c amqp_socket.h amqp_tcp_socket.c amqp_tcp_socket.h
|
||||
amqp_time.c amqp_time.h
|
||||
amqp_consumer.c
|
||||
${AMQP_SSL_SRCS}
|
||||
)
|
||||
|
||||
add_definitions(-DAMQP_BUILD)
|
||||
|
||||
set(RMQ_LIBRARIES ${AMQP_SSL_LIBS} ${SOCKET_LIBRARIES} ${LIBRT} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
add_library(rabbitmq SHARED ${RABBITMQ_SOURCES})
|
||||
if (THREADS_HAVE_PTHREAD_ARG)
|
||||
target_compile_options(rabbitmq PUBLIC "-pthread")
|
||||
endif()
|
||||
|
||||
target_link_libraries(rabbitmq ${RMQ_LIBRARIES})
|
||||
|
||||
if (WIN32)
|
||||
set_target_properties(rabbitmq PROPERTIES VERSION ${RMQ_VERSION} OUTPUT_NAME rabbitmq.${RMQ_SOVERSION})
|
||||
else (WIN32)
|
||||
set_target_properties(rabbitmq PROPERTIES VERSION ${RMQ_VERSION} SOVERSION ${RMQ_SOVERSION})
|
||||
endif (WIN32)
|
||||
|
||||
install(TARGETS rabbitmq
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
set(RMQ_LIBRARY_TARGET rabbitmq)
|
||||
endif (BUILD_SHARED_LIBS)
|
||||
|
||||
if (BUILD_STATIC_LIBS)
|
||||
add_library(rabbitmq-static STATIC ${RABBITMQ_SOURCES})
|
||||
if (THREADS_HAVE_PTHREAD_ARG)
|
||||
target_compile_options(rabbitmq-static PUBLIC "-pthread")
|
||||
endif()
|
||||
|
||||
target_link_libraries(rabbitmq-static ${RMQ_LIBRARIES})
|
||||
|
||||
set_target_properties(rabbitmq-static PROPERTIES COMPILE_DEFINITIONS AMQP_STATIC)
|
||||
if (WIN32)
|
||||
set_target_properties(rabbitmq-static PROPERTIES
|
||||
VERSION ${RMQ_VERSION}
|
||||
OUTPUT_NAME librabbitmq.${RMQ_SOVERSION})
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties(rabbitmq-static PROPERTIES
|
||||
# Embed debugging info in the library itself instead of generating
|
||||
# a .pdb file.
|
||||
COMPILE_OPTIONS "/Z7")
|
||||
endif(MSVC)
|
||||
|
||||
else (WIN32)
|
||||
set_target_properties(rabbitmq-static PROPERTIES VERSION ${RMQ_VERSION} SOVERSION ${RMQ_SOVERSION} OUTPUT_NAME rabbitmq)
|
||||
endif (WIN32)
|
||||
|
||||
install(TARGETS rabbitmq-static
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
if (NOT DEFINED RMQ_LIBRARY_TARGET)
|
||||
set(RMQ_LIBRARY_TARGET rabbitmq-static)
|
||||
endif ()
|
||||
endif (BUILD_STATIC_LIBS)
|
||||
|
||||
install(FILES
|
||||
amqp.h
|
||||
${AMQP_FRAMING_H_PATH}
|
||||
amqp_tcp_socket.h
|
||||
${AMQP_SSL_SOCKET_H_PATH}
|
||||
${STDINT_H_INSTALL_FILE}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
set(RMQ_LIBRARY_TARGET ${RMQ_LIBRARY_TARGET} PARENT_SCOPE)
|
2535
ext/librabbitmq/librabbitmq/amqp.h
Normal file
2535
ext/librabbitmq/librabbitmq/amqp.h
Normal file
File diff suppressed because it is too large
Load diff
394
ext/librabbitmq/librabbitmq/amqp_api.c
Normal file
394
ext/librabbitmq/librabbitmq/amqp_api.c
Normal file
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MIT
|
||||
*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2012-2013
|
||||
* Alan Antonuk. All Rights Reserved.
|
||||
*
|
||||
* Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
|
||||
* VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#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_ERRO R -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"};
|
||||
|
||||
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,
|
||||
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,
|
||||
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;
|
||||
}
|
595
ext/librabbitmq/librabbitmq/amqp_connection.c
Normal file
595
ext/librabbitmq/librabbitmq/amqp_connection.c
Normal file
|
@ -0,0 +1,595 @@
|
|||
/*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MIT
|
||||
*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2012-2014
|
||||
* Alan Antonuk. All Rights Reserved.
|
||||
*
|
||||
* Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
|
||||
* VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include "amqp_tcp_socket.h"
|
||||
#include "amqp_time.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;
|
||||
/* frame length is 3 bytes in */
|
||||
channel = amqp_d16(amqp_offset(raw_frame, 1));
|
||||
|
||||
state->target_size =
|
||||
amqp_d32(amqp_offset(raw_frame, 3)) + HEADER_SIZE + FOOTER_SIZE;
|
||||
|
||||
if ((size_t)state->frame_max < state->target_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, state->target_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;
|
||||
|
||||
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;
|
||||
}
|
307
ext/librabbitmq/librabbitmq/amqp_consumer.c
Normal file
307
ext/librabbitmq/librabbitmq/amqp_consumer.c
Normal file
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MIT
|
||||
*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2013-2014
|
||||
* Alan Antonuk. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
#include "amqp.h"
|
||||
#include "amqp_private.h"
|
||||
#include "amqp_socket.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,
|
||||
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;
|
||||
}
|
2876
ext/librabbitmq/librabbitmq/amqp_framing.c
Normal file
2876
ext/librabbitmq/librabbitmq/amqp_framing.c
Normal file
File diff suppressed because it is too large
Load diff
1144
ext/librabbitmq/librabbitmq/amqp_framing.h
Normal file
1144
ext/librabbitmq/librabbitmq/amqp_framing.h
Normal file
File diff suppressed because it is too large
Load diff
195
ext/librabbitmq/librabbitmq/amqp_hostcheck.c
Normal file
195
ext/librabbitmq/librabbitmq/amqp_hostcheck.c
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright 1996-2014 Daniel Stenberg <daniel@haxx.se>.
|
||||
* Copyright 2014 Michael Steinert
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Except as contained in this notice, the name of a copyright holder shall
|
||||
* not be used in advertising or otherwise to promote the sale, use or other
|
||||
* dealings in this Software without prior written authorization of the
|
||||
* copyright holder.
|
||||
*/
|
||||
|
||||
#include "amqp_hostcheck.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Portable, consistent toupper (remember EBCDIC). Do not use toupper()
|
||||
* because its behavior is altered by the current locale.
|
||||
*/
|
||||
|
||||
static char amqp_raw_toupper(char in) {
|
||||
switch (in) {
|
||||
case 'a':
|
||||
return 'A';
|
||||
case 'b':
|
||||
return 'B';
|
||||
case 'c':
|
||||
return 'C';
|
||||
case 'd':
|
||||
return 'D';
|
||||
case 'e':
|
||||
return 'E';
|
||||
case 'f':
|
||||
return 'F';
|
||||
case 'g':
|
||||
return 'G';
|
||||
case 'h':
|
||||
return 'H';
|
||||
case 'i':
|
||||
return 'I';
|
||||
case 'j':
|
||||
return 'J';
|
||||
case 'k':
|
||||
return 'K';
|
||||
case 'l':
|
||||
return 'L';
|
||||
case 'm':
|
||||
return 'M';
|
||||
case 'n':
|
||||
return 'N';
|
||||
case 'o':
|
||||
return 'O';
|
||||
case 'p':
|
||||
return 'P';
|
||||
case 'q':
|
||||
return 'Q';
|
||||
case 'r':
|
||||
return 'R';
|
||||
case 's':
|
||||
return 'S';
|
||||
case 't':
|
||||
return 'T';
|
||||
case 'u':
|
||||
return 'U';
|
||||
case 'v':
|
||||
return 'V';
|
||||
case 'w':
|
||||
return 'W';
|
||||
case 'x':
|
||||
return 'X';
|
||||
case 'y':
|
||||
return 'Y';
|
||||
case 'z':
|
||||
return 'Z';
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
/*
|
||||
* amqp_raw_equal() is for doing "raw" case insensitive strings. This is meant
|
||||
* to be locale independent and only compare strings we know are safe for
|
||||
* this. See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
|
||||
* some further explanation to why this function is necessary.
|
||||
*
|
||||
* The function is capable of comparing a-z case insensitively even for
|
||||
* non-ascii.
|
||||
*/
|
||||
|
||||
static int amqp_raw_equal(const char *first, const char *second) {
|
||||
while (*first && *second) {
|
||||
if (amqp_raw_toupper(*first) != amqp_raw_toupper(*second)) {
|
||||
/* get out of the loop as soon as they don't match */
|
||||
break;
|
||||
}
|
||||
first++;
|
||||
second++;
|
||||
}
|
||||
/* we do the comparison here (possibly again), just to make sure that if
|
||||
* the loop above is skipped because one of the strings reached zero, we
|
||||
* must not return this as a successful match
|
||||
*/
|
||||
return (amqp_raw_toupper(*first) == amqp_raw_toupper(*second));
|
||||
}
|
||||
|
||||
static int amqp_raw_nequal(const char *first, const char *second, size_t max) {
|
||||
while (*first && *second && max) {
|
||||
if (amqp_raw_toupper(*first) != amqp_raw_toupper(*second)) {
|
||||
break;
|
||||
}
|
||||
max--;
|
||||
first++;
|
||||
second++;
|
||||
}
|
||||
if (0 == max) {
|
||||
return 1; /* they are equal this far */
|
||||
}
|
||||
return amqp_raw_toupper(*first) == amqp_raw_toupper(*second);
|
||||
}
|
||||
|
||||
/*
|
||||
* Match a hostname against a wildcard pattern.
|
||||
* E.g.
|
||||
* "foo.host.com" matches "*.host.com".
|
||||
*
|
||||
* We use the matching rule described in RFC6125, section 6.4.3.
|
||||
* http://tools.ietf.org/html/rfc6125#section-6.4.3
|
||||
*/
|
||||
|
||||
static amqp_hostcheck_result amqp_hostmatch(const char *hostname,
|
||||
const char *pattern) {
|
||||
const char *pattern_label_end, *pattern_wildcard, *hostname_label_end;
|
||||
int wildcard_enabled;
|
||||
size_t prefixlen, suffixlen;
|
||||
pattern_wildcard = strchr(pattern, '*');
|
||||
if (pattern_wildcard == NULL) {
|
||||
return amqp_raw_equal(pattern, hostname) ? AMQP_HCR_MATCH
|
||||
: AMQP_HCR_NO_MATCH;
|
||||
}
|
||||
/* We require at least 2 dots in pattern to avoid too wide wildcard match. */
|
||||
wildcard_enabled = 1;
|
||||
pattern_label_end = strchr(pattern, '.');
|
||||
if (pattern_label_end == NULL || strchr(pattern_label_end + 1, '.') == NULL ||
|
||||
pattern_wildcard > pattern_label_end ||
|
||||
amqp_raw_nequal(pattern, "xn--", 4)) {
|
||||
wildcard_enabled = 0;
|
||||
}
|
||||
if (!wildcard_enabled) {
|
||||
return amqp_raw_equal(pattern, hostname) ? AMQP_HCR_MATCH
|
||||
: AMQP_HCR_NO_MATCH;
|
||||
}
|
||||
hostname_label_end = strchr(hostname, '.');
|
||||
if (hostname_label_end == NULL ||
|
||||
!amqp_raw_equal(pattern_label_end, hostname_label_end)) {
|
||||
return AMQP_HCR_NO_MATCH;
|
||||
}
|
||||
/* The wildcard must match at least one character, so the left-most
|
||||
* label of the hostname is at least as large as the left-most label
|
||||
* of the pattern.
|
||||
*/
|
||||
if (hostname_label_end - hostname < pattern_label_end - pattern) {
|
||||
return AMQP_HCR_NO_MATCH;
|
||||
}
|
||||
prefixlen = pattern_wildcard - pattern;
|
||||
suffixlen = pattern_label_end - (pattern_wildcard + 1);
|
||||
return amqp_raw_nequal(pattern, hostname, prefixlen) &&
|
||||
amqp_raw_nequal(pattern_wildcard + 1,
|
||||
hostname_label_end - suffixlen, suffixlen)
|
||||
? AMQP_HCR_MATCH
|
||||
: AMQP_HCR_NO_MATCH;
|
||||
}
|
||||
|
||||
amqp_hostcheck_result amqp_hostcheck(const char *match_pattern,
|
||||
const char *hostname) {
|
||||
/* sanity check */
|
||||
if (!match_pattern || !*match_pattern || !hostname || !*hostname) {
|
||||
return AMQP_HCR_NO_MATCH;
|
||||
}
|
||||
/* trivial case */
|
||||
if (amqp_raw_equal(hostname, match_pattern)) {
|
||||
return AMQP_HCR_MATCH;
|
||||
}
|
||||
return amqp_hostmatch(hostname, match_pattern);
|
||||
}
|
48
ext/librabbitmq/librabbitmq/amqp_hostcheck.h
Normal file
48
ext/librabbitmq/librabbitmq/amqp_hostcheck.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
#ifndef librabbitmq_amqp_hostcheck_h
|
||||
#define librabbitmq_amqp_hostcheck_h
|
||||
|
||||
/*
|
||||
* Copyright 1996-2014 Daniel Stenberg <daniel@haxx.se>.
|
||||
* Copyright 2014 Michael Steinert
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Except as contained in this notice, the name of a copyright holder shall
|
||||
* not be used in advertising or otherwise to promote the sale, use or other
|
||||
* dealings in this Software without prior written authorization of the
|
||||
* copyright holder.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
AMQP_HCR_NO_MATCH = 0,
|
||||
AMQP_HCR_MATCH = 1
|
||||
} amqp_hostcheck_result;
|
||||
|
||||
/**
|
||||
* Determine whether hostname matches match_pattern.
|
||||
*
|
||||
* match_pattern may include wildcards.
|
||||
*
|
||||
* Match is performed based on the rules set forth in RFC6125 section 6.4.3.
|
||||
* http://tools.ietf.org/html/rfc6125#section-6.4.3
|
||||
*
|
||||
* \param match_pattern RFC6125 compliant pattern
|
||||
* \param hostname to match against
|
||||
* \returns AMQP_HCR_MATCH if its a match, AMQP_HCR_NO_MATCH otherwise.
|
||||
*/
|
||||
amqp_hostcheck_result amqp_hostcheck(const char *match_pattern,
|
||||
const char *hostname);
|
||||
|
||||
#endif
|
242
ext/librabbitmq/librabbitmq/amqp_mem.c
Normal file
242
ext/librabbitmq/librabbitmq/amqp_mem.c
Normal file
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MIT
|
||||
*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2012-2013
|
||||
* Alan Antonuk. All Rights Reserved.
|
||||
*
|
||||
* Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
|
||||
* VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
704
ext/librabbitmq/librabbitmq/amqp_openssl.c
Normal file
704
ext/librabbitmq/librabbitmq/amqp_openssl.c
Normal file
|
@ -0,0 +1,704 @@
|
|||
/*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2012-2014 Alan Antonuk.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Michael Steinert are Copyright (c) 2012-2014 Michael
|
||||
* Steinert. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "amqp_openssl_bio.h"
|
||||
#include "amqp_openssl_hostname_validation.h"
|
||||
#include "amqp_private.h"
|
||||
#include "amqp_socket.h"
|
||||
#include "amqp_ssl_socket.h"
|
||||
#include "amqp_time.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/engine.h>
|
||||
#include <openssl/err.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 unsigned long ssl_threadid_callback(void);
|
||||
static void ssl_locking_callback(int mode, int n, const char *file, int line);
|
||||
static pthread_mutex_t *amqp_openssl_lockarray = NULL;
|
||||
|
||||
static pthread_mutex_t openssl_init_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static amqp_boolean_t do_initialize_openssl = 1;
|
||||
static amqp_boolean_t openssl_initialized = 0;
|
||||
static amqp_boolean_t openssl_bio_initialized = 0;
|
||||
static int openssl_connections = 0;
|
||||
|
||||
#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,
|
||||
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;
|
||||
}
|
||||
|
||||
cert = SSL_get_peer_certificate(self->ssl);
|
||||
|
||||
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 (AMQP_HVR_MATCH_FOUND != amqp_ssl_validate_hostname(host, cert)) {
|
||||
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(SSLv23_client_method());
|
||||
if (!self->ctx) {
|
||||
goto error;
|
||||
}
|
||||
/* Disable SSLv2 and SSLv3 */
|
||||
SSL_CTX_set_options(self->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
|
||||
amqp_set_socket(state, (amqp_socket_t *)self);
|
||||
|
||||
return (amqp_socket_t *)self;
|
||||
error:
|
||||
amqp_ssl_socket_delete((amqp_socket_t *)self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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_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;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
{
|
||||
long clear_options;
|
||||
long set_options = 0;
|
||||
#if defined(SSL_OP_NO_TLSv1_2)
|
||||
amqp_tls_version_t max_supported = AMQP_TLSv1_2;
|
||||
clear_options = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
|
||||
#elif defined(SSL_OP_NO_TLSv1_1)
|
||||
amqp_tls_version_t max_supported = AMQP_TLSv1_1;
|
||||
clear_options = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
|
||||
#elif defined(SSL_OP_NO_TLSv1)
|
||||
amqp_tls_version_t max_supported = AMQP_TLSv1;
|
||||
clear_options = SSL_OP_NO_TLSv1;
|
||||
#else
|
||||
#error "Need a version of OpenSSL that can support TLSv1 or greater."
|
||||
#endif
|
||||
|
||||
if (AMQP_TLSvLATEST == max) {
|
||||
max = max_supported;
|
||||
}
|
||||
if (AMQP_TLSvLATEST == min) {
|
||||
min = max_supported;
|
||||
}
|
||||
|
||||
if (min > max) {
|
||||
return AMQP_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (max > max_supported || min > max_supported) {
|
||||
return AMQP_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (min > AMQP_TLSv1) {
|
||||
set_options |= SSL_OP_NO_TLSv1;
|
||||
}
|
||||
#ifdef SSL_OP_NO_TLSv1_1
|
||||
if (min > AMQP_TLSv1_1 || max < AMQP_TLSv1_1) {
|
||||
set_options |= SSL_OP_NO_TLSv1_1;
|
||||
}
|
||||
#endif
|
||||
#ifdef SSL_OP_NO_TLSv1_2
|
||||
if (max < AMQP_TLSv1_2) {
|
||||
set_options |= SSL_OP_NO_TLSv1_2;
|
||||
}
|
||||
#endif
|
||||
SSL_CTX_clear_options(self->ctx, clear_options);
|
||||
SSL_CTX_set_options(self->ctx, set_options);
|
||||
}
|
||||
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
void amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize) {
|
||||
CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
|
||||
|
||||
if (openssl_connections == 0 && !openssl_initialized) {
|
||||
do_initialize_openssl = do_initialize;
|
||||
}
|
||||
CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
|
||||
}
|
||||
|
||||
static unsigned long ssl_threadid_callback(void) {
|
||||
return (unsigned long)pthread_self();
|
||||
}
|
||||
|
||||
static void ssl_locking_callback(int mode, int n, AMQP_UNUSED const char *file,
|
||||
AMQP_UNUSED int line) {
|
||||
if (mode & CRYPTO_LOCK) {
|
||||
CHECK_SUCCESS(pthread_mutex_lock(&amqp_openssl_lockarray[n]));
|
||||
} else {
|
||||
CHECK_SUCCESS(pthread_mutex_unlock(&amqp_openssl_lockarray[n]));
|
||||
}
|
||||
}
|
||||
|
||||
static int setup_openssl(void) {
|
||||
int status;
|
||||
|
||||
int i;
|
||||
amqp_openssl_lockarray = calloc(CRYPTO_num_locks(), sizeof(pthread_mutex_t));
|
||||
if (!amqp_openssl_lockarray) {
|
||||
status = AMQP_STATUS_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < CRYPTO_num_locks(); i++) {
|
||||
if (pthread_mutex_init(&amqp_openssl_lockarray[i], NULL)) {
|
||||
int j;
|
||||
for (j = 0; j < i; j++) {
|
||||
pthread_mutex_destroy(&amqp_openssl_lockarray[j]);
|
||||
}
|
||||
free(amqp_openssl_lockarray);
|
||||
status = AMQP_STATUS_SSL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
CRYPTO_set_id_callback(ssl_threadid_callback);
|
||||
CRYPTO_set_locking_callback(ssl_locking_callback);
|
||||
|
||||
#ifdef AMQP_OPENSSL_V110
|
||||
if (CONF_modules_load_file(NULL, "rabbitmq-c", CONF_MFLAGS_DEFAULT_SECTION) <=
|
||||
0) {
|
||||
status = AMQP_STATUS_SSL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
#else
|
||||
OPENSSL_config(NULL);
|
||||
#endif
|
||||
SSL_library_init();
|
||||
SSL_load_error_strings();
|
||||
|
||||
status = AMQP_STATUS_OK;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
int amqp_initialize_ssl_library(void) {
|
||||
int status;
|
||||
CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
|
||||
|
||||
if (!openssl_initialized) {
|
||||
status = setup_openssl();
|
||||
if (status) {
|
||||
goto out;
|
||||
}
|
||||
openssl_initialized = 1;
|
||||
}
|
||||
|
||||
status = AMQP_STATUS_OK;
|
||||
out:
|
||||
CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
|
||||
return status;
|
||||
}
|
||||
|
||||
static int initialize_ssl_and_increment_connections() {
|
||||
int status;
|
||||
CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
|
||||
|
||||
if (do_initialize_openssl && !openssl_initialized) {
|
||||
status = setup_openssl();
|
||||
if (status) {
|
||||
goto exit;
|
||||
}
|
||||
openssl_initialized = 1;
|
||||
}
|
||||
|
||||
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--;
|
||||
}
|
||||
|
||||
CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
|
||||
return AMQP_STATUS_OK;
|
||||
}
|
||||
|
||||
int amqp_uninitialize_ssl_library(void) {
|
||||
int status;
|
||||
CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
|
||||
|
||||
if (openssl_connections > 0) {
|
||||
status = AMQP_STATUS_SOCKET_INUSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
amqp_openssl_bio_destroy();
|
||||
openssl_bio_initialized = 0;
|
||||
|
||||
#ifndef AMQP_OPENSSL_V110
|
||||
ERR_remove_state(0);
|
||||
#endif
|
||||
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
FIPS_mode_set(0);
|
||||
#endif
|
||||
|
||||
CRYPTO_set_locking_callback(NULL);
|
||||
CRYPTO_set_id_callback(NULL);
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < CRYPTO_num_locks(); i++) {
|
||||
pthread_mutex_destroy(&amqp_openssl_lockarray[i]);
|
||||
}
|
||||
free(amqp_openssl_lockarray);
|
||||
}
|
||||
|
||||
ENGINE_cleanup();
|
||||
CONF_modules_free();
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
ERR_free_strings();
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x10002003L) && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
SSL_COMP_free_compression_methods();
|
||||
#endif
|
||||
|
||||
openssl_initialized = 0;
|
||||
|
||||
status = AMQP_STATUS_OK;
|
||||
out:
|
||||
CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
|
||||
return status;
|
||||
}
|
193
ext/librabbitmq/librabbitmq/amqp_openssl_bio.c
Normal file
193
ext/librabbitmq/librabbitmq/amqp_openssl_bio.c
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2017 Alan Antonuk.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#ifndef AMQP_OPENSSL_V110
|
||||
static int BIO_meth_set_write(BIO_METHOD *biom,
|
||||
int (*wfn)(BIO *, const char *, int)) {
|
||||
biom->bwrite = wfn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int BIO_meth_set_read(BIO_METHOD *biom, int (*rfn)(BIO *, char *, int)) {
|
||||
biom->bread = rfn;
|
||||
return 0;
|
||||
}
|
||||
#endif /* AQP_OPENSSL_V110 */
|
||||
#endif /* AMQP_USE_AMQP_BIO */
|
||||
|
||||
int amqp_openssl_bio_init(void) {
|
||||
assert(!amqp_ssl_bio_initialized);
|
||||
#ifdef AMQP_USE_AMQP_BIO
|
||||
#ifdef AMQP_OPENSSL_V110
|
||||
if (!(amqp_bio_method = BIO_meth_new(BIO_TYPE_SOCKET, "amqp_bio_method"))) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
// casting away const is necessary until
|
||||
// https://github.com/openssl/openssl/pull/2181/, which is targeted for
|
||||
// openssl 1.1.1
|
||||
BIO_METHOD *meth = (BIO_METHOD *)BIO_s_socket();
|
||||
BIO_meth_set_create(amqp_bio_method, BIO_meth_get_create(meth));
|
||||
BIO_meth_set_destroy(amqp_bio_method, BIO_meth_get_destroy(meth));
|
||||
BIO_meth_set_ctrl(amqp_bio_method, BIO_meth_get_ctrl(meth));
|
||||
BIO_meth_set_callback_ctrl(amqp_bio_method, BIO_meth_get_callback_ctrl(meth));
|
||||
BIO_meth_set_read(amqp_bio_method, BIO_meth_get_read(meth));
|
||||
BIO_meth_set_write(amqp_bio_method, BIO_meth_get_write(meth));
|
||||
BIO_meth_set_gets(amqp_bio_method, BIO_meth_get_gets(meth));
|
||||
BIO_meth_set_puts(amqp_bio_method, BIO_meth_get_puts(meth));
|
||||
#else
|
||||
if (!(amqp_bio_method = OPENSSL_malloc(sizeof(BIO_METHOD)))) {
|
||||
return AMQP_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(amqp_bio_method, BIO_s_socket(), sizeof(BIO_METHOD));
|
||||
#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
|
||||
#ifdef AMQP_OPENSSL_V110
|
||||
BIO_meth_free(amqp_bio_method);
|
||||
#else
|
||||
OPENSSL_free(amqp_bio_method);
|
||||
#endif
|
||||
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
|
||||
}
|
44
ext/librabbitmq/librabbitmq/amqp_openssl_bio.h
Normal file
44
ext/librabbitmq/librabbitmq/amqp_openssl_bio.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2017 Alan Antonuk.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef AMQP_OPENSSL_BIO
|
||||
#define AMQP_OPENSSL_BIO
|
||||
|
||||
#include <openssl/bio.h>
|
||||
|
||||
int amqp_openssl_bio_init(void);
|
||||
|
||||
void amqp_openssl_bio_destroy(void);
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
|
||||
#define AMQP_OPENSSL_V110
|
||||
#endif
|
||||
|
||||
#ifdef AMQP_OPENSSL_V110
|
||||
typedef const BIO_METHOD *BIO_METHOD_PTR;
|
||||
#else
|
||||
typedef BIO_METHOD *BIO_METHOD_PTR;
|
||||
#endif
|
||||
|
||||
BIO_METHOD_PTR amqp_openssl_bio(void);
|
||||
|
||||
#endif /* ifndef AMQP_OPENSSL_BIO */
|
179
ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.c
Normal file
179
ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.c
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (C) 2012, iSEC Partners.
|
||||
* Copyright (C) 2015 Alan Antonuk.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Except as contained in this notice, the name of a copyright holder shall
|
||||
* not be used in advertising or otherwise to promote the sale, use or other
|
||||
* dealings in this Software without prior written authorization of the
|
||||
* copyright holder.
|
||||
*/
|
||||
|
||||
/* Originally from:
|
||||
* https://github.com/iSECPartners/ssl-conservatory
|
||||
* https://wiki.openssl.org/index.php/Hostname_validation
|
||||
*/
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509v3.h>
|
||||
|
||||
#include "amqp_hostcheck.h"
|
||||
#include "amqp_openssl_bio.h"
|
||||
#include "amqp_openssl_hostname_validation.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define HOSTNAME_MAX_SIZE 255
|
||||
|
||||
/**
|
||||
* Tries to find a match for hostname in the certificate's Common Name field.
|
||||
*
|
||||
* Returns AMQP_HVR_MATCH_FOUND if a match was found.
|
||||
* Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found.
|
||||
* Returns AMQP_HVR_MALFORMED_CERTIFICATE if the Common Name had a NUL character
|
||||
* embedded in it.
|
||||
* Returns AMQP_HVR_ERROR if the Common Name could not be extracted.
|
||||
*/
|
||||
static amqp_hostname_validation_result amqp_matches_common_name(
|
||||
const char *hostname, const X509 *server_cert) {
|
||||
int common_name_loc = -1;
|
||||
X509_NAME_ENTRY *common_name_entry = NULL;
|
||||
ASN1_STRING *common_name_asn1 = NULL;
|
||||
const char *common_name_str = NULL;
|
||||
|
||||
// Find the position of the CN field in the Subject field of the certificate
|
||||
common_name_loc = X509_NAME_get_index_by_NID(
|
||||
X509_get_subject_name((X509 *)server_cert), NID_commonName, -1);
|
||||
if (common_name_loc < 0) {
|
||||
return AMQP_HVR_ERROR;
|
||||
}
|
||||
|
||||
// Extract the CN field
|
||||
common_name_entry = X509_NAME_get_entry(
|
||||
X509_get_subject_name((X509 *)server_cert), common_name_loc);
|
||||
if (common_name_entry == NULL) {
|
||||
return AMQP_HVR_ERROR;
|
||||
}
|
||||
|
||||
// Convert the CN field to a C string
|
||||
common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
|
||||
if (common_name_asn1 == NULL) {
|
||||
return AMQP_HVR_ERROR;
|
||||
}
|
||||
|
||||
#ifdef AMQP_OPENSSL_V110
|
||||
common_name_str = (const char *)ASN1_STRING_get0_data(common_name_asn1);
|
||||
#else
|
||||
common_name_str = (char *)ASN1_STRING_data(common_name_asn1);
|
||||
#endif
|
||||
|
||||
// Make sure there isn't an embedded NUL character in the CN
|
||||
if ((size_t)ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) {
|
||||
return AMQP_HVR_MALFORMED_CERTIFICATE;
|
||||
}
|
||||
|
||||
// Compare expected hostname with the CN
|
||||
if (amqp_hostcheck(common_name_str, hostname) == AMQP_HCR_MATCH) {
|
||||
return AMQP_HVR_MATCH_FOUND;
|
||||
} else {
|
||||
return AMQP_HVR_MATCH_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find a match for hostname in the certificate's Subject Alternative
|
||||
* Name extension.
|
||||
*
|
||||
* Returns AMQP_HVR_MATCH_FOUND if a match was found.
|
||||
* Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found.
|
||||
* Returns AMQP_HVR_MALFORMED_CERTIFICATE if any of the hostnames had a NUL
|
||||
* character embedded in it.
|
||||
* Returns AMQP_HVR_NO_SAN_PRESENT if the SAN extension was not present in the
|
||||
* certificate.
|
||||
*/
|
||||
static amqp_hostname_validation_result amqp_matches_subject_alternative_name(
|
||||
const char *hostname, const X509 *server_cert) {
|
||||
amqp_hostname_validation_result result = AMQP_HVR_MATCH_NOT_FOUND;
|
||||
int i;
|
||||
int san_names_nb = -1;
|
||||
STACK_OF(GENERAL_NAME) *san_names = NULL;
|
||||
|
||||
// Try to extract the names within the SAN extension from the certificate
|
||||
san_names =
|
||||
X509_get_ext_d2i((X509 *)server_cert, NID_subject_alt_name, NULL, NULL);
|
||||
if (san_names == NULL) {
|
||||
return AMQP_HVR_NO_SAN_PRESENT;
|
||||
}
|
||||
san_names_nb = sk_GENERAL_NAME_num(san_names);
|
||||
|
||||
// Check each name within the extension
|
||||
for (i = 0; i < san_names_nb; i++) {
|
||||
const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i);
|
||||
|
||||
if (current_name->type == GEN_DNS) {
|
||||
// Current name is a DNS name, let's check it
|
||||
const char *dns_name = (const char *)
|
||||
#ifdef AMQP_OPENSSL_V110
|
||||
ASN1_STRING_get0_data(current_name->d.dNSName);
|
||||
#else
|
||||
ASN1_STRING_data(current_name->d.dNSName);
|
||||
#endif
|
||||
|
||||
// Make sure there isn't an embedded NUL character in the DNS name
|
||||
if ((size_t)ASN1_STRING_length(current_name->d.dNSName) !=
|
||||
strlen(dns_name)) {
|
||||
result = AMQP_HVR_MALFORMED_CERTIFICATE;
|
||||
break;
|
||||
} else { // Compare expected hostname with the DNS name
|
||||
if (amqp_hostcheck(dns_name, hostname) == AMQP_HCR_MATCH) {
|
||||
result = AMQP_HVR_MATCH_FOUND;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the server's identity by looking for the expected hostname in the
|
||||
* server's certificate. As described in RFC 6125, it first tries to find a
|
||||
* match in the Subject Alternative Name extension. If the extension is not
|
||||
* present in the certificate, it checks the Common Name instead.
|
||||
*
|
||||
* Returns AMQP_HVR_MATCH_FOUND if a match was found.
|
||||
* Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found.
|
||||
* Returns AMQP_HVR_MALFORMED_CERTIFICATE if any of the hostnames had a NUL
|
||||
* character embedded in it.
|
||||
* Returns AMQP_HVR_ERROR if there was an error.
|
||||
*/
|
||||
amqp_hostname_validation_result amqp_ssl_validate_hostname(
|
||||
const char *hostname, const X509 *server_cert) {
|
||||
amqp_hostname_validation_result result;
|
||||
|
||||
if ((hostname == NULL) || (server_cert == NULL)) return AMQP_HVR_ERROR;
|
||||
|
||||
// First try the Subject Alternative Names extension
|
||||
result = amqp_matches_subject_alternative_name(hostname, server_cert);
|
||||
if (result == AMQP_HVR_NO_SAN_PRESENT) {
|
||||
// Extension was not found: try the Common Name
|
||||
result = amqp_matches_common_name(hostname, server_cert);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef librabbitmq_amqp_openssl_hostname_validation_h
|
||||
#define librabbitmq_amqp_openssl_hostname_validation_h
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012, iSEC Partners.
|
||||
* Copyright (C) 2015 Alan Antonuk.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Except as contained in this notice, the name of a copyright holder shall
|
||||
* not be used in advertising or otherwise to promote the sale, use or other
|
||||
* dealings in this Software without prior written authorization of the
|
||||
* copyright holder.
|
||||
*/
|
||||
|
||||
/* Originally from:
|
||||
* https://github.com/iSECPartners/ssl-conservatory
|
||||
* https://wiki.openssl.org/index.php/Hostname_validation
|
||||
*/
|
||||
|
||||
#include <openssl/x509v3.h>
|
||||
|
||||
typedef enum {
|
||||
AMQP_HVR_MATCH_FOUND,
|
||||
AMQP_HVR_MATCH_NOT_FOUND,
|
||||
AMQP_HVR_NO_SAN_PRESENT,
|
||||
AMQP_HVR_MALFORMED_CERTIFICATE,
|
||||
AMQP_HVR_ERROR
|
||||
} amqp_hostname_validation_result;
|
||||
|
||||
/**
|
||||
* Validates the server's identity by looking for the expected hostname in the
|
||||
* server's certificate. As described in RFC 6125, it first tries to find a match
|
||||
* in the Subject Alternative Name extension. If the extension is not present in
|
||||
* the certificate, it checks the Common Name instead.
|
||||
*
|
||||
* Returns AMQP_HVR_MATCH_FOUND if a match was found.
|
||||
* Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found.
|
||||
* Returns AMQP_HVR_MALFORMED_CERTIFICATE if any of the hostnames had a NUL
|
||||
* character embedded in it.
|
||||
* Returns AMQP_HVR_ERROR if there was an error.
|
||||
*/
|
||||
amqp_hostname_validation_result amqp_ssl_validate_hostname(
|
||||
const char *hostname, const X509 *server_cert);
|
||||
|
||||
#endif
|
374
ext/librabbitmq/librabbitmq/amqp_private.h
Normal file
374
ext/librabbitmq/librabbitmq/amqp_private.h
Normal file
|
@ -0,0 +1,374 @@
|
|||
#ifndef librabbitmq_amqp_private_h
|
||||
#define librabbitmq_amqp_private_h
|
||||
|
||||
/*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MIT
|
||||
*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2012-2014
|
||||
* Alan Antonuk. All Rights Reserved.
|
||||
*
|
||||
* Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
|
||||
* VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#define AMQ_COPYRIGHT \
|
||||
"Copyright (c) 2007-2014 VMWare Inc, Tony Garnock-Jones," \
|
||||
" and Alan Antonuk."
|
||||
|
||||
#include "amqp.h"
|
||||
#include "amqp_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
|
||||
#else
|
||||
#define AMQP_NORETURN
|
||||
#define AMQP_UNUSED
|
||||
#endif
|
||||
|
||||
#if __GNUC__ >= 4
|
||||
#define AMQP_PRIVATE __attribute__((visibility("hidden")))
|
||||
#else
|
||||
#define AMQP_PRIVATE
|
||||
#endif
|
||||
|
||||
char *amqp_os_error_string(int err);
|
||||
|
||||
#ifdef WITH_SSL
|
||||
char *amqp_ssl_error_string(int err);
|
||||
#endif
|
||||
|
||||
#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
|
1492
ext/librabbitmq/librabbitmq/amqp_socket.c
Normal file
1492
ext/librabbitmq/librabbitmq/amqp_socket.c
Normal file
File diff suppressed because it is too large
Load diff
188
ext/librabbitmq/librabbitmq/amqp_socket.h
Normal file
188
ext/librabbitmq/librabbitmq/amqp_socket.h
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Michael Steinert are Copyright (c) 2012-2013 Michael
|
||||
* Steinert. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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, 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,
|
||||
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 */
|
239
ext/librabbitmq/librabbitmq/amqp_ssl_socket.h
Normal file
239
ext/librabbitmq/librabbitmq/amqp_ssl_socket.h
Normal file
|
@ -0,0 +1,239 @@
|
|||
/** \file */
|
||||
/*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Michael Steinert are Copyright (c) 2012-2013 Michael
|
||||
* Steinert. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef AMQP_SSL_H
|
||||
#define AMQP_SSL_H
|
||||
|
||||
#include <amqp.h>
|
||||
|
||||
AMQP_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Create a new SSL/TLS socket object.
|
||||
*
|
||||
* The returned socket object is owned by the \ref amqp_connection_state_t
|
||||
* object and will be destroyed when the state object is destroyed or a new
|
||||
* socket object is created.
|
||||
*
|
||||
* If the socket object creation fails, the \ref amqp_connection_state_t object
|
||||
* will not be changed.
|
||||
*
|
||||
* The object returned by this function can be retrieved from the
|
||||
* amqp_connection_state_t object later using the amqp_get_socket() function.
|
||||
*
|
||||
* Calling this function may result in the underlying SSL library being
|
||||
* initialized.
|
||||
* \sa amqp_set_initialize_ssl_library()
|
||||
*
|
||||
* \param [in,out] state The connection object that owns the SSL/TLS socket
|
||||
* \return A new socket object or NULL if an error occurred.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
amqp_socket_t *AMQP_CALL amqp_ssl_socket_new(amqp_connection_state_t state);
|
||||
|
||||
/**
|
||||
* Set the CA certificate.
|
||||
*
|
||||
* \param [in,out] self An SSL/TLS socket object.
|
||||
* \param [in] cacert Path to the CA cert file in PEM format.
|
||||
*
|
||||
* \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on
|
||||
* failure.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int AMQP_CALL amqp_ssl_socket_set_cacert(amqp_socket_t *self,
|
||||
const char *cacert);
|
||||
|
||||
/**
|
||||
* Set the client key.
|
||||
*
|
||||
* \param [in,out] self An SSL/TLS socket object.
|
||||
* \param [in] cert Path to the client certificate in PEM foramt.
|
||||
* \param [in] key Path to the client key in PEM format.
|
||||
*
|
||||
* \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on
|
||||
* failure.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int AMQP_CALL amqp_ssl_socket_set_key(amqp_socket_t *self, const char *cert,
|
||||
const char *key);
|
||||
|
||||
/**
|
||||
* Set the client key from a buffer.
|
||||
*
|
||||
* \param [in,out] self An SSL/TLS socket object.
|
||||
* \param [in] cert Path to the client certificate in PEM foramt.
|
||||
* \param [in] key A buffer containing client key in PEM format.
|
||||
* \param [in] n The length of the buffer.
|
||||
*
|
||||
* \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on
|
||||
* failure.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int AMQP_CALL amqp_ssl_socket_set_key_buffer(amqp_socket_t *self,
|
||||
const char *cert, const void *key,
|
||||
size_t n);
|
||||
|
||||
/**
|
||||
* Enable or disable peer verification.
|
||||
*
|
||||
* \deprecated use \amqp_ssl_socket_set_verify_peer and
|
||||
* \amqp_ssl_socket_set_verify_hostname instead.
|
||||
*
|
||||
* If peer verification is enabled then the common name in the server
|
||||
* certificate must match the server name. Peer verification is enabled by
|
||||
* default.
|
||||
*
|
||||
* \param [in,out] self An SSL/TLS socket object.
|
||||
* \param [in] verify Enable or disable peer verification.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_DEPRECATED(AMQP_PUBLIC_FUNCTION void AMQP_CALL amqp_ssl_socket_set_verify(
|
||||
amqp_socket_t *self, amqp_boolean_t verify));
|
||||
|
||||
/**
|
||||
* Enable or disable peer verification.
|
||||
*
|
||||
* Peer verification validates the certificate chain that is sent by the broker.
|
||||
* Hostname validation is controlled by \amqp_ssl_socket_set_verify_peer.
|
||||
*
|
||||
* \param [in,out] self An SSL/TLS socket object.
|
||||
* \param [in] verify enable or disable peer validation
|
||||
*
|
||||
* \since v0.8.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
void AMQP_CALL amqp_ssl_socket_set_verify_peer(amqp_socket_t *self,
|
||||
amqp_boolean_t verify);
|
||||
|
||||
/**
|
||||
* Enable or disable hostname verification.
|
||||
*
|
||||
* Hostname verification checks the broker cert for a CN or SAN that matches the
|
||||
* hostname that amqp_socket_open() is presented. Peer verification is
|
||||
* controlled by \amqp_ssl_socket_set_verify_peer
|
||||
*
|
||||
* \since v0.8.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
void AMQP_CALL amqp_ssl_socket_set_verify_hostname(amqp_socket_t *self,
|
||||
amqp_boolean_t verify);
|
||||
|
||||
typedef enum {
|
||||
AMQP_TLSv1 = 1,
|
||||
AMQP_TLSv1_1 = 2,
|
||||
AMQP_TLSv1_2 = 3,
|
||||
AMQP_TLSvLATEST = 0xFFFF
|
||||
} amqp_tls_version_t;
|
||||
|
||||
/**
|
||||
* Set min and max TLS versions.
|
||||
*
|
||||
* Set the oldest and newest acceptable TLS versions that are acceptable when
|
||||
* connecting to the broker. Set min == max to restrict to just that
|
||||
* version.
|
||||
*
|
||||
* \param [in,out] self An SSL/TLS socket object.
|
||||
* \param [in] min the minimum acceptable TLS version
|
||||
* \param [in] max the maxmium acceptable TLS version
|
||||
* \returns AMQP_STATUS_OK on success, AMQP_STATUS_UNSUPPORTED if OpenSSL does
|
||||
* not support the requested TLS version, AMQP_STATUS_INVALID_PARAMETER if an
|
||||
* invalid combination of parameters is passed.
|
||||
*
|
||||
* \since v0.8.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int AMQP_CALL amqp_ssl_socket_set_ssl_versions(amqp_socket_t *self,
|
||||
amqp_tls_version_t min,
|
||||
amqp_tls_version_t max);
|
||||
|
||||
/**
|
||||
* Sets whether rabbitmq-c will initialize OpenSSL.
|
||||
*
|
||||
* OpenSSL requires a one-time initialization across a whole program, this sets
|
||||
* whether or not rabbitmq-c will initialize the SSL library when the first call
|
||||
* to amqp_ssl_socket_new() is made. You should call this function with
|
||||
* do_init = 0 if the underlying SSL library is initialized somewhere else
|
||||
* the program.
|
||||
*
|
||||
* Failing to initialize or double initialization of the SSL library will
|
||||
* result in undefined behavior
|
||||
*
|
||||
* By default rabbitmq-c will initialize the underlying SSL library.
|
||||
*
|
||||
* NOTE: calling this function after the first socket has been opened with
|
||||
* amqp_open_socket() will not have any effect.
|
||||
*
|
||||
* \param [in] do_initialize If 0 rabbitmq-c will not initialize the SSL
|
||||
* library, otherwise rabbitmq-c will initialize the
|
||||
* SSL library
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
void AMQP_CALL amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize);
|
||||
|
||||
/**
|
||||
* Initialize the underlying SSL/TLS library.
|
||||
*
|
||||
* The OpenSSL library requires a one-time initialization across the whole
|
||||
* program.
|
||||
*
|
||||
* This function unconditionally initializes OpenSSL so that rabbitmq-c may
|
||||
* use it.
|
||||
*
|
||||
* This function is thread-safe, and may be called more than once.
|
||||
*
|
||||
* \return AMQP_STATUS_OK on success.
|
||||
*
|
||||
* \since v0.9.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int AMQP_CALL amqp_initialize_ssl_library(void);
|
||||
|
||||
/**
|
||||
* Uninitialize the underlying SSL/TLS library.
|
||||
*
|
||||
* \return AMQP_STATUS_OK on success.
|
||||
*
|
||||
* \since v0.9.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int AMQP_CALL amqp_uninitialize_ssl_library(void);
|
||||
|
||||
AMQP_END_DECLS
|
||||
|
||||
#endif /* AMQP_SSL_H */
|
668
ext/librabbitmq/librabbitmq/amqp_table.c
Normal file
668
ext/librabbitmq/librabbitmq/amqp_table.c
Normal file
|
@ -0,0 +1,668 @@
|
|||
/*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MIT
|
||||
*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2012-2013
|
||||
* Alan Antonuk. All Rights Reserved.
|
||||
*
|
||||
* Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
|
||||
* VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool,
|
||||
amqp_field_value_t *entry, size_t *offset);
|
||||
|
||||
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) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
int amqp_decode_table(amqp_bytes_t encoded, amqp_pool_t *pool,
|
||||
amqp_table_t *output, size_t *offset) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool,
|
||||
amqp_field_value_t *entry, size_t *offset) {
|
||||
int res = 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);
|
||||
goto out;
|
||||
|
||||
case AMQP_FIELD_KIND_TIMESTAMP:
|
||||
TRIVIAL_FIELD_DECODER(64);
|
||||
|
||||
case AMQP_FIELD_KIND_TABLE:
|
||||
res = amqp_decode_table(encoded, pool, &(entry->value.table), offset);
|
||||
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;
|
||||
}
|
81
ext/librabbitmq/librabbitmq/amqp_table.h
Normal file
81
ext/librabbitmq/librabbitmq/amqp_table.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MIT
|
||||
*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2014 Alan Antonuk.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
#ifndef AMQP_TABLE_H
|
||||
#define AMQP_TABLE_H
|
||||
|
||||
#include "amqp.h"
|
||||
#include "amqp_private.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 */
|
238
ext/librabbitmq/librabbitmq/amqp_tcp_socket.c
Normal file
238
ext/librabbitmq/librabbitmq/amqp_tcp_socket.c
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright 2012-2013 Michael Steinert
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "amqp_private.h"
|
||||
#include "amqp_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,
|
||||
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;
|
||||
}
|
68
ext/librabbitmq/librabbitmq/amqp_tcp_socket.h
Normal file
68
ext/librabbitmq/librabbitmq/amqp_tcp_socket.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/** \file */
|
||||
/*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Michael Steinert are Copyright (c) 2012-2013 Michael
|
||||
* Steinert. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A TCP socket connection.
|
||||
*/
|
||||
|
||||
#ifndef AMQP_TCP_SOCKET_H
|
||||
#define AMQP_TCP_SOCKET_H
|
||||
|
||||
#include <amqp.h>
|
||||
|
||||
AMQP_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Create a new TCP socket.
|
||||
*
|
||||
* Call amqp_connection_close() to release socket resources.
|
||||
*
|
||||
* \return A new socket object or NULL if an error occurred.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
amqp_socket_t *AMQP_CALL amqp_tcp_socket_new(amqp_connection_state_t state);
|
||||
|
||||
/**
|
||||
* Assign an open file descriptor to a socket object.
|
||||
*
|
||||
* This function must not be used in conjunction with amqp_socket_open(), i.e.
|
||||
* the socket connection should already be open(2) when this function is
|
||||
* called.
|
||||
*
|
||||
* \param [in,out] self A TCP socket object.
|
||||
* \param [in] sockfd An open socket descriptor.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
void AMQP_CALL amqp_tcp_socket_set_sockfd(amqp_socket_t *self, int sockfd);
|
||||
|
||||
AMQP_END_DECLS
|
||||
|
||||
#endif /* AMQP_TCP_SOCKET_H */
|
265
ext/librabbitmq/librabbitmq/amqp_time.c
Normal file
265
ext/librabbitmq/librabbitmq/amqp_time.c
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "amqp_time.h"
|
||||
#include "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, 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 (0 == timeout->tv_sec && 0 == timeout->tv_usec) {
|
||||
*time = amqp_time_immediate();
|
||||
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_immediate(void) {
|
||||
amqp_time_t time;
|
||||
time.time_point_ns = 0;
|
||||
return time;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
130
ext/librabbitmq/librabbitmq/amqp_time.h
Normal file
130
ext/librabbitmq/librabbitmq/amqp_time.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#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.
|
||||
* If timeout = {0, 0}, an amqp_time_immediate() 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, 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 immediate amqp_time_t */
|
||||
amqp_time_t amqp_time_immediate(void);
|
||||
|
||||
/* 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 */
|
220
ext/librabbitmq/librabbitmq/amqp_url.c
Normal file
220
ext/librabbitmq/librabbitmq/amqp_url.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MIT
|
||||
*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2012-2013
|
||||
* Alan Antonuk. All Rights Reserved.
|
||||
*
|
||||
* Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
|
||||
* VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#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 = start = 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;
|
||||
}
|
785
ext/librabbitmq/librabbitmq/codegen.py
Normal file
785
ext/librabbitmq/librabbitmq/codegen.py
Normal file
|
@ -0,0 +1,785 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MIT
|
||||
#
|
||||
# Portions created by Alan Antonuk are Copyright (c) 2012-2013
|
||||
# Alan Antonuk. All Rights Reserved.
|
||||
#
|
||||
# Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
|
||||
# VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
# files (the "Software"), to deal in the Software without
|
||||
# restriction, including without limitation the rights to use, copy,
|
||||
# modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
# of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
from __future__ import nested_scopes
|
||||
from __future__ import division
|
||||
|
||||
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_PUBLIC_FUNCTION\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 """/* Generated code. Do not edit. Edit and re-run codegen.py instead.
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MIT
|
||||
*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2012-2013
|
||||
* Alan Antonuk. All Rights Reserved.
|
||||
*
|
||||
* Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
|
||||
* VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#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,v,cls) 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,v,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 """/* Generated code. Do not edit. Edit and re-run codegen.py instead.
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MIT
|
||||
*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2012-2013
|
||||
* Alan Antonuk. All Rights Reserved.
|
||||
*
|
||||
* Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
|
||||
* VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** @file amqp_framing.h */
|
||||
#ifndef AMQP_FRAMING_H
|
||||
#define AMQP_FRAMING_H
|
||||
|
||||
#include <amqp.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_PUBLIC_FUNCTION
|
||||
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_PUBLIC_FUNCTION
|
||||
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_PUBLIC_FUNCTION
|
||||
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_PUBLIC_FUNCTION
|
||||
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_PUBLIC_FUNCTION
|
||||
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_PUBLIC_FUNCTION
|
||||
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_PUBLIC_FUNCTION
|
||||
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_PUBLIC_FUNCTION
|
||||
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 /* AMQP_FRAMING_H */"""
|
||||
|
||||
def generateErl(specPath):
|
||||
genErl(AmqpSpec(specPath))
|
||||
|
||||
def generateHrl(specPath):
|
||||
genHrl(AmqpSpec(specPath))
|
||||
|
||||
if __name__ == "__main__":
|
||||
do_main(generateHrl, generateErl)
|
28
ext/librabbitmq/librabbitmq/unix/threads.h
Normal file
28
ext/librabbitmq/librabbitmq/unix/threads.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2012-2013 Michael Steinert
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef AMQP_THREADS_H
|
||||
#define AMQP_THREADS_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#endif /* AMQP_THREADS_H */
|
245
ext/librabbitmq/librabbitmq/win32/msinttypes/stdint.h
Normal file
245
ext/librabbitmq/librabbitmq/win32/msinttypes/stdint.h
Normal file
|
@ -0,0 +1,245 @@
|
|||
// ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2008 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The name of the author may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_STDINT_H_ // [
|
||||
#define _MSC_STDINT_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||
// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
|
||||
// or compiler give many errors like this:
|
||||
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <wchar.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// Define _W64 macros to mark types changing their size, like intptr_t.
|
||||
#ifndef _W64
|
||||
#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
|
||||
#define _W64 __w64
|
||||
#else
|
||||
#define _W64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// 7.18.1 Integer types
|
||||
|
||||
// 7.18.1.1 Exact-width integer types
|
||||
|
||||
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
|
||||
// realize that, e.g. char has the same size as __int8
|
||||
// so we give up on __intX for them.
|
||||
#if (_MSC_VER < 1300)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#endif
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
// 7.18.1.2 Minimum-width integer types
|
||||
typedef int8_t int_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
typedef int64_t int_least64_t;
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
typedef uint64_t uint_least64_t;
|
||||
|
||||
// 7.18.1.3 Fastest minimum-width integer types
|
||||
typedef int8_t int_fast8_t;
|
||||
typedef int16_t int_fast16_t;
|
||||
typedef int32_t int_fast32_t;
|
||||
typedef int64_t int_fast64_t;
|
||||
typedef uint8_t uint_fast8_t;
|
||||
typedef uint16_t uint_fast16_t;
|
||||
typedef uint32_t uint_fast32_t;
|
||||
typedef uint64_t uint_fast64_t;
|
||||
|
||||
// 7.18.1.4 Integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
typedef signed __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else // _WIN64 ][
|
||||
typedef _W64 signed int intptr_t;
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.1.5 Greatest-width integer types
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
|
||||
// 7.18.2 Limits of specified-width integer types
|
||||
|
||||
#if !defined(__cplusplus) || \
|
||||
defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and
|
||||
// footnote 221 at page 259
|
||||
|
||||
// 7.18.2.1 Limits of exact-width integer types
|
||||
#define INT8_MIN ((int8_t)_I8_MIN)
|
||||
#define INT8_MAX _I8_MAX
|
||||
#define INT16_MIN ((int16_t)_I16_MIN)
|
||||
#define INT16_MAX _I16_MAX
|
||||
#define INT32_MIN ((int32_t)_I32_MIN)
|
||||
#define INT32_MAX _I32_MAX
|
||||
#define INT64_MIN ((int64_t)_I64_MIN)
|
||||
#define INT64_MAX _I64_MAX
|
||||
#define UINT8_MAX _UI8_MAX
|
||||
#define UINT16_MAX _UI16_MAX
|
||||
#define UINT32_MAX _UI32_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
|
||||
// 7.18.2.2 Limits of minimum-width integer types
|
||||
#define INT_LEAST8_MIN INT8_MIN
|
||||
#define INT_LEAST8_MAX INT8_MAX
|
||||
#define INT_LEAST16_MIN INT16_MIN
|
||||
#define INT_LEAST16_MAX INT16_MAX
|
||||
#define INT_LEAST32_MIN INT32_MIN
|
||||
#define INT_LEAST32_MAX INT32_MAX
|
||||
#define INT_LEAST64_MIN INT64_MIN
|
||||
#define INT_LEAST64_MAX INT64_MAX
|
||||
#define UINT_LEAST8_MAX UINT8_MAX
|
||||
#define UINT_LEAST16_MAX UINT16_MAX
|
||||
#define UINT_LEAST32_MAX UINT32_MAX
|
||||
#define UINT_LEAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.3 Limits of fastest minimum-width integer types
|
||||
#define INT_FAST8_MIN INT8_MIN
|
||||
#define INT_FAST8_MAX INT8_MAX
|
||||
#define INT_FAST16_MIN INT16_MIN
|
||||
#define INT_FAST16_MAX INT16_MAX
|
||||
#define INT_FAST32_MIN INT32_MIN
|
||||
#define INT_FAST32_MAX INT32_MAX
|
||||
#define INT_FAST64_MIN INT64_MIN
|
||||
#define INT_FAST64_MAX INT64_MAX
|
||||
#define UINT_FAST8_MAX UINT8_MAX
|
||||
#define UINT_FAST16_MAX UINT16_MAX
|
||||
#define UINT_FAST32_MAX UINT32_MAX
|
||||
#define UINT_FAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.4 Limits of integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
#define INTPTR_MIN INT64_MIN
|
||||
#define INTPTR_MAX INT64_MAX
|
||||
#define UINTPTR_MAX UINT64_MAX
|
||||
#else // _WIN64 ][
|
||||
#define INTPTR_MIN INT32_MIN
|
||||
#define INTPTR_MAX INT32_MAX
|
||||
#define UINTPTR_MAX UINT32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.2.5 Limits of greatest-width integer types
|
||||
#define INTMAX_MIN INT64_MIN
|
||||
#define INTMAX_MAX INT64_MAX
|
||||
#define UINTMAX_MAX UINT64_MAX
|
||||
|
||||
// 7.18.3 Limits of other integer types
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
#define PTRDIFF_MIN _I64_MIN
|
||||
#define PTRDIFF_MAX _I64_MAX
|
||||
#else // _WIN64 ][
|
||||
#define PTRDIFF_MIN _I32_MIN
|
||||
#define PTRDIFF_MAX _I32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#define SIG_ATOMIC_MIN INT_MIN
|
||||
#define SIG_ATOMIC_MAX INT_MAX
|
||||
|
||||
#ifndef SIZE_MAX // [
|
||||
#ifdef _WIN64 // [
|
||||
#define SIZE_MAX _UI64_MAX
|
||||
#else // _WIN64 ][
|
||||
#define SIZE_MAX _UI32_MAX
|
||||
#endif // _WIN64 ]
|
||||
#endif // SIZE_MAX ]
|
||||
|
||||
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
|
||||
#ifndef WCHAR_MIN // [
|
||||
#define WCHAR_MIN 0
|
||||
#endif // WCHAR_MIN ]
|
||||
#ifndef WCHAR_MAX // [
|
||||
#define WCHAR_MAX _UI16_MAX
|
||||
#endif // WCHAR_MAX ]
|
||||
|
||||
#define WINT_MIN 0
|
||||
#define WINT_MAX _UI16_MAX
|
||||
|
||||
#endif // __STDC_LIMIT_MACROS ]
|
||||
|
||||
// 7.18.4 Limits of other integer types
|
||||
|
||||
#if !defined(__cplusplus) || \
|
||||
defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
#define INTMAX_C INT64_C
|
||||
#define UINTMAX_C UINT64_C
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
#endif // _MSC_STDINT_H_ ]
|
56
ext/librabbitmq/librabbitmq/win32/threads.c
Normal file
56
ext/librabbitmq/librabbitmq/win32/threads.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2012-2013 Michael Steinert
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
52
ext/librabbitmq/librabbitmq/win32/threads.h
Normal file
52
ext/librabbitmq/librabbitmq/win32/threads.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Michael Steinert are Copyright (c) 2012-2013 Michael
|
||||
* Steinert. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#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 */
|
Loading…
Add table
Add a link
Reference in a new issue