Beginning CMake configuration for ZT

Only tested on Windows so far
This commit is contained in:
Grant Limberg 2019-06-20 16:13:52 -07:00
commit 0b3b5f6174
111 changed files with 19586 additions and 36 deletions

View 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)

File diff suppressed because it is too large Load diff

View 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;
}

View 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;
}

View 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;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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);
}

View 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

View 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;
}

View 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;
}

View 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
}

View 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 */

View 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;
}

View file

@ -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

View 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

File diff suppressed because it is too large Load diff

View 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 */

View 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 */

View 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;
}

View 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 */

View 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;
}

View 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 */

View 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;
}

View 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 */

View 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;
}

View 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)

View 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 */

View 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_ ]

View 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;
}

View 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 */