From 96a25cd188cbf0268e30860e562a14acb0b78788 Mon Sep 17 00:00:00 2001 From: Patrick Webster Date: Wed, 21 Aug 2019 11:17:27 -0500 Subject: [PATCH] Makefile, pg_amqp.c, librabbitmq: updating librabbitmq-c to current version --- Makefile | 3 + src/librabbitmq/LICENSE-MIT | 28 + src/librabbitmq/LICENSE-MPL-RabbitMQ | 473 -- src/librabbitmq/amqp.h | 2772 +++++++++-- src/librabbitmq/amqp_api.c | 599 ++- src/librabbitmq/amqp_config.h | 2 - src/librabbitmq/amqp_connection.c | 757 +-- src/librabbitmq/amqp_consumer.c | 307 ++ src/librabbitmq/amqp_debug.c | 4 +- src/librabbitmq/amqp_debug.h | 5 + src/librabbitmq/amqp_framing.c | 4434 +++++++++-------- src/librabbitmq/amqp_framing.h | 1540 +++--- src/librabbitmq/amqp_hostcheck.c | 195 + src/librabbitmq/amqp_hostcheck.h | 48 + src/librabbitmq/amqp_mem.c | 137 +- src/librabbitmq/amqp_openssl.c | 712 +++ src/librabbitmq/amqp_openssl_bio.c | 193 + src/librabbitmq/amqp_openssl_bio.h | 44 + .../amqp_openssl_hostname_validation.c | 179 + .../amqp_openssl_hostname_validation.h | 58 + src/librabbitmq/amqp_private.h | 405 +- src/librabbitmq/amqp_socket.c | 1554 +++++- src/librabbitmq/amqp_socket.h | 188 + src/librabbitmq/amqp_ssl_socket.h | 252 + src/librabbitmq/amqp_table.c | 736 ++- src/librabbitmq/amqp_table.h | 81 + src/librabbitmq/amqp_tcp_socket.c | 238 + src/librabbitmq/amqp_tcp_socket.h | 68 + src/librabbitmq/amqp_time.c | 265 + src/librabbitmq/amqp_time.h | 130 + src/librabbitmq/amqp_url.c | 220 + src/librabbitmq/unix/threads.h | 28 + src/librabbitmq/win32/msinttypes/stdint.h | 245 + src/librabbitmq/win32/threads.c | 56 + src/librabbitmq/win32/threads.h | 52 + src/pg_amqp.c | 142 +- 36 files changed, 12561 insertions(+), 4589 deletions(-) create mode 100644 src/librabbitmq/LICENSE-MIT delete mode 100644 src/librabbitmq/LICENSE-MPL-RabbitMQ delete mode 100644 src/librabbitmq/amqp_config.h create mode 100644 src/librabbitmq/amqp_consumer.c create mode 100644 src/librabbitmq/amqp_debug.h create mode 100644 src/librabbitmq/amqp_hostcheck.c create mode 100644 src/librabbitmq/amqp_hostcheck.h create mode 100644 src/librabbitmq/amqp_openssl.c create mode 100644 src/librabbitmq/amqp_openssl_bio.c create mode 100644 src/librabbitmq/amqp_openssl_bio.h create mode 100644 src/librabbitmq/amqp_openssl_hostname_validation.c create mode 100644 src/librabbitmq/amqp_openssl_hostname_validation.h create mode 100644 src/librabbitmq/amqp_socket.h create mode 100644 src/librabbitmq/amqp_ssl_socket.h create mode 100644 src/librabbitmq/amqp_table.h create mode 100644 src/librabbitmq/amqp_tcp_socket.c create mode 100644 src/librabbitmq/amqp_tcp_socket.h create mode 100644 src/librabbitmq/amqp_time.c create mode 100644 src/librabbitmq/amqp_time.h create mode 100644 src/librabbitmq/amqp_url.c create mode 100644 src/librabbitmq/unix/threads.h create mode 100644 src/librabbitmq/win32/msinttypes/stdint.h create mode 100644 src/librabbitmq/win32/threads.c create mode 100644 src/librabbitmq/win32/threads.h diff --git a/Makefile b/Makefile index 89885e2..b3d315f 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,9 @@ PG_CONFIG = pg_config PG91 = $(shell $(PG_CONFIG) --version | grep -qE " 8\.| 9\.0" && echo no || echo yes) ifeq ($(PG91),yes) +CFLAGS_SL='-D HAVE_POLL' +# Windows Support +# CFLAGS_SL='-D HAVE_SELECT' DOCS = $(wildcard doc/*.*) #TESTS = $(wildcard test/sql/*.sql) #REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS)) diff --git a/src/librabbitmq/LICENSE-MIT b/src/librabbitmq/LICENSE-MIT new file mode 100644 index 0000000..5c7630d --- /dev/null +++ b/src/librabbitmq/LICENSE-MIT @@ -0,0 +1,28 @@ +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. diff --git a/src/librabbitmq/LICENSE-MPL-RabbitMQ b/src/librabbitmq/LICENSE-MPL-RabbitMQ deleted file mode 100644 index 2d0a7b1..0000000 --- a/src/librabbitmq/LICENSE-MPL-RabbitMQ +++ /dev/null @@ -1,473 +0,0 @@ - MOZILLA PUBLIC LICENSE - Version 1.1 - - --------------- - -1. Definitions. - - 1.0.1. "Commercial Use" means distribution or otherwise making the - Covered Code available to a third party. - - 1.1. "Contributor" means each entity that creates or contributes to - the creation of Modifications. - - 1.2. "Contributor Version" means the combination of the Original - Code, prior Modifications used by a Contributor, and the Modifications - made by that particular Contributor. - - 1.3. "Covered Code" means the Original Code or Modifications or the - combination of the Original Code and Modifications, in each case - including portions thereof. - - 1.4. "Electronic Distribution Mechanism" means a mechanism generally - accepted in the software development community for the electronic - transfer of data. - - 1.5. "Executable" means Covered Code in any form other than Source - Code. - - 1.6. "Initial Developer" means the individual or entity identified - as the Initial Developer in the Source Code notice required by Exhibit - A. - - 1.7. "Larger Work" means a work which combines Covered Code or - portions thereof with code not governed by the terms of this License. - - 1.8. "License" means this document. - - 1.8.1. "Licensable" means having the right to grant, to the maximum - extent possible, whether at the time of the initial grant or - subsequently acquired, any and all of the rights conveyed herein. - - 1.9. "Modifications" means any addition to or deletion from the - substance or structure of either the Original Code or any previous - Modifications. When Covered Code is released as a series of files, a - Modification is: - A. Any addition to or deletion from the contents of a file - containing Original Code or previous Modifications. - - B. Any new file that contains any part of the Original Code or - previous Modifications. - - 1.10. "Original Code" means Source Code of computer software code - which is described in the Source Code notice required by Exhibit A as - Original Code, and which, at the time of its release under this - License is not already Covered Code governed by this License. - - 1.10.1. "Patent Claims" means any patent claim(s), now owned or - hereafter acquired, including without limitation, method, process, - and apparatus claims, in any patent Licensable by grantor. - - 1.11. "Source Code" means the preferred form of the Covered Code for - making modifications to it, including all modules it contains, plus - any associated interface definition files, scripts used to control - compilation and installation of an Executable, or source code - differential comparisons against either the Original Code or another - well known, available Covered Code of the Contributor's choice. The - Source Code can be in a compressed or archival form, provided the - appropriate decompression or de-archiving software is widely available - for no charge. - - 1.12. "You" (or "Your") means an individual or a legal entity - exercising rights under, and complying with all of the terms of, this - License or a future version of this License issued under Section 6.1. - For legal entities, "You" includes any entity which controls, is - controlled by, or is under common control with You. For purposes of - this definition, "control" means (a) the power, direct or indirect, - to cause the direction or management of such entity, whether by - contract or otherwise, or (b) ownership of more than fifty percent - (50%) of the outstanding shares or beneficial ownership of such - entity. - -2. Source Code License. - - 2.1. The Initial Developer Grant. - The Initial Developer hereby grants You a world-wide, royalty-free, - non-exclusive license, subject to third party intellectual property - claims: - (a) under intellectual property rights (other than patent or - trademark) Licensable by Initial Developer to use, reproduce, - modify, display, perform, sublicense and distribute the Original - Code (or portions thereof) with or without Modifications, and/or - as part of a Larger Work; and - - (b) under Patents Claims infringed by the making, using or - selling of Original Code, to make, have made, use, practice, - sell, and offer for sale, and/or otherwise dispose of the - Original Code (or portions thereof). - - (c) the licenses granted in this Section 2.1(a) and (b) are - effective on the date Initial Developer first distributes - Original Code under the terms of this License. - - (d) Notwithstanding Section 2.1(b) above, no patent license is - granted: 1) for code that You delete from the Original Code; 2) - separate from the Original Code; or 3) for infringements caused - by: i) the modification of the Original Code or ii) the - combination of the Original Code with other software or devices. - - 2.2. Contributor Grant. - Subject to third party intellectual property claims, each Contributor - hereby grants You a world-wide, royalty-free, non-exclusive license - - (a) under intellectual property rights (other than patent or - trademark) Licensable by Contributor, to use, reproduce, modify, - display, perform, sublicense and distribute the Modifications - created by such Contributor (or portions thereof) either on an - unmodified basis, with other Modifications, as Covered Code - and/or as part of a Larger Work; and - - (b) under Patent Claims infringed by the making, using, or - selling of Modifications made by that Contributor either alone - and/or in combination with its Contributor Version (or portions - of such combination), to make, use, sell, offer for sale, have - made, and/or otherwise dispose of: 1) Modifications made by that - Contributor (or portions thereof); and 2) the combination of - Modifications made by that Contributor with its Contributor - Version (or portions of such combination). - - (c) the licenses granted in Sections 2.2(a) and 2.2(b) are - effective on the date Contributor first makes Commercial Use of - the Covered Code. - - (d) Notwithstanding Section 2.2(b) above, no patent license is - granted: 1) for any code that Contributor has deleted from the - Contributor Version; 2) separate from the Contributor Version; - 3) for infringements caused by: i) third party modifications of - Contributor Version or ii) the combination of Modifications made - by that Contributor with other software (except as part of the - Contributor Version) or other devices; or 4) under Patent Claims - infringed by Covered Code in the absence of Modifications made by - that Contributor. - -3. Distribution Obligations. - - 3.1. Application of License. - The Modifications which You create or to which You contribute are - governed by the terms of this License, including without limitation - Section 2.2. The Source Code version of Covered Code may be - distributed only under the terms of this License or a future version - of this License released under Section 6.1, and You must include a - copy of this License with every copy of the Source Code You - distribute. You may not offer or impose any terms on any Source Code - version that alters or restricts the applicable version of this - License or the recipients' rights hereunder. However, You may include - an additional document offering the additional rights described in - Section 3.5. - - 3.2. Availability of Source Code. - Any Modification which You create or to which You contribute must be - made available in Source Code form under the terms of this License - either on the same media as an Executable version or via an accepted - Electronic Distribution Mechanism to anyone to whom you made an - Executable version available; and if made available via Electronic - Distribution Mechanism, must remain available for at least twelve (12) - months after the date it initially became available, or at least six - (6) months after a subsequent version of that particular Modification - has been made available to such recipients. You are responsible for - ensuring that the Source Code version remains available even if the - Electronic Distribution Mechanism is maintained by a third party. - - 3.3. Description of Modifications. - You must cause all Covered Code to which You contribute to contain a - file documenting the changes You made to create that Covered Code and - the date of any change. You must include a prominent statement that - the Modification is derived, directly or indirectly, from Original - Code provided by the Initial Developer and including the name of the - Initial Developer in (a) the Source Code, and (b) in any notice in an - Executable version or related documentation in which You describe the - origin or ownership of the Covered Code. - - 3.4. Intellectual Property Matters - (a) Third Party Claims. - If Contributor has knowledge that a license under a third party's - intellectual property rights is required to exercise the rights - granted by such Contributor under Sections 2.1 or 2.2, - Contributor must include a text file with the Source Code - distribution titled "LEGAL" which describes the claim and the - party making the claim in sufficient detail that a recipient will - know whom to contact. If Contributor obtains such knowledge after - the Modification is made available as described in Section 3.2, - Contributor shall promptly modify the LEGAL file in all copies - Contributor makes available thereafter and shall take other steps - (such as notifying appropriate mailing lists or newsgroups) - reasonably calculated to inform those who received the Covered - Code that new knowledge has been obtained. - - (b) Contributor APIs. - If Contributor's Modifications include an application programming - interface and Contributor has knowledge of patent licenses which - are reasonably necessary to implement that API, Contributor must - also include this information in the LEGAL file. - - (c) Representations. - Contributor represents that, except as disclosed pursuant to - Section 3.4(a) above, Contributor believes that Contributor's - Modifications are Contributor's original creation(s) and/or - Contributor has sufficient rights to grant the rights conveyed by - this License. - - 3.5. Required Notices. - You must duplicate the notice in Exhibit A in each file of the Source - Code. If it is not possible to put such notice in a particular Source - Code file due to its structure, then You must include such notice in a - location (such as a relevant directory) where a user would be likely - to look for such a notice. If You created one or more Modification(s) - You may add your name as a Contributor to the notice described in - Exhibit A. You must also duplicate this License in any documentation - for the Source Code where You describe recipients' rights or ownership - rights relating to Covered Code. You may choose to offer, and to - charge a fee for, warranty, support, indemnity or liability - obligations to one or more recipients of Covered Code. However, You - may do so only on Your own behalf, and not on behalf of the Initial - Developer or any Contributor. You must make it absolutely clear than - any such warranty, support, indemnity or liability obligation is - offered by You alone, and You hereby agree to indemnify the Initial - Developer and every Contributor for any liability incurred by the - Initial Developer or such Contributor as a result of warranty, - support, indemnity or liability terms You offer. - - 3.6. Distribution of Executable Versions. - You may distribute Covered Code in Executable form only if the - requirements of Section 3.1-3.5 have been met for that Covered Code, - and if You include a notice stating that the Source Code version of - the Covered Code is available under the terms of this License, - including a description of how and where You have fulfilled the - obligations of Section 3.2. The notice must be conspicuously included - in any notice in an Executable version, related documentation or - collateral in which You describe recipients' rights relating to the - Covered Code. You may distribute the Executable version of Covered - Code or ownership rights under a license of Your choice, which may - contain terms different from this License, provided that You are in - compliance with the terms of this License and that the license for the - Executable version does not attempt to limit or alter the recipient's - rights in the Source Code version from the rights set forth in this - License. If You distribute the Executable version under a different - license You must make it absolutely clear that any terms which differ - from this License are offered by You alone, not by the Initial - Developer or any Contributor. You hereby agree to indemnify the - Initial Developer and every Contributor for any liability incurred by - the Initial Developer or such Contributor as a result of any such - terms You offer. - - 3.7. Larger Works. - You may create a Larger Work by combining Covered Code with other code - not governed by the terms of this License and distribute the Larger - Work as a single product. In such a case, You must make sure the - requirements of this License are fulfilled for the Covered Code. - -4. Inability to Comply Due to Statute or Regulation. - - If it is impossible for You to comply with any of the terms of this - License with respect to some or all of the Covered Code due to - statute, judicial order, or regulation then You must: (a) comply with - the terms of this License to the maximum extent possible; and (b) - describe the limitations and the code they affect. Such description - must be included in the LEGAL file described in Section 3.4 and must - be included with all distributions of the Source Code. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Application of this License. - - This License applies to code to which the Initial Developer has - attached the notice in Exhibit A and to related Covered Code. - -6. Versions of the License. - - 6.1. New Versions. - Netscape Communications Corporation ("Netscape") may publish revised - and/or new versions of the License from time to time. Each version - will be given a distinguishing version number. - - 6.2. Effect of New Versions. - Once Covered Code has been published under a particular version of the - License, You may always continue to use it under the terms of that - version. You may also choose to use such Covered Code under the terms - of any subsequent version of the License published by Netscape. No one - other than Netscape has the right to modify the terms applicable to - Covered Code created under this License. - - 6.3. Derivative Works. - If You create or use a modified version of this License (which you may - only do in order to apply it to code which is not already Covered Code - governed by this License), You must (a) rename Your license so that - the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", - "MPL", "NPL" or any confusingly similar phrase do not appear in your - license (except to note that your license differs from this License) - and (b) otherwise make it clear that Your version of the license - contains terms which differ from the Mozilla Public License and - Netscape Public License. (Filling in the name of the Initial - Developer, Original Code or Contributor in the notice described in - Exhibit A shall not of themselves be deemed to be modifications of - this License.) - -7. DISCLAIMER OF WARRANTY. - - COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, - WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF - DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. - THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE - IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, - YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE - COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER - OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF - ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. - -8. TERMINATION. - - 8.1. This License and the rights granted hereunder will terminate - automatically if You fail to comply with terms herein and fail to cure - such breach within 30 days of becoming aware of the breach. All - sublicenses to the Covered Code which are properly granted shall - survive any termination of this License. Provisions which, by their - nature, must remain in effect beyond the termination of this License - shall survive. - - 8.2. If You initiate litigation by asserting a patent infringement - claim (excluding declatory judgment actions) against Initial Developer - or a Contributor (the Initial Developer or Contributor against whom - You file such action is referred to as "Participant") alleging that: - - (a) such Participant's Contributor Version directly or indirectly - infringes any patent, then any and all rights granted by such - Participant to You under Sections 2.1 and/or 2.2 of this License - shall, upon 60 days notice from Participant terminate prospectively, - unless if within 60 days after receipt of notice You either: (i) - agree in writing to pay Participant a mutually agreeable reasonable - royalty for Your past and future use of Modifications made by such - Participant, or (ii) withdraw Your litigation claim with respect to - the Contributor Version against such Participant. If within 60 days - of notice, a reasonable royalty and payment arrangement are not - mutually agreed upon in writing by the parties or the litigation claim - is not withdrawn, the rights granted by Participant to You under - Sections 2.1 and/or 2.2 automatically terminate at the expiration of - the 60 day notice period specified above. - - (b) any software, hardware, or device, other than such Participant's - Contributor Version, directly or indirectly infringes any patent, then - any rights granted to You by such Participant under Sections 2.1(b) - and 2.2(b) are revoked effective as of the date You first made, used, - sold, distributed, or had made, Modifications made by that - Participant. - - 8.3. If You assert a patent infringement claim against Participant - alleging that such Participant's Contributor Version directly or - indirectly infringes any patent where such claim is resolved (such as - by license or settlement) prior to the initiation of patent - infringement litigation, then the reasonable value of the licenses - granted by such Participant under Sections 2.1 or 2.2 shall be taken - into account in determining the amount or value of any payment or - license. - - 8.4. In the event of termination under Sections 8.1 or 8.2 above, - all end user license agreements (excluding distributors and resellers) - which have been validly granted by You or any distributor hereunder - prior to termination shall survive termination. - -9. LIMITATION OF LIABILITY. - - UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT - (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL - DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, - OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR - ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY - CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, - WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER - COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN - INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF - LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY - RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW - PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE - EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO - THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. - -10. U.S. GOVERNMENT END USERS. - - The Covered Code is a "commercial item," as that term is defined in - 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer - software" and "commercial computer software documentation," as such - terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 - C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), - all U.S. Government End Users acquire Covered Code with only those - rights set forth herein. - -11. MISCELLANEOUS. - - This License represents the complete agreement concerning subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. This License shall be governed by - California law provisions (except to the extent applicable law, if - any, provides otherwise), excluding its conflict-of-law provisions. - With respect to disputes in which at least one party is a citizen of, - or an entity chartered or registered to do business in the United - States of America, any litigation relating to this License shall be - subject to the jurisdiction of the Federal Courts of the Northern - District of California, with venue lying in Santa Clara County, - California, with the losing party responsible for costs, including - without limitation, court costs and reasonable attorneys' fees and - expenses. The application of the United Nations Convention on - Contracts for the International Sale of Goods is expressly excluded. - Any law or regulation which provides that the language of a contract - shall be construed against the drafter shall not apply to this - License. - -12. RESPONSIBILITY FOR CLAIMS. - - As between Initial Developer and the Contributors, each party is - responsible for claims and damages arising, directly or indirectly, - out of its utilization of rights under this License and You agree to - work with Initial Developer and Contributors to distribute such - responsibility on an equitable basis. Nothing herein is intended or - shall be deemed to constitute any admission of liability. - -13. MULTIPLE-LICENSED CODE. - - Initial Developer may designate portions of the Covered Code as - "Multiple-Licensed". "Multiple-Licensed" means that the Initial - Developer permits you to utilize portions of the Covered Code under - Your choice of the NPL or the alternative licenses, if any, specified - by the Initial Developer in the file described in Exhibit A. - -EXHIBIT A -Mozilla Public License. - - ``The contents of this file are subject to the Mozilla Public License - Version 1.1 (the "License"); you may not use this file except in - compliance with the License. You may obtain a copy of the License at - http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - License for the specific language governing rights and limitations - under the License. - - The Original Code is RabbitMQ. - - The Initial Developers of the Original Code are LShift Ltd, - Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd. - - Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd, - Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd - are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial - Technologies LLC, and Rabbit Technologies Ltd. - - Portions created by LShift Ltd are Copyright (C) 2007-2009 LShift - Ltd. Portions created by Cohesive Financial Technologies LLC are - Copyright (C) 2007-2009 Cohesive Financial Technologies - LLC. Portions created by Rabbit Technologies Ltd are Copyright - (C) 2007-2009 Rabbit Technologies Ltd. - - All Rights Reserved. - - Contributor(s): ______________________________________.'' - - [NOTE: The text of this Exhibit A may differ slightly from the text of - the notices in the Source Code files of the Original Code. You should - use the text of this Exhibit A rather than the text found in the - Original Code Source Code for Your Modifications.] - - - diff --git a/src/librabbitmq/amqp.h b/src/librabbitmq/amqp.h index 5cc43a8..6494902 100644 --- a/src/librabbitmq/amqp.h +++ b/src/librabbitmq/amqp.h @@ -1,350 +1,2538 @@ -#ifndef librabbitmq_amqp_h -#define librabbitmq_amqp_h +/** \file */ +/* + * ***** 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 ***** + */ + +#ifndef AMQP_H +#define AMQP_H + +/** \cond HIDE_FROM_DOXYGEN */ #ifdef __cplusplus -extern "C" { +#define AMQP_BEGIN_DECLS extern "C" { +#define AMQP_END_DECLS } +#else +#define AMQP_BEGIN_DECLS +#define AMQP_END_DECLS +#endif + +/* + * \internal + * Important API decorators: + * AMQP_PUBLIC_FUNCTION - a public API function + * AMQP_PUBLIC_VARIABLE - a public API external variable + * AMQP_CALL - calling convension (used on Win32) + */ + +#if defined(_WIN32) && defined(_MSC_VER) +#if defined(AMQP_BUILD) && !defined(AMQP_STATIC) +#define AMQP_PUBLIC_FUNCTION __declspec(dllexport) +#define AMQP_PUBLIC_VARIABLE __declspec(dllexport) extern +#else +#define AMQP_PUBLIC_FUNCTION +#if !defined(AMQP_STATIC) +#define AMQP_PUBLIC_VARIABLE __declspec(dllimport) extern +#else +#define AMQP_PUBLIC_VARIABLE extern +#endif +#endif +#define AMQP_CALL __cdecl + +#elif defined(_WIN32) && defined(__BORLANDC__) +#if defined(AMQP_BUILD) && !defined(AMQP_STATIC) +#define AMQP_PUBLIC_FUNCTION __declspec(dllexport) +#define AMQP_PUBLIC_VARIABLE __declspec(dllexport) extern +#else +#define AMQP_PUBLIC_FUNCTION +#if !defined(AMQP_STATIC) +#define AMQP_PUBLIC_VARIABLE __declspec(dllimport) extern +#else +#define AMQP_PUBLIC_VARIABLE extern +#endif +#endif +#define AMQP_CALL __cdecl + +#elif defined(_WIN32) && defined(__MINGW32__) +#if defined(AMQP_BUILD) && !defined(AMQP_STATIC) +#define AMQP_PUBLIC_FUNCTION __declspec(dllexport) +#define AMQP_PUBLIC_VARIABLE __declspec(dllexport) extern +#else +#define AMQP_PUBLIC_FUNCTION +#if !defined(AMQP_STATIC) +#define AMQP_PUBLIC_VARIABLE __declspec(dllimport) extern +#else +#define AMQP_PUBLIC_VARIABLE extern +#endif +#endif +#define AMQP_CALL __cdecl + +#elif defined(_WIN32) && defined(__CYGWIN__) +#if defined(AMQP_BUILD) && !defined(AMQP_STATIC) +#define AMQP_PUBLIC_FUNCTION __declspec(dllexport) +#define AMQP_PUBLIC_VARIABLE __declspec(dllexport) +#else +#define AMQP_PUBLIC_FUNCTION +#if !defined(AMQP_STATIC) +#define AMQP_PUBLIC_VARIABLE __declspec(dllimport) extern +#else +#define AMQP_PUBLIC_VARIABLE extern +#endif +#endif +#define AMQP_CALL __cdecl + +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define AMQP_PUBLIC_FUNCTION __attribute__((visibility("default"))) +#define AMQP_PUBLIC_VARIABLE __attribute__((visibility("default"))) extern +#define AMQP_CALL +#else +#define AMQP_PUBLIC_FUNCTION +#define AMQP_PUBLIC_VARIABLE extern +#define AMQP_CALL +#endif + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +#define AMQP_DEPRECATED(function) function __attribute__((__deprecated__)) +#elif defined(_MSC_VER) +#define AMQP_DEPRECATED(function) __declspec(deprecated) function +#else +#define AMQP_DEPRECATED(function) +#endif + +/* Define ssize_t on Win32/64 platforms + See: http://lists.cs.uiuc.edu/pipermail/llvmdev/2010-April/030649.html for + details + */ +#if !defined(_W64) +#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +#define _W64 __w64 +#else +#define _W64 +#endif +#endif + +#ifdef _MSC_VER +#ifdef _WIN64 +typedef __int64 ssize_t; +#else +typedef _W64 int ssize_t; +#endif +#endif + +#if defined(_WIN32) && defined(__MINGW32__) +#include +#endif + +/** \endcond */ + +#include +#include + +struct timeval; + +AMQP_BEGIN_DECLS + +/** + * \def AMQP_VERSION_MAJOR + * + * Major library version number compile-time constant + * + * The major version is incremented when backwards incompatible API changes + * are made. + * + * \sa AMQP_VERSION, AMQP_VERSION_STRING + * + * \since v0.4.0 + */ + +/** + * \def AMQP_VERSION_MINOR + * + * Minor library version number compile-time constant + * + * The minor version is incremented when new APIs are added. Existing APIs + * are left alone. + * + * \sa AMQP_VERSION, AMQP_VERSION_STRING + * + * \since v0.4.0 + */ + +/** + * \def AMQP_VERSION_PATCH + * + * Patch library version number compile-time constant + * + * The patch version is incremented when library code changes, but the API + * is not changed. + * + * \sa AMQP_VERSION, AMQP_VERSION_STRING + * + * \since v0.4.0 + */ + +/** + * \def AMQP_VERSION_IS_RELEASE + * + * Version constant set to 1 for tagged release, 0 otherwise + * + * NOTE: versions that are not tagged releases are not guaranteed to be API/ABI + * compatible with older releases, and may change commit-to-commit. + * + * \sa AMQP_VERSION, AMQP_VERSION_STRING + * + * \since v0.4.0 + */ +/* + * Developer note: when changing these, be sure to update SOVERSION constants + * in CMakeLists.txt and configure.ac + */ + +#define AMQP_VERSION_MAJOR 0 +#define AMQP_VERSION_MINOR 10 +#define AMQP_VERSION_PATCH 0 +#define AMQP_VERSION_IS_RELEASE 0 + +/** + * \def AMQP_VERSION_CODE + * + * Helper macro to geneate a packed version code suitable for + * comparison with AMQP_VERSION. + * + * \sa amqp_version_number() AMQP_VERSION_MAJOR, AMQP_VERSION_MINOR, + * AMQP_VERSION_PATCH, AMQP_VERSION_IS_RELEASE, AMQP_VERSION + * + * \since v0.6.1 + */ +#define AMQP_VERSION_CODE(major, minor, patch, release) \ + ((major << 24) | (minor << 16) | (patch << 8) | (release)) + +/** + * \def AMQP_VERSION + * + * Packed version number + * + * AMQP_VERSION is a 4-byte unsigned integer with the most significant byte + * set to AMQP_VERSION_MAJOR, the second most significant byte set to + * AMQP_VERSION_MINOR, third most significant byte set to AMQP_VERSION_PATCH, + * and the lowest byte set to AMQP_VERSION_IS_RELEASE. + * + * For example version 2.3.4 which is released version would be encoded as + * 0x02030401 + * + * \sa amqp_version_number() AMQP_VERSION_MAJOR, AMQP_VERSION_MINOR, + * AMQP_VERSION_PATCH, AMQP_VERSION_IS_RELEASE, AMQP_VERSION_CODE + * + * \since v0.4.0 + */ +#define AMQP_VERSION \ + AMQP_VERSION_CODE(AMQP_VERSION_MAJOR, AMQP_VERSION_MINOR, \ + AMQP_VERSION_PATCH, AMQP_VERSION_IS_RELEASE) + +/** \cond HIDE_FROM_DOXYGEN */ +#define AMQ_STRINGIFY(s) AMQ_STRINGIFY_HELPER(s) +#define AMQ_STRINGIFY_HELPER(s) #s + +#define AMQ_VERSION_STRING \ + AMQ_STRINGIFY(AMQP_VERSION_MAJOR) \ + "." AMQ_STRINGIFY(AMQP_VERSION_MINOR) "." AMQ_STRINGIFY(AMQP_VERSION_PATCH) +/** \endcond */ + +/** + * \def AMQP_VERSION_STRING + * + * Version string compile-time constant + * + * Non-released versions of the library will have "-pre" appended to the + * version string + * + * \sa amqp_version() + * + * \since v0.4.0 + */ +#if AMQP_VERSION_IS_RELEASE +#define AMQP_VERSION_STRING AMQ_VERSION_STRING +#else +#define AMQP_VERSION_STRING AMQ_VERSION_STRING "-pre" #endif +/** + * Returns the rabbitmq-c version as a packed integer. + * + * See \ref AMQP_VERSION + * + * \return packed 32-bit integer representing version of library at runtime + * + * \sa AMQP_VERSION, amqp_version() + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +uint32_t AMQP_CALL amqp_version_number(void); + +/** + * Returns the rabbitmq-c version as a string. + * + * See \ref AMQP_VERSION_STRING + * + * \return a statically allocated string describing the version of rabbitmq-c. + * + * \sa amqp_version_number(), AMQP_VERSION_STRING, AMQP_VERSION + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +char const *AMQP_CALL amqp_version(void); + +/** + * \def AMQP_DEFAULT_FRAME_SIZE + * + * Default frame size (128Kb) + * + * \sa amqp_login(), amqp_login_with_properties() + * + * \since v0.4.0 + */ +#define AMQP_DEFAULT_FRAME_SIZE 131072 + +/** + * \def AMQP_DEFAULT_MAX_CHANNELS + * + * Default maximum number of channels (2047, RabbitMQ default limit of 2048, + * minus 1 for channel 0). RabbitMQ set a default limit of 2048 channels per + * connection in v3.7.5 to prevent broken clients from leaking too many + * channels. + * + * \sa amqp_login(), amqp_login_with_properties() + * + * \since v0.4.0 + */ +#define AMQP_DEFAULT_MAX_CHANNELS 2047 + +/** + * \def AMQP_DEFAULT_HEARTBEAT + * + * Default heartbeat interval (0, heartbeat disabled) + * + * \sa amqp_login(), amqp_login_with_properties() + * + * \since v0.4.0 + */ +#define AMQP_DEFAULT_HEARTBEAT 0 + +/** + * \def AMQP_DEFAULT_VHOST + * + * Default RabbitMQ vhost: "/" + * + * \sa amqp_login(), amqp_login_with_properties() + * + * \since v0.9.0 + */ +#define AMQP_DEFAULT_VHOST "/" + +/** + * boolean type 0 = false, true otherwise + * + * \since v0.1 + */ typedef int amqp_boolean_t; + +/** + * Method number + * + * \since v0.1 + */ typedef uint32_t amqp_method_number_t; + +/** + * Bitmask for flags + * + * \since v0.1 + */ typedef uint32_t amqp_flags_t; + +/** + * Channel type + * + * \since v0.1 + */ typedef uint16_t amqp_channel_t; +/** + * Buffer descriptor + * + * \since v0.1 + */ typedef struct amqp_bytes_t_ { - size_t len; - void *bytes; + size_t len; /**< length of the buffer in bytes */ + void *bytes; /**< pointer to the beginning of the buffer */ } amqp_bytes_t; -#define AMQP_EMPTY_BYTES ((amqp_bytes_t) { .len = 0, .bytes = NULL }) - +/** + * Decimal data type + * + * \since v0.1 + */ typedef struct amqp_decimal_t_ { - int decimals; - uint32_t value; + uint8_t decimals; /**< the location of the decimal point */ + uint32_t value; /**< the value before the decimal point is applied */ } amqp_decimal_t; -#define AMQP_DECIMAL(d,v) ((amqp_decimal_t) { .decimals = (d), .value = (v) }) - +/** + * AMQP field table + * + * An AMQP field table is a set of key-value pairs. + * A key is a UTF-8 encoded string up to 128 bytes long, and are not null + * terminated. + * A value can be one of several different datatypes. \sa + * amqp_field_value_kind_t + * + * \sa amqp_table_entry_t + * + * \since v0.1 + */ typedef struct amqp_table_t_ { - int num_entries; - struct amqp_table_entry_t_ *entries; + int num_entries; /**< length of entries array */ + struct amqp_table_entry_t_ *entries; /**< an array of table entries */ } amqp_table_t; -#define AMQP_EMPTY_TABLE ((amqp_table_t) { .num_entries = 0, .entries = NULL }) +/** + * An AMQP Field Array + * + * A repeated set of field values, all must be of the same type + * + * \since v0.1 + */ +typedef struct amqp_array_t_ { + int num_entries; /**< Number of entries in the table */ + struct amqp_field_value_t_ *entries; /**< linked list of field values */ +} amqp_array_t; -typedef struct amqp_table_entry_t_ { - amqp_bytes_t key; - char kind; +/* + 0-9 0-9-1 Qpid/Rabbit Type Remarks +--------------------------------------------------------------------------- + t t Boolean + b b Signed 8-bit + B Unsigned 8-bit + U s Signed 16-bit (A1) + u Unsigned 16-bit + I I I Signed 32-bit + i Unsigned 32-bit + L l Signed 64-bit (B) + l Unsigned 64-bit + f f 32-bit float + d d 64-bit float + D D D Decimal + s Short string (A2) + S S S Long string + A Nested Array + T T T Timestamp (u64) + F F F Nested Table + V V V Void + x Byte array + +Remarks: + + A1, A2: Notice how the types **CONFLICT** here. In Qpid and Rabbit, + 's' means a signed 16-bit integer; in 0-9-1, it means a + short string. + + B: Notice how the signednesses **CONFLICT** here. In Qpid and Rabbit, + 'l' means a signed 64-bit integer; in 0-9-1, it means an unsigned + 64-bit integer. + +I'm going with the Qpid/Rabbit types, where there's a conflict, and +the 0-9-1 types otherwise. 0-8 is a subset of 0-9, which is a subset +of the other two, so this will work for both 0-8 and 0-9-1 branches of +the code. +*/ + +/** + * A field table value + * + * \since v0.1 + */ +typedef struct amqp_field_value_t_ { + uint8_t kind; /**< the type of the entry /sa amqp_field_value_kind_t */ union { - amqp_bytes_t bytes; - int32_t i32; - amqp_decimal_t decimal; - uint64_t u64; - amqp_table_t table; - } value; + amqp_boolean_t boolean; /**< boolean type AMQP_FIELD_KIND_BOOLEAN */ + int8_t i8; /**< int8_t type AMQP_FIELD_KIND_I8 */ + uint8_t u8; /**< uint8_t type AMQP_FIELD_KIND_U8 */ + int16_t i16; /**< int16_t type AMQP_FIELD_KIND_I16 */ + uint16_t u16; /**< uint16_t type AMQP_FIELD_KIND_U16 */ + int32_t i32; /**< int32_t type AMQP_FIELD_KIND_I32 */ + uint32_t u32; /**< uint32_t type AMQP_FIELD_KIND_U32 */ + int64_t i64; /**< int64_t type AMQP_FIELD_KIND_I64 */ + uint64_t u64; /**< uint64_t type AMQP_FIELD_KIND_U64, + AMQP_FIELD_KIND_TIMESTAMP */ + float f32; /**< float type AMQP_FIELD_KIND_F32 */ + double f64; /**< double type AMQP_FIELD_KIND_F64 */ + amqp_decimal_t decimal; /**< amqp_decimal_t AMQP_FIELD_KIND_DECIMAL */ + amqp_bytes_t bytes; /**< amqp_bytes_t type AMQP_FIELD_KIND_UTF8, + AMQP_FIELD_KIND_BYTES */ + amqp_table_t table; /**< amqp_table_t type AMQP_FIELD_KIND_TABLE */ + amqp_array_t array; /**< amqp_array_t type AMQP_FIELD_KIND_ARRAY */ + } value; /**< a union of the value */ +} amqp_field_value_t; + +/** + * An entry in a field-table + * + * \sa amqp_table_encode(), amqp_table_decode(), amqp_table_clone() + * + * \since v0.1 + */ +typedef struct amqp_table_entry_t_ { + amqp_bytes_t key; /**< the table entry key. Its a null-terminated UTF-8 + * string, with a maximum size of 128 bytes */ + amqp_field_value_t value; /**< the table entry values */ } amqp_table_entry_t; -#define _AMQP_TE_INIT(ke,ki,v) { .key = (ke), .kind = (ki), .value = { v } } -#define AMQP_TABLE_ENTRY_S(k,v) _AMQP_TE_INIT(amqp_cstring_bytes(k), 'S', .bytes = (v)) -#define AMQP_TABLE_ENTRY_I(k,v) _AMQP_TE_INIT(amqp_cstring_bytes(k), 'I', .i32 = (v)) -#define AMQP_TABLE_ENTRY_D(k,v) _AMQP_TE_INIT(amqp_cstring_bytes(k), 'D', .decimal = (v)) -#define AMQP_TABLE_ENTRY_T(k,v) _AMQP_TE_INIT(amqp_cstring_bytes(k), 'T', .u64 = (v)) -#define AMQP_TABLE_ENTRY_F(k,v) _AMQP_TE_INIT(amqp_cstring_bytes(k), 'F', .table = (v)) - +/** + * Field value types + * + * \since v0.1 + */ +typedef enum { + AMQP_FIELD_KIND_BOOLEAN = + 't', /**< boolean type. 0 = false, 1 = true @see amqp_boolean_t */ + AMQP_FIELD_KIND_I8 = 'b', /**< 8-bit signed integer, datatype: int8_t */ + AMQP_FIELD_KIND_U8 = 'B', /**< 8-bit unsigned integer, datatype: uint8_t */ + AMQP_FIELD_KIND_I16 = 's', /**< 16-bit signed integer, datatype: int16_t */ + AMQP_FIELD_KIND_U16 = 'u', /**< 16-bit unsigned integer, datatype: uint16_t */ + AMQP_FIELD_KIND_I32 = 'I', /**< 32-bit signed integer, datatype: int32_t */ + AMQP_FIELD_KIND_U32 = 'i', /**< 32-bit unsigned integer, datatype: uint32_t */ + AMQP_FIELD_KIND_I64 = 'l', /**< 64-bit signed integer, datatype: int64_t */ + AMQP_FIELD_KIND_U64 = 'L', /**< 64-bit unsigned integer, datatype: uint64_t */ + AMQP_FIELD_KIND_F32 = + 'f', /**< single-precision floating point value, datatype: float */ + AMQP_FIELD_KIND_F64 = + 'd', /**< double-precision floating point value, datatype: double */ + AMQP_FIELD_KIND_DECIMAL = + 'D', /**< amqp-decimal value, datatype: amqp_decimal_t */ + AMQP_FIELD_KIND_UTF8 = 'S', /**< UTF-8 null-terminated character string, + datatype: amqp_bytes_t */ + AMQP_FIELD_KIND_ARRAY = 'A', /**< field array (repeated values of another + datatype. datatype: amqp_array_t */ + AMQP_FIELD_KIND_TIMESTAMP = 'T', /**< 64-bit timestamp. datatype uint64_t */ + AMQP_FIELD_KIND_TABLE = 'F', /**< field table. encapsulates a table inside a + table entry. datatype: amqp_table_t */ + AMQP_FIELD_KIND_VOID = 'V', /**< empty entry */ + AMQP_FIELD_KIND_BYTES = + 'x' /**< unformatted byte string, datatype: amqp_bytes_t */ +} amqp_field_value_kind_t; + +/** + * A list of allocation blocks + * + * \since v0.1 + */ typedef struct amqp_pool_blocklist_t_ { - int num_blocks; - void **blocklist; + int num_blocks; /**< Number of blocks in the block list */ + void **blocklist; /**< Array of memory blocks */ } amqp_pool_blocklist_t; +/** + * A memory pool + * + * \since v0.1 + */ typedef struct amqp_pool_t_ { - size_t pagesize; - - amqp_pool_blocklist_t pages; - amqp_pool_blocklist_t large_blocks; - - int next_page; - char *alloc_block; - size_t alloc_used; + size_t pagesize; /**< the size of the page in bytes. Allocations less than or + * equal to this size are allocated in the pages block list. + * Allocations greater than this are allocated in their own + * own block in the large_blocks block list */ + + amqp_pool_blocklist_t pages; /**< blocks that are the size of pagesize */ + amqp_pool_blocklist_t + large_blocks; /**< allocations larger than the pagesize */ + + int next_page; /**< an index to the next unused page block */ + char *alloc_block; /**< pointer to the current allocation block */ + size_t alloc_used; /**< number of bytes in the current allocation block that + has been used */ } amqp_pool_t; +/** + * An amqp method + * + * \since v0.1 + */ typedef struct amqp_method_t_ { - amqp_method_number_t id; - void *decoded; + amqp_method_number_t id; /**< the method id number */ + void *decoded; /**< pointer to the decoded method, + * cast to the appropriate type to use */ } amqp_method_t; +/** + * An AMQP frame + * + * \since v0.1 + */ typedef struct amqp_frame_t_ { - uint8_t frame_type; /* 0 means no event */ - amqp_channel_t channel; + uint8_t frame_type; /**< frame type. The types: + * - AMQP_FRAME_METHOD - use the method union member + * - AMQP_FRAME_HEADER - use the properties union member + * - AMQP_FRAME_BODY - use the body_fragment union member + */ + amqp_channel_t channel; /**< the channel the frame was received on */ union { - amqp_method_t method; + amqp_method_t + method; /**< a method, use if frame_type == AMQP_FRAME_METHOD */ struct { - uint16_t class_id; - uint64_t body_size; - void *decoded; - } properties; - amqp_bytes_t body_fragment; + uint16_t class_id; /**< the class for the properties */ + uint64_t body_size; /**< size of the body in bytes */ + void *decoded; /**< the decoded properties */ + amqp_bytes_t raw; /**< amqp-encoded properties structure */ + } properties; /**< message header, a.k.a., properties, + use if frame_type == AMQP_FRAME_HEADER */ + amqp_bytes_t body_fragment; /**< a body fragment, use if frame_type == + AMQP_FRAME_BODY */ struct { - uint8_t transport_high; - uint8_t transport_low; - uint8_t protocol_version_major; - uint8_t protocol_version_minor; - } protocol_header; - } payload; + uint8_t transport_high; /**< @internal first byte of handshake */ + uint8_t transport_low; /**< @internal second byte of handshake */ + uint8_t protocol_version_major; /**< @internal third byte of handshake */ + uint8_t protocol_version_minor; /**< @internal fourth byte of handshake */ + } protocol_header; /**< Used only when doing the initial handshake with the + broker, don't use otherwise */ + } payload; /**< the payload of the frame */ } amqp_frame_t; +/** + * Response type + * + * \since v0.1 + */ typedef enum amqp_response_type_enum_ { - AMQP_RESPONSE_NONE = 0, - AMQP_RESPONSE_NORMAL, - AMQP_RESPONSE_LIBRARY_EXCEPTION, - AMQP_RESPONSE_SERVER_EXCEPTION + AMQP_RESPONSE_NONE = 0, /**< the library got an EOF from the socket */ + AMQP_RESPONSE_NORMAL, /**< response normal, the RPC completed successfully */ + AMQP_RESPONSE_LIBRARY_EXCEPTION, /**< library error, an error occurred in the + library, examine the library_error */ + AMQP_RESPONSE_SERVER_EXCEPTION /**< server exception, the broker returned an + error, check replay */ } amqp_response_type_enum; +/** + * Reply from a RPC method on the broker + * + * \since v0.1 + */ typedef struct amqp_rpc_reply_t_ { - amqp_response_type_enum reply_type; - amqp_method_t reply; - int library_errno; /* if AMQP_RESPONSE_LIBRARY_EXCEPTION, then 0 here means socket EOF */ + amqp_response_type_enum reply_type; /**< the reply type: + * - AMQP_RESPONSE_NORMAL - the RPC + * completed successfully + * - AMQP_RESPONSE_SERVER_EXCEPTION - the + * broker returned + * an exception, check the reply field + * - AMQP_RESPONSE_LIBRARY_EXCEPTION - the + * library + * encountered an error, check the + * library_error field + */ + amqp_method_t reply; /**< in case of AMQP_RESPONSE_SERVER_EXCEPTION this + * field will be set to the method returned from the + * broker */ + int library_error; /**< in case of AMQP_RESPONSE_LIBRARY_EXCEPTION this + * field will be set to an error code. An error + * string can be retrieved using amqp_error_string */ } amqp_rpc_reply_t; +/** + * SASL method type + * + * \since v0.1 + */ typedef enum amqp_sasl_method_enum_ { - AMQP_SASL_METHOD_PLAIN = 0 + AMQP_SASL_METHOD_UNDEFINED = -1, /**< Invalid SASL method */ + AMQP_SASL_METHOD_PLAIN = + 0, /**< the PLAIN SASL method for authentication to the broker */ + AMQP_SASL_METHOD_EXTERNAL = + 1 /**< the EXTERNAL SASL method for authentication to the broker */ } amqp_sasl_method_enum; -#define AMQP_PSEUDOFRAME_PROTOCOL_HEADER ((uint8_t) 'A') -#define AMQP_PSEUDOFRAME_PROTOCOL_CHANNEL ((amqp_channel_t) ((((int) 'M') << 8) | ((int) 'Q'))) +/** + * connection state object + * + * \since v0.1 + */ +typedef struct amqp_connection_state_t_ *amqp_connection_state_t; + +/** + * Socket object + * + * \since v0.4.0 + */ +typedef struct amqp_socket_t_ amqp_socket_t; -typedef struct amqp_basic_return_t_ amqp_basic_return_t; -typedef int (*amqp_output_fn_t)(void *context, void *buffer, size_t count); -typedef void (*amqp_basic_return_fn_t)(amqp_channel_t, amqp_basic_return_t *, - void *); +/** + * Status codes + * + * \since v0.4.0 + */ +/* NOTE: When updating this enum, update the strings in librabbitmq/amqp_api.c + */ +typedef enum amqp_status_enum_ { + AMQP_STATUS_OK = 0x0, /**< Operation successful */ + AMQP_STATUS_NO_MEMORY = -0x0001, /**< Memory allocation + failed */ + AMQP_STATUS_BAD_AMQP_DATA = -0x0002, /**< Incorrect or corrupt + data was received from + the broker. This is a + protocol error. */ + AMQP_STATUS_UNKNOWN_CLASS = -0x0003, /**< An unknown AMQP class + was received. This is + a protocol error. */ + AMQP_STATUS_UNKNOWN_METHOD = -0x0004, /**< An unknown AMQP method + was received. This is + a protocol error. */ + AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED = -0x0005, /**< Unable to resolve the + * hostname */ + AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION = -0x0006, /**< The broker advertised + an incompaible AMQP + version */ + AMQP_STATUS_CONNECTION_CLOSED = -0x0007, /**< The connection to the + broker has been closed + */ + AMQP_STATUS_BAD_URL = -0x0008, /**< malformed AMQP URL */ + AMQP_STATUS_SOCKET_ERROR = -0x0009, /**< A socket error + occurred */ + AMQP_STATUS_INVALID_PARAMETER = -0x000A, /**< An invalid parameter + was passed into the + function */ + AMQP_STATUS_TABLE_TOO_BIG = -0x000B, /**< The amqp_table_t object + cannot be serialized + because the output + buffer is too small */ + AMQP_STATUS_WRONG_METHOD = -0x000C, /**< The wrong method was + received */ + AMQP_STATUS_TIMEOUT = -0x000D, /**< Operation timed out */ + AMQP_STATUS_TIMER_FAILURE = -0x000E, /**< The underlying system + timer facility failed */ + AMQP_STATUS_HEARTBEAT_TIMEOUT = -0x000F, /**< Timed out waiting for + heartbeat */ + AMQP_STATUS_UNEXPECTED_STATE = -0x0010, /**< Unexpected protocol + state */ + AMQP_STATUS_SOCKET_CLOSED = -0x0011, /**< Underlying socket is + closed */ + AMQP_STATUS_SOCKET_INUSE = -0x0012, /**< Underlying socket is + already open */ + AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD = -0x0013, /**< Broker does not + support the requested + SASL mechanism */ + AMQP_STATUS_UNSUPPORTED = -0x0014, /**< Parameter is unsupported + in this version */ + _AMQP_STATUS_NEXT_VALUE = -0x0015, /**< Internal value */ + + AMQP_STATUS_TCP_ERROR = -0x0100, /**< A generic TCP error + occurred */ + AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR = -0x0101, /**< An error occurred trying + to initialize the + socket library*/ + _AMQP_STATUS_TCP_NEXT_VALUE = -0x0102, /**< Internal value */ + + AMQP_STATUS_SSL_ERROR = -0x0200, /**< A generic SSL error + occurred. */ + AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED = -0x0201, /**< SSL validation of + hostname against + peer certificate + failed */ + AMQP_STATUS_SSL_PEER_VERIFY_FAILED = -0x0202, /**< SSL validation of peer + certificate failed. */ + AMQP_STATUS_SSL_CONNECTION_FAILED = -0x0203, /**< SSL handshake failed. */ + _AMQP_STATUS_SSL_NEXT_VALUE = -0x0204 /**< Internal value */ +} amqp_status_enum; + +/** + * AMQP delivery modes. + * Use these values for the #amqp_basic_properties_t::delivery_mode field. + * + * \since v0.5 + */ +typedef enum { + AMQP_DELIVERY_NONPERSISTENT = 1, /**< Non-persistent message */ + AMQP_DELIVERY_PERSISTENT = 2 /**< Persistent message */ +} amqp_delivery_mode_enum; +AMQP_END_DECLS -/* Opaque struct. */ -typedef struct amqp_connection_state_t_ *amqp_connection_state_t; +#include "amqp_framing.h" -extern char const *amqp_version(void); - -extern void init_amqp_pool(amqp_pool_t *pool, size_t pagesize); -extern void recycle_amqp_pool(amqp_pool_t *pool); -extern void empty_amqp_pool(amqp_pool_t *pool); - -extern void *amqp_pool_alloc(amqp_pool_t *pool, size_t amount); -extern void amqp_pool_alloc_bytes(amqp_pool_t *pool, size_t amount, amqp_bytes_t *output); - -extern amqp_bytes_t amqp_cstring_bytes(char const *cstr); -extern amqp_bytes_t amqp_bytes_malloc_dup(amqp_bytes_t src); - -#define AMQP_BYTES_FREE(b) \ - ({ \ - if ((b).bytes != NULL) { \ - free((b).bytes); \ - (b).bytes = NULL; \ - } \ - }) - -extern amqp_connection_state_t amqp_new_connection(void); -extern int amqp_get_sockfd(amqp_connection_state_t state); -extern void amqp_set_sockfd(amqp_connection_state_t state, - int sockfd); -extern void amqp_set_basic_return_cb(amqp_connection_state_t state, - amqp_basic_return_fn_t fn, - void *data); -extern int amqp_tune_connection(amqp_connection_state_t state, - int channel_max, - int frame_max, - int heartbeat); -int amqp_get_channel_max(amqp_connection_state_t state); -extern void amqp_destroy_connection(amqp_connection_state_t state); - -extern int amqp_handle_input(amqp_connection_state_t state, - amqp_bytes_t received_data, - amqp_frame_t *decoded_frame); - -extern amqp_boolean_t amqp_release_buffers_ok(amqp_connection_state_t state); - -extern void amqp_release_buffers(amqp_connection_state_t state); - -extern void amqp_maybe_release_buffers(amqp_connection_state_t state); - -extern int amqp_send_frame(amqp_connection_state_t state, - amqp_frame_t const *frame); -extern int amqp_send_frame_to(amqp_connection_state_t state, - amqp_frame_t const *frame, - amqp_output_fn_t fn, - void *context); - -extern int amqp_table_entry_cmp(void const *entry1, void const *entry2); - -extern int amqp_open_socket(char const *hostname, int portnumber, - struct timeval *timeout); - -extern int amqp_send_header(amqp_connection_state_t state); -extern int amqp_send_header_to(amqp_connection_state_t state, - amqp_output_fn_t fn, - void *context); - -extern amqp_boolean_t amqp_frames_enqueued(amqp_connection_state_t state); - -extern int amqp_simple_wait_frame(amqp_connection_state_t state, - amqp_frame_t *decoded_frame); - -extern int amqp_simple_wait_method(amqp_connection_state_t state, - amqp_channel_t expected_channel, - amqp_method_number_t expected_method, - amqp_method_t *output); - -extern int amqp_send_method(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_method_number_t id, - void *decoded); - -extern amqp_rpc_reply_t amqp_simple_rpc(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_method_number_t request_id, - amqp_method_number_t *expected_reply_ids, - void *decoded_request_method); - -#define AMQP_EXPAND_METHOD(classname, methodname) (AMQP_ ## classname ## _ ## methodname ## _METHOD) - -#define AMQP_SIMPLE_RPC(state, channel, classname, requestname, replyname, structname, ...) \ - ({ \ - structname _simple_rpc_request___ = (structname) { __VA_ARGS__ }; \ - amqp_method_number_t _replies__[2] = { AMQP_EXPAND_METHOD(classname, replyname), 0}; \ - amqp_simple_rpc(state, channel, \ - AMQP_EXPAND_METHOD(classname, requestname), \ - (amqp_method_number_t *)&_replies__, \ - &_simple_rpc_request___); \ - }) - -#define AMQP_MULTIPLE_RESPONSE_RPC(state, channel, classname, requestname, replynames, structname, ...) \ - ({ \ - structname _simple_rpc_request___ = (structname) { __VA_ARGS__ }; \ - amqp_simple_rpc(state, channel, \ - AMQP_EXPAND_METHOD(classname, requestname), \ - replynames, \ - &_simple_rpc_request___); \ - }) - - -extern amqp_rpc_reply_t amqp_login(amqp_connection_state_t state, - char const *vhost, - int channel_max, - int frame_max, - int heartbeat, - amqp_sasl_method_enum sasl_method, ...); - -extern struct amqp_channel_open_ok_t_ *amqp_channel_open(amqp_connection_state_t state, - amqp_channel_t channel); +AMQP_BEGIN_DECLS -struct amqp_basic_properties_t_; -extern 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, - struct amqp_basic_properties_t_ const *properties, - amqp_bytes_t body); - -extern amqp_rpc_reply_t amqp_channel_close(amqp_connection_state_t state, - amqp_channel_t channel, - int code); -extern amqp_rpc_reply_t amqp_connection_close(amqp_connection_state_t state, - int code); - -extern struct amqp_exchange_declare_ok_t_ *amqp_exchange_declare(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t exchange, - amqp_bytes_t type, - amqp_boolean_t passive, - amqp_boolean_t durable, - amqp_boolean_t auto_delete, - amqp_table_t arguments); - -extern struct amqp_exchange_delete_ok_t_ *amqp_exchange_delete(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t exchange, - amqp_boolean_t if_unused, - amqp_boolean_t nowait); - -extern struct amqp_queue_declare_ok_t_ *amqp_queue_declare(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t queue, - amqp_boolean_t passive, - amqp_boolean_t durable, - amqp_boolean_t exclusive, - amqp_boolean_t auto_delete, - amqp_table_t arguments); - -extern struct amqp_queue_bind_ok_t_ *amqp_queue_bind(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t queue, - amqp_bytes_t exchange, - amqp_bytes_t routing_key, - amqp_table_t arguments); - -extern struct amqp_queue_unbind_ok_t_ *amqp_queue_unbind(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t queue, - amqp_bytes_t exchange, - amqp_bytes_t binding_key, - amqp_table_t arguments); - -extern struct amqp_basic_consume_ok_t_ *amqp_basic_consume(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t queue, - amqp_bytes_t consumer_tag, - amqp_boolean_t no_local, - amqp_boolean_t no_ack, - amqp_boolean_t exclusive); - -extern int amqp_basic_ack(amqp_connection_state_t state, - amqp_channel_t channel, - uint64_t delivery_tag, - amqp_boolean_t multiple); - -extern 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); - -extern struct amqp_queue_purge_ok_t_ *amqp_queue_purge(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t queue, - amqp_boolean_t no_wait); - -extern struct amqp_tx_select_ok_t_ *amqp_tx_select(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_table_t arguments); - -extern struct amqp_tx_commit_ok_t_ *amqp_tx_commit(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_table_t arguments); - -extern struct amqp_tx_rollback_ok_t_ *amqp_tx_rollback(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_table_t arguments); - -extern struct amqp_basic_qos_ok_t_ *amqp_basic_qos(amqp_connection_state_t state, - amqp_channel_t channel, - uint32_t prefetch_size, - uint16_t prefetch_count, - amqp_boolean_t global); +/** + * Empty bytes structure + * + * \since v0.2 + */ +AMQP_PUBLIC_VARIABLE const amqp_bytes_t amqp_empty_bytes; -/* - * Can be used to see if there is data still in the buffer, if so - * calling amqp_simple_wait_frame will not immediately enter a - * blocking read. +/** + * Empty table structure * - * Possibly amqp_frames_enqueued should be used for this? + * \since v0.2 */ -extern amqp_boolean_t amqp_data_in_buffer(amqp_connection_state_t state); +AMQP_PUBLIC_VARIABLE const amqp_table_t amqp_empty_table; -/* - * Expose amqp_rpc_reply to libraries. +/** + * Empty table array structure + * + * \since v0.2 */ -extern amqp_rpc_reply_t *amqp_get_rpc_reply(void); +AMQP_PUBLIC_VARIABLE const amqp_array_t amqp_empty_array; -#ifdef __cplusplus -} -#endif +/* Compatibility macros for the above, to avoid the need to update + code written against earlier versions of librabbitmq. */ -#endif +/** + * \def AMQP_EMPTY_BYTES + * + * Deprecated, use \ref amqp_empty_bytes instead + * + * \deprecated use \ref amqp_empty_bytes instead + * + * \since v0.1 + */ +#define AMQP_EMPTY_BYTES amqp_empty_bytes + +/** + * \def AMQP_EMPTY_TABLE + * + * Deprecated, use \ref amqp_empty_table instead + * + * \deprecated use \ref amqp_empty_table instead + * + * \since v0.1 + */ +#define AMQP_EMPTY_TABLE amqp_empty_table + +/** + * \def AMQP_EMPTY_ARRAY + * + * Deprecated, use \ref amqp_empty_array instead + * + * \deprecated use \ref amqp_empty_array instead + * + * \since v0.1 + */ +#define AMQP_EMPTY_ARRAY amqp_empty_array + +/** + * Initializes an amqp_pool_t memory allocation pool for use + * + * Readies an allocation pool for use. An amqp_pool_t + * must be initialized before use + * + * \param [in] pool the amqp_pool_t structure to initialize. + * Calling this function on a pool a pool that has + * already been initialized will result in undefined + * behavior + * \param [in] pagesize the unit size that the pool will allocate + * memory chunks in. Anything allocated against the pool + * with a requested size will be carved out of a block + * this size. Allocations larger than this will be + * allocated individually + * + * \sa recycle_amqp_pool(), empty_amqp_pool(), amqp_pool_alloc(), + * amqp_pool_alloc_bytes(), amqp_pool_t + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL init_amqp_pool(amqp_pool_t *pool, size_t pagesize); + +/** + * Recycles an amqp_pool_t memory allocation pool + * + * Recycles the space allocate by the pool + * + * This invalidates all allocations made against the pool before this call is + * made, any use of any allocations made before recycle_amqp_pool() is called + * will result in undefined behavior. + * + * Note: this may or may not release memory, to force memory to be released + * call empty_amqp_pool(). + * + * \param [in] pool the amqp_pool_t to recycle + * + * \sa recycle_amqp_pool(), empty_amqp_pool(), amqp_pool_alloc(), + * amqp_pool_alloc_bytes() + * + * \since v0.1 + * + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL recycle_amqp_pool(amqp_pool_t *pool); + +/** + * Empties an amqp memory pool + * + * Releases all memory associated with an allocation pool + * + * \param [in] pool the amqp_pool_t to empty + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL empty_amqp_pool(amqp_pool_t *pool); + +/** + * Allocates a block of memory from an amqp_pool_t memory pool + * + * Memory will be aligned on a 8-byte boundary. If a 0-length allocation is + * requested, a NULL pointer will be returned. + * + * \param [in] pool the allocation pool to allocate the memory from + * \param [in] amount the size of the allocation in bytes. + * \return a pointer to the memory block, or NULL if the allocation cannot + * be satisfied. + * + * \sa init_amqp_pool(), recycle_amqp_pool(), empty_amqp_pool(), + * amqp_pool_alloc_bytes() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void *AMQP_CALL amqp_pool_alloc(amqp_pool_t *pool, size_t amount); + +/** + * Allocates a block of memory from an amqp_pool_t to an amqp_bytes_t + * + * Memory will be aligned on a 8-byte boundary. If a 0-length allocation is + * requested, output.bytes = NULL. + * + * \param [in] pool the allocation pool to allocate the memory from + * \param [in] amount the size of the allocation in bytes + * \param [in] output the location to store the pointer. On success + * output.bytes will be set to the beginning of the buffer + * output.len will be set to amount + * On error output.bytes will be set to NULL and output.len + * set to 0 + * + * \sa init_amqp_pool(), recycle_amqp_pool(), empty_amqp_pool(), + * amqp_pool_alloc() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL amqp_pool_alloc_bytes(amqp_pool_t *pool, size_t amount, + amqp_bytes_t *output); + +/** + * Wraps a c string in an amqp_bytes_t + * + * Takes a string, calculates its length and creates an + * amqp_bytes_t that points to it. The string is not duplicated. + * + * For a given input cstr, The amqp_bytes_t output.bytes is the + * same as cstr, output.len is the length of the string not including + * the \0 terminator + * + * This function uses strlen() internally so cstr must be properly + * terminated + * + * \param [in] cstr the c string to wrap + * \return an amqp_bytes_t that describes the string + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_bytes_t AMQP_CALL amqp_cstring_bytes(char const *cstr); + +/** + * Duplicates an amqp_bytes_t buffer. + * + * The buffer is cloned and the contents copied. + * + * The memory associated with the output is allocated + * with amqp_bytes_malloc() and should be freed with + * amqp_bytes_free() + * + * \param [in] src + * \return a clone of the src + * + * \sa amqp_bytes_free(), amqp_bytes_malloc() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_bytes_t AMQP_CALL amqp_bytes_malloc_dup(amqp_bytes_t src); + +/** + * Allocates a amqp_bytes_t buffer + * + * Creates an amqp_bytes_t buffer of the specified amount, the buffer should be + * freed using amqp_bytes_free() + * + * \param [in] amount the size of the buffer in bytes + * \returns an amqp_bytes_t with amount bytes allocated. + * output.bytes will be set to NULL on error + * + * \sa amqp_bytes_free(), amqp_bytes_malloc_dup() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_bytes_t AMQP_CALL amqp_bytes_malloc(size_t amount); + +/** + * Frees an amqp_bytes_t buffer + * + * Frees a buffer allocated with amqp_bytes_malloc() or amqp_bytes_malloc_dup() + * + * Calling amqp_bytes_free on buffers not allocated with one + * of those two functions will result in undefined behavior + * + * \param [in] bytes the buffer to free + * + * \sa amqp_bytes_malloc(), amqp_bytes_malloc_dup() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL amqp_bytes_free(amqp_bytes_t bytes); + +/** + * Allocate and initialize a new amqp_connection_state_t object + * + * amqp_connection_state_t objects created with this function + * should be freed with amqp_destroy_connection() + * + * \returns an opaque pointer on success, NULL or 0 on failure. + * + * \sa amqp_destroy_connection() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_connection_state_t AMQP_CALL amqp_new_connection(void); + +/** + * Get the underlying socket descriptor for the connection + * + * \warning Use the socket returned from this function carefully, incorrect use + * of the socket outside of the library will lead to undefined behavior. + * Additionally rabbitmq-c may use the socket differently version-to-version, + * what may work in one version, may break in the next version. Be sure to + * throughly test any applications that use the socket returned by this + * function especially when using a newer version of rabbitmq-c + * + * \param [in] state the connection object + * \returns the socket descriptor if one has been set, -1 otherwise + * + * \sa amqp_tcp_socket_new(), amqp_ssl_socket_new(), amqp_socket_open() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_get_sockfd(amqp_connection_state_t state); + +/** + * Deprecated, use amqp_tcp_socket_new() or amqp_ssl_socket_new() + * + * \deprecated Use amqp_tcp_socket_new() or amqp_ssl_socket_new() + * + * Sets the socket descriptor associated with the connection. The socket + * should be connected to a broker, and should not be read to or written from + * before calling this function. A socket descriptor can be created and opened + * using amqp_open_socket() + * + * \param [in] state the connection object + * \param [in] sockfd the socket + * + * \sa amqp_open_socket(), amqp_tcp_socket_new(), amqp_ssl_socket_new() + * + * \since v0.1 + */ +AMQP_DEPRECATED(AMQP_PUBLIC_FUNCTION void AMQP_CALL + amqp_set_sockfd(amqp_connection_state_t state, int sockfd)); + +/** + * Tune client side parameters + * + * \warning This function may call abort() if the connection is in a certain + * state. As such it should probably not be called code outside the library. + * connection parameters should be specified when calling amqp_login() or + * amqp_login_with_properties() + * + * This function changes channel_max, frame_max, and heartbeat parameters, on + * the client side only. It does not try to renegotiate these parameters with + * the broker. Using this function will lead to unexpected results. + * + * \param [in] state the connection object + * \param [in] channel_max the maximum number of channels. + * The largest this can be is 65535 + * \param [in] frame_max the maximum size of an frame. + * The smallest this can be is 4096 + * The largest this can be is 2147483647 + * Unless you know what you're doing the recommended + * size is 131072 or 128KB + * \param [in] heartbeat the number of seconds between heartbeats + * + * \return AMQP_STATUS_OK on success, an amqp_status_enum value otherwise. + * Possible error codes include: + * - AMQP_STATUS_NO_MEMORY memory allocation failed. + * - AMQP_STATUS_TIMER_FAILURE the underlying system timer indicated it + * failed. + * + * \sa amqp_login(), amqp_login_with_properties() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_tune_connection(amqp_connection_state_t state, + int channel_max, int frame_max, + int heartbeat); + +/** + * Get the maximum number of channels the connection can handle + * + * The maximum number of channels is set when connection negotiation takes + * place in amqp_login() or amqp_login_with_properties(). + * + * \param [in] state the connection object + * \return the maximum number of channels. 0 if there is no limit + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_get_channel_max(amqp_connection_state_t state); + +/** + * Get the maximum size of an frame the connection can handle + * + * The maximum size of an frame is set when connection negotiation takes + * place in amqp_login() or amqp_login_with_properties(). + * + * \param [in] state the connection object + * \return the maximum size of an frame. + * + * \since v0.6 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_get_frame_max(amqp_connection_state_t state); + +/** + * Get the number of seconds between heartbeats of the connection + * + * The number of seconds between heartbeats is set when connection + * negotiation takes place in amqp_login() or amqp_login_with_properties(). + * + * \param [in] state the connection object + * \return the number of seconds between heartbeats. + * + * \since v0.6 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_get_heartbeat(amqp_connection_state_t state); + +/** + * Destroys an amqp_connection_state_t object + * + * Destroys a amqp_connection_state_t object that was created with + * amqp_new_connection(). If the connection with the broker is open, it will be + * implicitly closed with a reply code of 200 (success). Any memory that + * would be freed with amqp_maybe_release_buffers() or + * amqp_maybe_release_buffers_on_channel() will be freed, and use of that + * memory will caused undefined behavior. + * + * \param [in] state the connection object + * \return AMQP_STATUS_OK on success. amqp_status_enum value failure + * + * \sa amqp_new_connection() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_destroy_connection(amqp_connection_state_t state); + +/** + * Process incoming data + * + * \warning This is a low-level function intended for those who want to + * have greater control over input and output over the socket from the + * broker. Correctly using this function requires in-depth knowledge of AMQP + * and rabbitmq-c. + * + * For a given buffer of data received from the broker, decode the first + * frame in the buffer. If more than one frame is contained in the input buffer + * the return value will be less than the received_data size, the caller should + * adjust received_data buffer descriptor to point to the beginning of the + * buffer + the return value. + * + * \param [in] state the connection object + * \param [in] received_data a buffer of data received from the broker. The + * function will return the number of bytes of the buffer it used. The + * function copies these bytes to an internal buffer: this part of the buffer + * may be reused after this function successfully completes. + * \param [in,out] decoded_frame caller should pass in a pointer to an + * amqp_frame_t struct. If there is enough data in received_data for a + * complete frame, decoded_frame->frame_type will be set to something OTHER + * than 0. decoded_frame may contain members pointing to memory owned by + * the state object. This memory can be recycled with + * amqp_maybe_release_buffers() or amqp_maybe_release_buffers_on_channel(). + * \return number of bytes consumed from received_data or 0 if a 0-length + * buffer was passed. A negative return value indicates failure. Possible + * errors: + * - AMQP_STATUS_NO_MEMORY failure in allocating memory. The library is likely + * in an indeterminate state making recovery unlikely. Client should note the + * error and terminate the application + * - AMQP_STATUS_BAD_AMQP_DATA bad AMQP data was received. The connection + * should be shutdown immediately + * - AMQP_STATUS_UNKNOWN_METHOD: an unknown method was received from the + * broker. This is likely a protocol error and the connection should be + * shutdown immediately + * - AMQP_STATUS_UNKNOWN_CLASS: a properties frame with an unknown class + * was received from the broker. This is likely a protocol error and the + * connection should be shutdown immediately + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_handle_input(amqp_connection_state_t state, + amqp_bytes_t received_data, + amqp_frame_t *decoded_frame); + +/** + * Check to see if connection memory can be released + * + * \deprecated This function is deprecated in favor of + * amqp_maybe_release_buffers() or amqp_maybe_release_buffers_on_channel() + * + * Checks the state of an amqp_connection_state_t object to see if + * amqp_release_buffers() can be called successfully. + * + * \param [in] state the connection object + * \returns TRUE if the buffers can be released FALSE otherwise + * + * \sa amqp_release_buffers() amqp_maybe_release_buffers() + * amqp_maybe_release_buffers_on_channel() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_boolean_t AMQP_CALL amqp_release_buffers_ok(amqp_connection_state_t state); + +/** + * Release amqp_connection_state_t owned memory + * + * \deprecated This function is deprecated in favor of + * amqp_maybe_release_buffers() or amqp_maybe_release_buffers_on_channel() + * + * \warning caller should ensure amqp_release_buffers_ok() returns true before + * calling this function. Failure to do so may result in abort() being called. + * + * Release memory owned by the amqp_connection_state_t for reuse by the + * library. Use of any memory returned by the library before this function is + * called will result in undefined behavior. + * + * \note internally rabbitmq-c tries to reuse memory when possible. As a result + * its possible calling this function may not have a noticeable effect on + * memory usage. + * + * \param [in] state the connection object + * + * \sa amqp_release_buffers_ok() amqp_maybe_release_buffers() + * amqp_maybe_release_buffers_on_channel() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL amqp_release_buffers(amqp_connection_state_t state); + +/** + * Release amqp_connection_state_t owned memory + * + * Release memory owned by the amqp_connection_state_t object related to any + * channel, allowing reuse by the library. Use of any memory returned by the + * library before this function is called with result in undefined behavior. + * + * \note internally rabbitmq-c tries to reuse memory when possible. As a result + * its possible calling this function may not have a noticeable effect on + * memory usage. + * + * \param [in] state the connection object + * + * \sa amqp_maybe_release_buffers_on_channel() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL amqp_maybe_release_buffers(amqp_connection_state_t state); + +/** + * Release amqp_connection_state_t owned memory related to a channel + * + * Release memory owned by the amqp_connection_state_t object related to the + * specified channel, allowing reuse by the library. Use of any memory returned + * the library for a specific channel will result in undefined behavior. + * + * \note internally rabbitmq-c tries to reuse memory when possible. As a result + * its possible calling this function may not have a noticeable effect on + * memory usage. + * + * \param [in] state the connection object + * \param [in] channel the channel specifier for which memory should be + * released. Note that the library does not care about the state of the + * channel when calling this function + * + * \sa amqp_maybe_release_buffers() + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL amqp_maybe_release_buffers_on_channel( + amqp_connection_state_t state, amqp_channel_t channel); + +/** + * Send a frame to the broker + * + * \param [in] state the connection object + * \param [in] frame the frame to send to the broker + * \return AMQP_STATUS_OK on success, an amqp_status_enum value on error. + * Possible error codes: + * - AMQP_STATUS_BAD_AMQP_DATA the serialized form of the method or + * properties was too large to fit in a single AMQP frame, or the + * method contains an invalid value. The frame was not sent. + * - AMQP_STATUS_TABLE_TOO_BIG the serialized form of an amqp_table_t is + * too large to fit in a single AMQP frame. Frame was not sent. + * - AMQP_STATUS_UNKNOWN_METHOD an invalid method type was passed in + * - AMQP_STATUS_UNKNOWN_CLASS an invalid properties type was passed in + * - AMQP_STATUS_TIMER_FAILURE system timer indicated failure. The frame + * was sent + * - AMQP_STATUS_SOCKET_ERROR + * - AMQP_STATUS_SSL_ERROR + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_send_frame(amqp_connection_state_t state, + amqp_frame_t const *frame); + +/** + * Compare two table entries + * + * Works just like strcmp(), comparing two the table keys, datatype, then values + * + * \param [in] entry1 the entry on the left + * \param [in] entry2 the entry on the right + * \return 0 if entries are equal, 0 < if left is greater, 0 > if right is + * greater + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_table_entry_cmp(void const *entry1, void const *entry2); + +/** + * Open a socket to a remote host + * + * \deprecated This function is deprecated in favor of amqp_socket_open() + * + * Looks up the hostname, then attempts to open a socket to the host using + * the specified portnumber. It also sets various options on the socket to + * improve performance and correctness. + * + * \param [in] hostname this can be a hostname or IP address. + * Both IPv4 and IPv6 are acceptable + * \param [in] portnumber the port to connect on. RabbitMQ brokers + * listen on port 5672, and 5671 for SSL + * \return a positive value indicates success and is the sockfd. A negative + * value (see amqp_status_enum)is returned on failure. Possible error codes: + * - AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR Initialization of underlying socket + * library failed. + * - AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED hostname lookup failed. + * - AMQP_STATUS_SOCKET_ERROR a socket error occurred. errno or + * WSAGetLastError() may return more useful information. + * + * \note IPv6 support was added in v0.3 + * + * \sa amqp_socket_open() amqp_set_sockfd() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_open_socket(char const *hostname, int portnumber); + +/** + * Send initial AMQP header to the broker + * + * \warning this is a low level function intended for those who want to + * interact with the broker at a very low level. Use of this function without + * understanding what it does will result in AMQP protocol errors. + * + * This function sends the AMQP protocol header to the broker. + * + * \param [in] state the connection object + * \return AMQP_STATUS_OK on success, a negative value on failure. Possible + * error codes: + * - AMQP_STATUS_CONNECTION_CLOSED the connection to the broker was closed. + * - AMQP_STATUS_SOCKET_ERROR a socket error occurred. It is likely the + * underlying socket has been closed. errno or WSAGetLastError() may provide + * further information. + * - AMQP_STATUS_SSL_ERROR a SSL error occurred. The connection to the broker + * was closed. + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_send_header(amqp_connection_state_t state); + +/** + * Checks to see if there are any incoming frames ready to be read + * + * Checks to see if there are any amqp_frame_t objects buffered by the + * amqp_connection_state_t object. Having one or more frames buffered means + * that amqp_simple_wait_frame() or amqp_simple_wait_frame_noblock() will + * return a frame without potentially blocking on a read() call. + * + * \param [in] state the connection object + * \return TRUE if there are frames enqueued, FALSE otherwise + * + * \sa amqp_simple_wait_frame() amqp_simple_wait_frame_noblock() + * amqp_data_in_buffer() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_boolean_t AMQP_CALL amqp_frames_enqueued(amqp_connection_state_t state); + +/** + * Read a single amqp_frame_t + * + * Waits for the next amqp_frame_t frame to be read from the broker. + * This function has the potential to block for a long time in the case of + * waiting for a basic.deliver method frame from the broker. + * + * The library may buffer frames. When an amqp_connection_state_t object + * has frames buffered calling amqp_simple_wait_frame() will return an + * amqp_frame_t without entering a blocking read(). You can test to see if + * an amqp_connection_state_t object has frames buffered by calling the + * amqp_frames_enqueued() function. + * + * The library has a socket read buffer. When there is data in an + * amqp_connection_state_t read buffer, amqp_simple_wait_frame() may return an + * amqp_frame_t without entering a blocking read(). You can test to see if an + * amqp_connection_state_t object has data in its read buffer by calling the + * amqp_data_in_buffer() function. + * + * \param [in] state the connection object + * \param [out] decoded_frame the frame + * \return AMQP_STATUS_OK on success, an amqp_status_enum value + * is returned otherwise. Possible errors include: + * - AMQP_STATUS_NO_MEMORY failure in allocating memory. The library is likely + * in an indeterminate state making recovery unlikely. Client should note the + * error and terminate the application + * - AMQP_STATUS_BAD_AMQP_DATA bad AMQP data was received. The connection + * should be shutdown immediately + * - AMQP_STATUS_UNKNOWN_METHOD: an unknown method was received from the + * broker. This is likely a protocol error and the connection should be + * shutdown immediately + * - AMQP_STATUS_UNKNOWN_CLASS: a properties frame with an unknown class + * was received from the broker. This is likely a protocol error and the + * connection should be shutdown immediately + * - AMQP_STATUS_HEARTBEAT_TIMEOUT timed out while waiting for heartbeat + * from the broker. The connection has been closed. + * - AMQP_STATUS_TIMER_FAILURE system timer indicated failure. + * - AMQP_STATUS_SOCKET_ERROR a socket error occurred. The connection has + * been closed + * - AMQP_STATUS_SSL_ERROR a SSL socket error occurred. The connection has + * been closed. + * + * \sa amqp_simple_wait_frame_noblock() amqp_frames_enqueued() + * amqp_data_in_buffer() + * + * \note as of v0.4.0 this function will no longer return heartbeat frames + * when enabled by specifying a non-zero heartbeat value in amqp_login(). + * Heartbeating is handled internally by the library. + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_simple_wait_frame(amqp_connection_state_t state, + amqp_frame_t *decoded_frame); + +/** + * Read a single amqp_frame_t with a timeout. + * + * Waits for the next amqp_frame_t frame to be read from the broker, up to + * a timespan specified by tv. The function will return AMQP_STATUS_TIMEOUT + * if the timeout is reached. The tv value is not modified by the function. + * + * If a 0 timeval is specified, the function behaves as if its non-blocking: it + * will test to see if a frame can be read from the broker, and return + * immediately. + * + * If NULL is passed in for tv, the function will behave like + * amqp_simple_wait_frame() and block until a frame is received from the broker + * + * The library may buffer frames. When an amqp_connection_state_t object + * has frames buffered calling amqp_simple_wait_frame_noblock() will return an + * amqp_frame_t without entering a blocking read(). You can test to see if an + * amqp_connection_state_t object has frames buffered by calling the + * amqp_frames_enqueued() function. + * + * The library has a socket read buffer. When there is data in an + * amqp_connection_state_t read buffer, amqp_simple_wait_frame_noblock() may + * return + * an amqp_frame_t without entering a blocking read(). You can test to see if an + * amqp_connection_state_t object has data in its read buffer by calling the + * amqp_data_in_buffer() function. + * + * \note This function does not return heartbeat frames. When enabled, + * heartbeating is handed internally internally by the library. + * + * \param [in,out] state the connection object + * \param [out] decoded_frame the frame + * \param [in] tv the maximum time to wait for a frame to be read. Setting + * tv->tv_sec = 0 and tv->tv_usec = 0 will do a non-blocking read. Specifying + * NULL for tv will make the function block until a frame is read. + * \return AMQP_STATUS_OK on success. An amqp_status_enum value is returned + * otherwise. Possible errors include: + * - AMQP_STATUS_TIMEOUT the timeout was reached while waiting for a frame + * from the broker. + * - AMQP_STATUS_INVALID_PARAMETER the tv parameter contains an invalid value. + * - AMQP_STATUS_NO_MEMORY failure in allocating memory. The library is likely + * in an indeterminate state making recovery unlikely. Client should note the + * error and terminate the application + * - AMQP_STATUS_BAD_AMQP_DATA bad AMQP data was received. The connection + * should be shutdown immediately + * - AMQP_STATUS_UNKNOWN_METHOD: an unknown method was received from the + * broker. This is likely a protocol error and the connection should be + * shutdown immediately + * - AMQP_STATUS_UNKNOWN_CLASS: a properties frame with an unknown class + * was received from the broker. This is likely a protocol error and the + * connection should be shutdown immediately + * - AMQP_STATUS_HEARTBEAT_TIMEOUT timed out while waiting for heartbeat + * from the broker. The connection has been closed. + * - AMQP_STATUS_TIMER_FAILURE system timer indicated failure. + * - AMQP_STATUS_SOCKET_ERROR a socket error occurred. The connection has + * been closed + * - AMQP_STATUS_SSL_ERROR a SSL socket error occurred. The connection has + * been closed. + * + * \sa amqp_simple_wait_frame() amqp_frames_enqueued() amqp_data_in_buffer() + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_simple_wait_frame_noblock(amqp_connection_state_t state, + amqp_frame_t *decoded_frame, + struct timeval *tv); + +/** + * Waits for a specific method from the broker + * + * \warning You probably don't want to use this function. If this function + * doesn't receive exactly the frame requested it closes the whole connection. + * + * Waits for a single method on a channel from the broker. + * If a frame is received that does not match expected_channel + * or expected_method the program will abort + * + * \param [in] state the connection object + * \param [in] expected_channel the channel that the method should be delivered + * on + * \param [in] expected_method the method to wait for + * \param [out] output the method + * \returns AMQP_STATUS_OK on success. An amqp_status_enum value is returned + * otherwise. Possible errors include: + * - AMQP_STATUS_WRONG_METHOD a frame containing the wrong method, wrong frame + * type or wrong channel was received. The connection is closed. + * - AMQP_STATUS_NO_MEMORY failure in allocating memory. The library is likely + * in an indeterminate state making recovery unlikely. Client should note the + * error and terminate the application + * - AMQP_STATUS_BAD_AMQP_DATA bad AMQP data was received. The connection + * should be shutdown immediately + * - AMQP_STATUS_UNKNOWN_METHOD: an unknown method was received from the + * broker. This is likely a protocol error and the connection should be + * shutdown immediately + * - AMQP_STATUS_UNKNOWN_CLASS: a properties frame with an unknown class + * was received from the broker. This is likely a protocol error and the + * connection should be shutdown immediately + * - AMQP_STATUS_HEARTBEAT_TIMEOUT timed out while waiting for heartbeat + * from the broker. The connection has been closed. + * - AMQP_STATUS_TIMER_FAILURE system timer indicated failure. + * - AMQP_STATUS_SOCKET_ERROR a socket error occurred. The connection has + * been closed + * - AMQP_STATUS_SSL_ERROR a SSL socket error occurred. The connection has + * been closed. + * + * \since v0.1 + */ + +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_simple_wait_method(amqp_connection_state_t state, + amqp_channel_t expected_channel, + amqp_method_number_t expected_method, + amqp_method_t *output); + +/** + * Sends a method to the broker + * + * This is a thin wrapper around amqp_send_frame(), providing a way to send + * a method to the broker on a specified channel. + * + * \param [in] state the connection object + * \param [in] channel the channel object + * \param [in] id the method number + * \param [in] decoded the method object + * \returns AMQP_STATUS_OK on success, an amqp_status_enum value otherwise. + * Possible errors include: + * - AMQP_STATUS_BAD_AMQP_DATA the serialized form of the method or + * properties was too large to fit in a single AMQP frame, or the + * method contains an invalid value. The frame was not sent. + * - AMQP_STATUS_TABLE_TOO_BIG the serialized form of an amqp_table_t is + * too large to fit in a single AMQP frame. Frame was not sent. + * - AMQP_STATUS_UNKNOWN_METHOD an invalid method type was passed in + * - AMQP_STATUS_UNKNOWN_CLASS an invalid properties type was passed in + * - AMQP_STATUS_TIMER_FAILURE system timer indicated failure. The frame + * was sent + * - AMQP_STATUS_SOCKET_ERROR + * - AMQP_STATUS_SSL_ERROR + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_send_method(amqp_connection_state_t state, + amqp_channel_t channel, amqp_method_number_t id, + void *decoded); + +/** + * Sends a method to the broker and waits for a method response + * + * \param [in] state the connection object + * \param [in] channel the channel object + * \param [in] request_id the method number of the request + * \param [in] expected_reply_ids a 0 terminated array of expected response + * method numbers + * \param [in] decoded_request_method the method to be sent to the broker + * \return a amqp_rpc_reply_t: + * - r.reply_type == AMQP_RESPONSE_NORMAL. RPC completed successfully + * - r.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION. The broker returned an + * exception: + * - If r.reply.id == AMQP_CHANNEL_CLOSE_METHOD a channel exception + * occurred, cast r.reply.decoded to amqp_channel_close_t* to see details + * of the exception. The client should amqp_send_method() a + * amqp_channel_close_ok_t. The channel must be re-opened before it + * can be used again. Any resources associated with the channel + * (auto-delete exchanges, auto-delete queues, consumers) are invalid + * and must be recreated before attempting to use them again. + * - If r.reply.id == AMQP_CONNECTION_CLOSE_METHOD a connection exception + * occurred, cast r.reply.decoded to amqp_connection_close_t* to see + * details of the exception. The client amqp_send_method() a + * amqp_connection_close_ok_t and disconnect from the broker. + * - r.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION. An exception occurred + * within the library. Examine r.library_error and compare it against + * amqp_status_enum values to determine the error. + * + * \sa amqp_simple_rpc_decoded() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t AMQP_CALL amqp_simple_rpc( + amqp_connection_state_t state, amqp_channel_t channel, + amqp_method_number_t request_id, amqp_method_number_t *expected_reply_ids, + void *decoded_request_method); + +/** + * Sends a method to the broker and waits for a method response + * + * \param [in] state the connection object + * \param [in] channel the channel object + * \param [in] request_id the method number of the request + * \param [in] reply_id the method number expected in response + * \param [in] decoded_request_method the request method + * \return a pointer to the method returned from the broker, or NULL on error. + * On error amqp_get_rpc_reply() will return an amqp_rpc_reply_t with + * details on the error that occurred. + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void *AMQP_CALL amqp_simple_rpc_decoded(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_method_number_t request_id, + amqp_method_number_t reply_id, + void *decoded_request_method); + +/** + * Get the last global amqp_rpc_reply + * + * The API methods corresponding to most synchronous AMQP methods + * return a pointer to the decoded method result. Upon error, they + * return NULL, and we need some way of discovering what, if anything, + * went wrong. amqp_get_rpc_reply() returns the most recent + * amqp_rpc_reply_t instance corresponding to such an API operation + * for the given connection. + * + * Only use it for operations that do not themselves return + * amqp_rpc_reply_t; operations that do return amqp_rpc_reply_t + * generally do NOT update this per-connection-global amqp_rpc_reply_t + * instance. + * + * \param [in] state the connection object + * \return the most recent amqp_rpc_reply_t: + * - r.reply_type == AMQP_RESPONSE_NORMAL. RPC completed successfully + * - r.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION. The broker returned an + * exception: + * - If r.reply.id == AMQP_CHANNEL_CLOSE_METHOD a channel exception + * occurred, cast r.reply.decoded to amqp_channel_close_t* to see details + * of the exception. The client should amqp_send_method() a + * amqp_channel_close_ok_t. The channel must be re-opened before it + * can be used again. Any resources associated with the channel + * (auto-delete exchanges, auto-delete queues, consumers) are invalid + * and must be recreated before attempting to use them again. + * - If r.reply.id == AMQP_CONNECTION_CLOSE_METHOD a connection exception + * occurred, cast r.reply.decoded to amqp_connection_close_t* to see + * details of the exception. The client amqp_send_method() a + * amqp_connection_close_ok_t and disconnect from the broker. + * - r.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION. An exception occurred + * within the library. Examine r.library_error and compare it against + * amqp_status_enum values to determine the error. + * + * \sa amqp_simple_rpc_decoded() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t AMQP_CALL amqp_get_rpc_reply(amqp_connection_state_t state); + +/** + * Login to the broker + * + * After using amqp_open_socket and amqp_set_sockfd, call + * amqp_login to complete connecting to the broker + * + * \param [in] state the connection object + * \param [in] vhost the virtual host to connect to on the broker. The default + * on most brokers is "/" + * \param [in] channel_max the limit for number of channels for the connection. + * 0 means no limit, and is a good default + * (AMQP_DEFAULT_MAX_CHANNELS) + * Note that the maximum number of channels the protocol supports + * is 65535 (2^16, with the 0-channel reserved). The server can + * set a lower channel_max and then the client will use the lowest + * of the two + * \param [in] frame_max the maximum size of an AMQP frame on the wire to + * request of the broker for this connection. 4096 is the minimum + * size, 2^31-1 is the maximum, a good default is 131072 (128KB), + * or AMQP_DEFAULT_FRAME_SIZE + * \param [in] heartbeat the number of seconds between heartbeat frames to + * request of the broker. A value of 0 disables heartbeats. + * Note rabbitmq-c only has partial support for heartbeats, as of + * v0.4.0 they are only serviced during amqp_basic_publish() and + * amqp_simple_wait_frame()/amqp_simple_wait_frame_noblock() + * \param [in] sasl_method the SASL method to authenticate with the broker. + * followed by the authentication information. The following SASL + * methods are implemented: + * - AMQP_SASL_METHOD_PLAIN, the AMQP_SASL_METHOD_PLAIN argument + * should be followed by two arguments in this order: + * const char* username, and const char* password. + * - AMQP_SASL_METHOD_EXTERNAL, the AMQP_SASL_METHOD_EXTERNAL + * argument should be followed one argument: + * const char* identity. + * \return amqp_rpc_reply_t indicating success or failure. + * - r.reply_type == AMQP_RESPONSE_NORMAL. Login completed successfully + * - r.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION. In most cases errors + * from the broker when logging in will be represented by the broker closing + * the socket. In this case r.library_error will be set to + * AMQP_STATUS_CONNECTION_CLOSED. This error can represent a number of + * error conditions including: invalid vhost, authentication failure. + * - r.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION. The broker returned an + * exception: + * - If r.reply.id == AMQP_CHANNEL_CLOSE_METHOD a channel exception + * occurred, cast r.reply.decoded to amqp_channel_close_t* to see details + * of the exception. The client should amqp_send_method() a + * amqp_channel_close_ok_t. The channel must be re-opened before it + * can be used again. Any resources associated with the channel + * (auto-delete exchanges, auto-delete queues, consumers) are invalid + * and must be recreated before attempting to use them again. + * - If r.reply.id == AMQP_CONNECTION_CLOSE_METHOD a connection exception + * occurred, cast r.reply.decoded to amqp_connection_close_t* to see + * details of the exception. The client amqp_send_method() a + * amqp_connection_close_ok_t and disconnect from the broker. + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t AMQP_CALL amqp_login(amqp_connection_state_t state, + char const *vhost, int channel_max, + int frame_max, int heartbeat, + amqp_sasl_method_enum sasl_method, ...); + +/** + * Login to the broker passing a properties table + * + * This function is similar to amqp_login() and differs in that it provides a + * way to pass client properties to the broker. This is commonly used to + * negotiate newer protocol features as they are supported by the broker. + * + * \param [in] state the connection object + * \param [in] vhost the virtual host to connect to on the broker. The default + * on most brokers is "/" + * \param [in] channel_max the limit for the number of channels for the + * connection. + * 0 means no limit, and is a good default + * (AMQP_DEFAULT_MAX_CHANNELS) + * Note that the maximum number of channels the protocol supports + * is 65535 (2^16, with the 0-channel reserved). The server can + * set a lower channel_max and then the client will use the lowest + * of the two + * \param [in] frame_max the maximum size of an AMQP frame ont he wire to + * request of the broker for this connection. 4096 is the minimum + * size, 2^31-1 is the maximum, a good default is 131072 (128KB), + * or AMQP_DEFAULT_FRAME_SIZE + * \param [in] heartbeat the number of seconds between heartbeat frame to + * request of the broker. A value of 0 disables heartbeats. + * Note rabbitmq-c only has partial support for hearts, as of + * v0.4.0 heartbeats are only serviced during amqp_basic_publish(), + * and amqp_simple_wait_frame()/amqp_simple_wait_frame_noblock() + * \param [in] properties a table of properties to send the broker. + * \param [in] sasl_method the SASL method to authenticate with the broker + * followed by the authentication information. The following SASL + * methods are implemented: + * - AMQP_SASL_METHOD_PLAIN, the AMQP_SASL_METHOD_PLAIN argument + * should be followed by two arguments in this order: + * const char* username, and const char* password. + * - AMQP_SASL_METHOD_EXTERNAL, the AMQP_SASL_METHOD_EXTERNAL + * argument should be followed one argument: + * const char* identity. + * \return amqp_rpc_reply_t indicating success or failure. + * - r.reply_type == AMQP_RESPONSE_NORMAL. Login completed successfully + * - r.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION. In most cases errors + * from the broker when logging in will be represented by the broker closing + * the socket. In this case r.library_error will be set to + * AMQP_STATUS_CONNECTION_CLOSED. This error can represent a number of + * error conditions including: invalid vhost, authentication failure. + * - r.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION. The broker returned an + * exception: + * - If r.reply.id == AMQP_CHANNEL_CLOSE_METHOD a channel exception + * occurred, cast r.reply.decoded to amqp_channel_close_t* to see details + * of the exception. The client should amqp_send_method() a + * amqp_channel_close_ok_t. The channel must be re-opened before it + * can be used again. Any resources associated with the channel + * (auto-delete exchanges, auto-delete queues, consumers) are invalid + * and must be recreated before attempting to use them again. + * - If r.reply.id == AMQP_CONNECTION_CLOSE_METHOD a connection exception + * occurred, cast r.reply.decoded to amqp_connection_close_t* to see + * details of the exception. The client amqp_send_method() a + * amqp_connection_close_ok_t and disconnect from the broker. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t AMQP_CALL amqp_login_with_properties( + amqp_connection_state_t state, char const *vhost, int channel_max, + int frame_max, int heartbeat, const amqp_table_t *properties, + amqp_sasl_method_enum sasl_method, ...); + +struct amqp_basic_properties_t_; + +/** + * Publish a message to the broker + * + * Publish a message on an exchange with a routing key. + * + * Note that at the AMQ protocol level basic.publish is an async method: + * this means error conditions that occur on the broker (such as publishing to + * a non-existent exchange) will not be reflected in the return value of this + * function. + * + * \param [in] state the connection object + * \param [in] channel the channel identifier + * \param [in] exchange the exchange on the broker to publish to + * \param [in] routing_key the routing key to use when publishing the message + * \param [in] mandatory indicate to the broker that the message MUST be routed + * to a queue. If the broker cannot do this it should respond with + * a basic.return method. + * \param [in] immediate indicate to the broker that the message MUST be + * delivered to a consumer immediately. If the broker cannot do this + * it should respond with a basic.return method. + * \param [in] properties the properties associated with the message + * \param [in] body the message body + * \return AMQP_STATUS_OK on success, amqp_status_enum value on failure. Note + * that basic.publish is an async method, the return value from this + * function only indicates that the message data was successfully + * transmitted to the broker. It does not indicate failures that occur + * on the broker, such as publishing to a non-existent exchange. + * Possible error values: + * - AMQP_STATUS_TIMER_FAILURE: system timer facility returned an error + * the message was not sent. + * - AMQP_STATUS_HEARTBEAT_TIMEOUT: connection timed out waiting for a + * heartbeat from the broker. The message was not sent. + * - AMQP_STATUS_NO_MEMORY: memory allocation failed. The message was + * not sent. + * - AMQP_STATUS_TABLE_TOO_BIG: a table in the properties was too large + * to fit in a single frame. Message was not sent. + * - AMQP_STATUS_CONNECTION_CLOSED: the connection was closed. + * - AMQP_STATUS_SSL_ERROR: a SSL error occurred. + * - AMQP_STATUS_TCP_ERROR: a TCP error occurred. errno or + * WSAGetLastError() may provide more information + * + * Note: this function does heartbeat processing as of v0.4.0 + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL 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, struct amqp_basic_properties_t_ const *properties, + amqp_bytes_t body); + +/** + * Closes an channel + * + * \param [in] state the connection object + * \param [in] channel the channel identifier + * \param [in] code the reason for closing the channel, AMQP_REPLY_SUCCESS is a + * good default + * \return amqp_rpc_reply_t indicating success or failure + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t AMQP_CALL amqp_channel_close(amqp_connection_state_t state, + amqp_channel_t channel, int code); + +/** + * Closes the entire connection + * + * Implicitly closes all channels and informs the broker the connection + * is being closed, after receiving acknowledgment from the broker it closes + * the socket. + * + * \param [in] state the connection object + * \param [in] code the reason code for closing the connection. + * AMQP_REPLY_SUCCESS is a good default. + * \return amqp_rpc_reply_t indicating the result + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t AMQP_CALL amqp_connection_close(amqp_connection_state_t state, + int code); + +/** + * Acknowledges a message + * + * Does a basic.ack on a received message + * + * \param [in] state the connection object + * \param [in] channel the channel identifier + * \param [in] delivery_tag the delivery tag of the message to be ack'd + * \param [in] multiple if true, ack all messages up to this delivery tag, if + * false ack only this delivery tag + * \return 0 on success, 0 > on failing to send the ack to the broker. + * this will not indicate failure if something goes wrong on the + * broker + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_basic_ack(amqp_connection_state_t state, + amqp_channel_t channel, uint64_t delivery_tag, + amqp_boolean_t multiple); + +/** + * Do a basic.get + * + * Synchonously polls the broker for a message in a queue, and + * retrieves the message if a message is in the queue. + * + * \param [in] state the connection object + * \param [in] channel the channel identifier to use + * \param [in] queue the queue name to retrieve from + * \param [in] no_ack if true the message is automatically ack'ed + * if false amqp_basic_ack should be called once the message + * retrieved has been processed + * \return amqp_rpc_reply indicating success or failure + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t AMQP_CALL amqp_basic_get(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_bytes_t queue, + amqp_boolean_t no_ack); + +/** + * Do a basic.reject + * + * Actively reject a message that has been delivered + * + * \param [in] state the connection object + * \param [in] channel the channel identifier + * \param [in] delivery_tag the delivery tag of the message to reject + * \param [in] requeue indicate to the broker whether it should requeue the + * message or just discard it. + * \return 0 on success, 0 > on failing to send the reject method to the broker. + * This will not indicate failure if something goes wrong on the + * broker. + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_basic_reject(amqp_connection_state_t state, + amqp_channel_t channel, uint64_t delivery_tag, + amqp_boolean_t requeue); + +/** + * Do a basic.nack + * + * Actively reject a message, this has the same effect as amqp_basic_reject() + * however, amqp_basic_nack() can negatively acknowledge multiple messages with + * one call much like amqp_basic_ack() can acknowledge mutliple messages with + * one call. + * + * \param [in] state the connection object + * \param [in] channel the channel identifier + * \param [in] delivery_tag the delivery tag of the message to reject + * \param [in] multiple if set to 1 negatively acknowledge all unacknowledged + * messages on this channel. + * \param [in] requeue indicate to the broker whether it should requeue the + * message or dead-letter it. + * \return AMQP_STATUS_OK on success, an amqp_status_enum value otherwise. + * + * \since v0.5.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_basic_nack(amqp_connection_state_t state, + amqp_channel_t channel, uint64_t delivery_tag, + amqp_boolean_t multiple, amqp_boolean_t requeue); +/** + * Check to see if there is data left in the receive buffer + * + * Can be used to see if there is data still in the buffer, if so + * calling amqp_simple_wait_frame will not immediately enter a + * blocking read. + * + * \param [in] state the connection object + * \return true if there is data in the recieve buffer, false otherwise + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_boolean_t AMQP_CALL amqp_data_in_buffer(amqp_connection_state_t state); + +/** + * Get the error string for the given error code. + * + * \deprecated This function has been deprecated in favor of + * \ref amqp_error_string2() which returns statically allocated + * string which do not need to be freed by the caller. + * + * The returned string resides on the heap; the caller is responsible + * for freeing it. + * + * \param [in] err return error code + * \return the error string + * + * \since v0.1 + */ +AMQP_DEPRECATED( + AMQP_PUBLIC_FUNCTION char *AMQP_CALL amqp_error_string(int err)); + +/** + * Get the error string for the given error code. + * + * Get an error string associated with an error code. The string is statically + * allocated and does not need to be freed + * + * \param [in] err the error code + * \return the error string + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +const char *AMQP_CALL amqp_error_string2(int err); + +/** + * Deserialize an amqp_table_t from AMQP wireformat + * + * This is an internal function and is not typically used by + * client applications + * + * \param [in] encoded the buffer containing the serialized data + * \param [in] pool memory pool used to allocate the table entries from + * \param [in] output the amqp_table_t structure to fill in. Any existing + * entries will be erased + * \param [in,out] offset The offset into the encoded buffer to start + * reading the serialized table. It will be updated + * by this function to end of the table + * \return AMQP_STATUS_OK on success, an amqp_status_enum value on failure + * Possible error codes: + * - AMQP_STATUS_NO_MEMORY out of memory + * - AMQP_STATUS_BAD_AMQP_DATA invalid wireformat + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_decode_table(amqp_bytes_t encoded, amqp_pool_t *pool, + amqp_table_t *output, size_t *offset); + +/** + * Serializes an amqp_table_t to the AMQP wireformat + * + * This is an internal function and is not typically used by + * client applications + * + * \param [in] encoded the buffer where to serialize the table to + * \param [in] input the amqp_table_t to serialize + * \param [in,out] offset The offset into the encoded buffer to start + * writing the serialized table. It will be updated + * by this function to where writing left off + * \return AMQP_STATUS_OK on success, an amqp_status_enum value on failure + * Possible error codes: + * - AMQP_STATUS_TABLE_TOO_BIG the serialized form is too large for the + * buffer + * - AMQP_STATUS_BAD_AMQP_DATA invalid table + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_encode_table(amqp_bytes_t encoded, amqp_table_t *input, + size_t *offset); + +/** + * Create a deep-copy of an amqp_table_t object + * + * Creates a deep-copy of an amqp_table_t object, using the provided pool + * object to allocate the necessary memory. This memory can be freed later by + * call recycle_amqp_pool(), or empty_amqp_pool() + * + * \param [in] original the table to copy + * \param [in,out] clone the table to copy to + * \param [in] pool the initialized memory pool to do allocations for the table + * from + * \return AMQP_STATUS_OK on success, amqp_status_enum value on failure. + * Possible error values: + * - AMQP_STATUS_NO_MEMORY - memory allocation failure. + * - AMQP_STATUS_INVALID_PARAMETER - invalid table (e.g., no key name) + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_table_clone(const amqp_table_t *original, + amqp_table_t *clone, amqp_pool_t *pool); + +/** + * A message object + * + * \since v0.4.0 + */ +typedef struct amqp_message_t_ { + amqp_basic_properties_t properties; /**< message properties */ + amqp_bytes_t body; /**< message body */ + amqp_pool_t pool; /**< pool used to allocate properties */ +} amqp_message_t; + +/** + * Reads the next message on a channel + * + * Reads a complete message (header + body) on a specified channel. This + * function is intended to be used with amqp_basic_get() or when an + * AMQP_BASIC_DELIVERY_METHOD method is received. + * + * \param [in,out] state the connection object + * \param [in] channel the channel on which to read the message from + * \param [in,out] message a pointer to a amqp_message_t object. Caller should + * call amqp_message_destroy() when it is done using the + * fields in the message object. The caller is responsible for + * allocating/destroying the amqp_message_t object itself. + * \param [in] flags pass in 0. Currently unused. + * \returns a amqp_rpc_reply_t object. ret.reply_type == AMQP_RESPONSE_NORMAL on + * success. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t AMQP_CALL amqp_read_message(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_message_t *message, + int flags); + +/** + * Frees memory associated with a amqp_message_t allocated in amqp_read_message + * + * \param [in] message + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL amqp_destroy_message(amqp_message_t *message); + +/** + * Envelope object + * + * \since v0.4.0 + */ +typedef struct amqp_envelope_t_ { + amqp_channel_t channel; /**< channel message was delivered on */ + amqp_bytes_t + consumer_tag; /**< the consumer tag the message was delivered to */ + uint64_t delivery_tag; /**< the messages delivery tag */ + amqp_boolean_t redelivered; /**< flag indicating whether this message is being + redelivered */ + amqp_bytes_t exchange; /**< exchange this message was published to */ + amqp_bytes_t + routing_key; /**< the routing key this message was published with */ + amqp_message_t message; /**< the message */ +} amqp_envelope_t; + +/** + * Wait for and consume a message + * + * Waits for a basic.deliver method on any channel, upon receipt of + * basic.deliver it reads that message, and returns. If any other method is + * received before basic.deliver, this function will return an amqp_rpc_reply_t + * with ret.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION, and + * ret.library_error == AMQP_STATUS_UNEXPECTED_STATE. The caller should then + * call amqp_simple_wait_frame() to read this frame and take appropriate action. + * + * This function should be used after starting a consumer with the + * amqp_basic_consume() function + * + * \param [in,out] state the connection object + * \param [in,out] envelope a pointer to a amqp_envelope_t object. Caller + * should call #amqp_destroy_envelope() when it is done using + * the fields in the envelope object. The caller is responsible + * for allocating/destroying the amqp_envelope_t object itself. + * \param [in] timeout a timeout to wait for a message delivery. Passing in + * NULL will result in blocking behavior. + * \param [in] flags pass in 0. Currently unused. + * \returns a amqp_rpc_reply_t object. ret.reply_type == AMQP_RESPONSE_NORMAL + * on success. If ret.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION, + * and ret.library_error == AMQP_STATUS_UNEXPECTED_STATE, a frame other + * than AMQP_BASIC_DELIVER_METHOD was received, the caller should call + * amqp_simple_wait_frame() to read this frame and take appropriate + * action. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t AMQP_CALL amqp_consume_message(amqp_connection_state_t state, + amqp_envelope_t *envelope, + struct timeval *timeout, + int flags); + +/** + * Frees memory associated with a amqp_envelope_t allocated in + * amqp_consume_message() + * + * \param [in] envelope + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL amqp_destroy_envelope(amqp_envelope_t *envelope); + +/** + * Parameters used to connect to the RabbitMQ broker + * + * \since v0.2 + */ +struct amqp_connection_info { + char *user; /**< the username to authenticate with the broker, default on most + broker is 'guest' */ + char *password; /**< the password to authenticate with the broker, default on + most brokers is 'guest' */ + char *host; /**< the hostname of the broker */ + char *vhost; /**< the virtual host on the broker to connect to, a good default + is "/" */ + int port; /**< the port that the broker is listening on, default on most + brokers is 5672 */ + amqp_boolean_t ssl; +}; + +/** + * Initialze an amqp_connection_info to default values + * + * The default values are: + * - user: "guest" + * - password: "guest" + * - host: "localhost" + * - vhost: "/" + * - port: 5672 + * + * \param [out] parsed the connection info to set defaults on + * + * \since v0.2 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL + amqp_default_connection_info(struct amqp_connection_info *parsed); + +/** + * Parse a connection URL + * + * An amqp connection url takes the form: + * + * amqp://[$USERNAME[:$PASSWORD]\@]$HOST[:$PORT]/[$VHOST] + * + * Examples: + * amqp://guest:guest\@localhost:5672// + * amqp://guest:guest\@localhost/myvhost + * + * Any missing parts of the URL will be set to the defaults specified in + * amqp_default_connection_info. For amqps: URLs the default port will be set + * to 5671 instead of 5672 for non-SSL URLs. + * + * \note This function modifies url parameter. + * + * \param [in] url URI to parse, note that this parameter is modified by the + * function. + * \param [out] parsed the connection info gleaned from the URI. The char* + * members will point to parts of the url input parameter. + * Memory management will depend on how the url is allocated. + * \returns AMQP_STATUS_OK on success, AMQP_STATUS_BAD_URL on failure + * + * \since v0.2 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_parse_url(char *url, struct amqp_connection_info *parsed); + +/* socket API */ + +/** + * 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,out] self A socket object. + * \param [in] host Connect to this host. + * \param [in] port Connect on this remote port. + * + * \return AMQP_STATUS_OK on success, an amqp_status_enum on failure + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_socket_open(amqp_socket_t *self, const char *host, int port); + +/** + * 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,out] self A socket object. + * \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 AMQP_STATUS_OK on success, an amqp_status_enum on failure. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_socket_open_noblock(amqp_socket_t *self, const char *host, + int port, struct timeval *timeout); + +/** + * Get the socket descriptor in use by a socket object. + * + * Retrieve the underlying socket descriptor. This function can be used to + * perform low-level socket operations that aren't supported by the socket + * interface. Use with caution! + * + * \param [in,out] self A socket object. + * + * \return The underlying socket descriptor, or -1 if there is no socket + * descriptor associated with + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_socket_get_sockfd(amqp_socket_t *self); + +/** + * Get the socket object associated with a amqp_connection_state_t + * + * \param [in] state the connection object to get the socket from + * \return a pointer to the socket object, or NULL if one has not been assigned + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_socket_t *AMQP_CALL amqp_get_socket(amqp_connection_state_t state); + +/** + * Get the broker properties table + * + * \param [in] state the connection object + * \return a pointer to an amqp_table_t containing the properties advertised + * by the broker on connection. The connection object owns the table, it + * should not be modified. + * + * \since v0.5.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_table_t *AMQP_CALL + amqp_get_server_properties(amqp_connection_state_t state); + +/** + * Get the client properties table + * + * Get the properties that were passed to the broker on connection. + * + * \param [in] state the connection object + * \return a pointer to an amqp_table_t containing the properties advertised + * by the client on connection. The connection object owns the table, it + * should not be modified. + * + * \since v0.7.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_table_t *AMQP_CALL + amqp_get_client_properties(amqp_connection_state_t state); + +/** + * Get the login handshake timeout. + * + * amqp_login and amqp_login_with_properties perform the login handshake with + * the broker. This function returns the timeout associated with completing + * this operation from the client side. This value can be set by using the + * amqp_set_handshake_timeout. + * + * Note that the RabbitMQ broker has configurable timeout for completing the + * login handshake, the default is 10 seconds. rabbitmq-c has a default of 12 + * seconds. + * + * \param [in] state the connection object + * \return a struct timeval representing the current login timeout for the state + * object. A NULL value represents an infinite timeout. The memory returned is + * owned by the connection object. + * + * \since v0.9.0 + */ +AMQP_PUBLIC_FUNCTION +struct timeval *AMQP_CALL + amqp_get_handshake_timeout(amqp_connection_state_t state); + +/** + * Set the login handshake timeout. + * + * amqp_login and amqp_login_with_properties perform the login handshake with + * the broker. This function sets the timeout associated with completing this + * operation from the client side. + * + * The timeout must be set before amqp_login or amqp_login_with_properties is + * called to change from the default timeout. + * + * Note that the RabbitMQ broker has a configurable timeout for completing the + * login handshake, the default is 10 seconds. rabbitmq-c has a default of 12 + * seconds. + * + * \param [in] state the connection object + * \param [in] timeout a struct timeval* representing new login timeout for the + * state object. NULL represents an infinite timeout. The value of timeout is + * copied internally, the caller is responsible for ownership of the passed in + * pointer, it does not need to remain valid after this function is called. + * \return AMQP_STATUS_OK on success. + * + * \since v0.9.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_set_handshake_timeout(amqp_connection_state_t state, + struct timeval *timeout); + +/** + * Get the RPC timeout + * + * Gets the timeout for any RPC-style AMQP command (e.g., amqp_queue_declare). + * This timeout may be changed at any time by calling \amqp_set_rpc_timeout + * function with a new timeout. The timeout applies individually to each RPC + * that is made. + * + * The default value is NULL, or an infinite timeout. + * + * When an RPC times out, the function will return an error AMQP_STATUS_TIMEOUT, + * and the connection will be closed. + * + *\warning RPC-timeouts are an advanced feature intended to be used to detect + * dead connections quickly when the rabbitmq-c implementation of heartbeats + * does not work. Do not use RPC timeouts unless you understand the implications + * of doing so. + * + * \param [in] state the connection object + * \return a struct timeval representing the current RPC timeout for the state + * object. A NULL value represents an infinite timeout. The memory returned is + * owned by the connection object. + * + * \since v0.9.0 + */ +AMQP_PUBLIC_FUNCTION +struct timeval *AMQP_CALL amqp_get_rpc_timeout(amqp_connection_state_t state); + +/** + * Set the RPC timeout + * + * Sets the timeout for any RPC-style AMQP command (e.g., amqp_queue_declare). + * This timeout may be changed at any time by calling this function with a new + * timeout. The timeout applies individually to each RPC that is made. + * + * The default value is NULL, or an infinite timeout. + * + * When an RPC times out, the function will return an error AMQP_STATUS_TIMEOUT, + * and the connection will be closed. + * + *\warning RPC-timeouts are an advanced feature intended to be used to detect + * dead connections quickly when the rabbitmq-c implementation of heartbeats + * does not work. Do not use RPC timeouts unless you understand the implications + * of doing so. + * + * \param [in] state the connection object + * \param [in] timeout a struct timeval* representing new RPC timeout for the + * state object. NULL represents an infinite timeout. The value of timeout is + * copied internally, the caller is responsible for ownership of the passed + * pointer, it does not need to remain valid after this function is called. + * \return AMQP_STATUS_SUCCESS on success. + * + * \since v0.9.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_set_rpc_timeout(amqp_connection_state_t state, + struct timeval *timeout); + +AMQP_END_DECLS + +#endif /* AMQP_H */ diff --git a/src/librabbitmq/amqp_api.c b/src/librabbitmq/amqp_api.c index 033f84a..28b2384 100644 --- a/src/librabbitmq/amqp_api.c +++ b/src/librabbitmq/amqp_api.c @@ -1,60 +1,230 @@ -#include -#include -#include -#include -#include +/* + * ***** 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 -#ifndef DISABLE_THREADS -#include +#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.h" -#include "amqp_framing.h" #include "amqp_private.h" +#include "amqp_time.h" +#include +#include +#include +#include +#include + +#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; -#include - -#define RPC_REPLY(replytype) \ - (amqp_rpc_reply->reply_type == AMQP_RESPONSE_NORMAL \ - ? (replytype *) amqp_rpc_reply->reply.decoded \ - : NULL) - -amqp_channel_open_ok_t *amqp_channel_open(amqp_connection_state_t state, - amqp_channel_t channel) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_SIMPLE_RPC(state, channel, CHANNEL, OPEN, OPEN_OK, - amqp_channel_open_t, - AMQP_EMPTY_BYTES); - return RPC_REPLY(amqp_channel_open_ok_t); + 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(); } -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) -{ +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); - - amqp_basic_publish_t m = - (amqp_basic_publish_t) { - .exchange = exchange, - .routing_key = routing_key, - .mandatory = mandatory, - .immediate = immediate - }; + 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; - AMQP_CHECK_RESULT(amqp_send_method(state, channel, AMQP_BASIC_PUBLISH_METHOD, &m)); + 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)); @@ -65,279 +235,160 @@ int amqp_basic_publish(amqp_connection_state_t state, f.channel = channel; f.payload.properties.class_id = AMQP_BASIC_CLASS; f.payload.properties.body_size = body.len; - f.payload.properties.decoded = (void *) properties; - AMQP_CHECK_RESULT(amqp_send_frame(state, &f)); + 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 (1) { - int remaining = body.len - body_offset; - assert(remaining >= 0); + while (body_offset < body.len) { + size_t remaining = body.len - body_offset; - if (remaining == 0) + if (remaining == 0) { break; + } f.frame_type = AMQP_FRAME_BODY; f.channel = channel; - f.payload.body_fragment.bytes = BUF_AT(body, body_offset); + 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; - AMQP_CHECK_RESULT(amqp_send_frame(state, &f)); + res = amqp_send_frame_inner(state, &f, flagz, amqp_time_infinite()); + if (res < 0) { + return res; + } } - return 0; + return AMQP_STATUS_OK; } amqp_rpc_reply_t amqp_channel_close(amqp_connection_state_t state, - amqp_channel_t channel, - int code) -{ + amqp_channel_t channel, int code) { char codestr[13]; - snprintf(codestr, sizeof(codestr), "%d", code); - return AMQP_SIMPLE_RPC(state, channel, CHANNEL, CLOSE, CLOSE_OK, - amqp_channel_close_t, - code, amqp_cstring_bytes(codestr), 0, 0); + 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) -{ + int code) { char codestr[13]; - snprintf(codestr, sizeof(codestr), "%d", code); - return AMQP_SIMPLE_RPC(state, 0, CONNECTION, CLOSE, CLOSE_OK, - amqp_connection_close_t, - code, amqp_cstring_bytes(codestr), 0, 0); -} - -amqp_exchange_declare_ok_t *amqp_exchange_declare(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t exchange, - amqp_bytes_t type, - amqp_boolean_t passive, - amqp_boolean_t durable, - amqp_boolean_t auto_delete, - amqp_table_t arguments) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_SIMPLE_RPC(state, channel, EXCHANGE, DECLARE, DECLARE_OK, - amqp_exchange_declare_t, - 0, exchange, type, passive, durable, auto_delete, 0, 0, arguments); - return RPC_REPLY(amqp_exchange_declare_ok_t); -} + amqp_method_number_t replies[2] = {AMQP_CONNECTION_CLOSE_OK_METHOD, 0}; + amqp_channel_close_t req; -amqp_exchange_delete_ok_t *amqp_exchange_delete(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t exchange, - amqp_boolean_t if_unused, - amqp_boolean_t nowait) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_SIMPLE_RPC(state, channel, EXCHANGE, DELETE, DELETE_OK, - amqp_exchange_delete_t, - 0, exchange, if_unused, nowait); - return RPC_REPLY(amqp_exchange_delete_ok_t); -} + if (code < 0 || code > UINT16_MAX) { + return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER); + } -amqp_queue_declare_ok_t *amqp_queue_declare(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t queue, - amqp_boolean_t passive, - amqp_boolean_t durable, - amqp_boolean_t exclusive, - amqp_boolean_t auto_delete, - amqp_table_t arguments) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_SIMPLE_RPC(state, channel, QUEUE, DECLARE, DECLARE_OK, - amqp_queue_declare_t, - 0, queue, passive, durable, exclusive, auto_delete, 0, arguments); - return RPC_REPLY(amqp_queue_declare_ok_t); -} + 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; -amqp_queue_bind_ok_t *amqp_queue_bind(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t queue, - amqp_bytes_t exchange, - amqp_bytes_t routing_key, - amqp_table_t arguments) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_SIMPLE_RPC(state, channel, QUEUE, BIND, BIND_OK, - amqp_queue_bind_t, - 0, queue, exchange, routing_key, 0, arguments); - return RPC_REPLY(amqp_queue_bind_ok_t); + return amqp_simple_rpc(state, 0, AMQP_CONNECTION_CLOSE_METHOD, replies, &req); } -amqp_queue_unbind_ok_t *amqp_queue_unbind(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t queue, - amqp_bytes_t exchange, - amqp_bytes_t binding_key, - amqp_table_t arguments) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_SIMPLE_RPC(state, channel, QUEUE, UNBIND, UNBIND_OK, - amqp_queue_unbind_t, - 0, queue, exchange, binding_key, arguments); - return RPC_REPLY(amqp_queue_unbind_ok_t); +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_basic_consume_ok_t *amqp_basic_consume(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t queue, - amqp_bytes_t consumer_tag, - amqp_boolean_t no_local, - amqp_boolean_t no_ack, - amqp_boolean_t exclusive) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_SIMPLE_RPC(state, channel, BASIC, CONSUME, CONSUME_OK, - amqp_basic_consume_t, - 0, queue, consumer_tag, no_local, no_ack, exclusive, 0); - return RPC_REPLY(amqp_basic_consume_ok_t); +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_ack(amqp_connection_state_t state, - amqp_channel_t channel, - uint64_t delivery_tag, - amqp_boolean_t multiple) -{ - amqp_basic_ack_t m = - (amqp_basic_ack_t) { - .delivery_tag = delivery_tag, - .multiple = multiple - }; - AMQP_CHECK_RESULT(amqp_send_method(state, channel, AMQP_BASIC_ACK_METHOD, &m)); - return 0; +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); } -amqp_queue_purge_ok_t *amqp_queue_purge(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_bytes_t queue, - amqp_boolean_t no_wait) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = AMQP_SIMPLE_RPC(state, channel, QUEUE, PURGE, PURGE_OK, - amqp_queue_purge_t, channel, queue, no_wait); - return RPC_REPLY(amqp_queue_purge_ok_t); +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); } -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_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_MULTIPLE_RESPONSE_RPC(state, channel, BASIC, GET, replies, - amqp_basic_get_t, - channel, queue, no_ack); - return *amqp_rpc_reply; +struct timeval *amqp_get_handshake_timeout(amqp_connection_state_t state) { + return state->handshake_timeout; } -/* - * Expose amqp_rpc_reply to dynamically linked libraries - */ -amqp_rpc_reply_t *amqp_get_rpc_reply(void) -{ -#ifndef DISABLE_THREADS - static int initialized = 0; - static pthread_key_t reply_key; - static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; - amqp_rpc_reply_t *amqp_rpc_reply; - if(!initialized) { - pthread_mutex_lock(&init_mutex); - if(!initialized) { - pthread_key_create(&reply_key, free); - initialized = 1; +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; } - pthread_mutex_unlock(&init_mutex); + state->internal_handshake_timeout = *timeout; + state->handshake_timeout = &state->internal_handshake_timeout; + } else { + state->handshake_timeout = NULL; } - amqp_rpc_reply = (amqp_rpc_reply_t *)pthread_getspecific(reply_key); - if(!amqp_rpc_reply) { - amqp_rpc_reply = calloc(1, sizeof(*amqp_rpc_reply)); - pthread_setspecific(reply_key, (void *)amqp_rpc_reply); - } - return amqp_rpc_reply; -#else - static amqp_rpc_reply_t amqp_rpc_reply; - return &amqp_rpc_reply; -#endif -} -/* - * Expose tx-style transactions - */ - -amqp_tx_select_ok_t *amqp_tx_select(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_table_t arguments) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_SIMPLE_RPC(state, channel, TX, SELECT, SELECT_OK, - amqp_tx_select_t, - channel); - return RPC_REPLY(amqp_tx_select_ok_t); -} - -amqp_tx_commit_ok_t *amqp_tx_commit(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_table_t arguments) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_SIMPLE_RPC(state, channel, TX, COMMIT, COMMIT_OK, - amqp_tx_commit_t, channel); - return RPC_REPLY(amqp_tx_commit_ok_t); + return AMQP_STATUS_OK; } -amqp_tx_rollback_ok_t *amqp_tx_rollback(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_table_t arguments) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_SIMPLE_RPC(state, channel, TX, ROLLBACK, ROLLBACK_OK, - amqp_tx_rollback_t, channel); - return RPC_REPLY(amqp_tx_rollback_ok_t); +struct timeval *amqp_get_rpc_timeout(amqp_connection_state_t state) { + return state->rpc_timeout; } -amqp_basic_qos_ok_t *amqp_basic_qos(amqp_connection_state_t state, - amqp_channel_t channel, - uint32_t prefetch_size, - uint16_t prefetch_count, - amqp_boolean_t global) -{ - amqp_rpc_reply_t *amqp_rpc_reply; - amqp_rpc_reply = amqp_get_rpc_reply(); - *amqp_rpc_reply = - AMQP_SIMPLE_RPC(state, channel, BASIC, QOS, QOS_OK, - amqp_basic_qos_t, prefetch_size, prefetch_count, global); - return RPC_REPLY(amqp_basic_qos_ok_t); +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; } - diff --git a/src/librabbitmq/amqp_config.h b/src/librabbitmq/amqp_config.h deleted file mode 100644 index e62464c..0000000 --- a/src/librabbitmq/amqp_config.h +++ /dev/null @@ -1,2 +0,0 @@ -#define VERSION "0.0.1" - diff --git a/src/librabbitmq/amqp_connection.c b/src/librabbitmq/amqp_connection.c index a1ec1cd..034b2e9 100644 --- a/src/librabbitmq/amqp_connection.c +++ b/src/librabbitmq/amqp_connection.c @@ -1,300 +1,423 @@ -#include -#include -#include -#include -#include +/* + * ***** 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 -#include -#include +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif -#include "amqp.h" -#include "amqp_framing.h" #include "amqp_private.h" +#include "amqp_tcp_socket.h" +#include "amqp_time.h" +#include +#include +#include +#include +#include -#include +#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 -#define INITIAL_FRAME_POOL_PAGE_SIZE 65536 -#define INITIAL_DECODING_POOL_PAGE_SIZE 131072 -#define INITIAL_INBOUND_SOCK_BUFFER_SIZE 131072 +#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); \ - int _wanted_state = (statenum); \ - amqp_assert(_check_state->state == _wanted_state, \ - "Programming error: invalid AMQP connection state: expected %d, got %d", \ - _wanted_state, \ - _check_state->state); \ +#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) { - amqp_connection_state_t state = - (amqp_connection_state_t) calloc(1, sizeof(struct amqp_connection_state_t_)); + int res; + amqp_connection_state_t state = (amqp_connection_state_t)calloc( + 1, sizeof(struct amqp_connection_state_t_)); if (state == NULL) { return NULL; } - init_amqp_pool(&state->frame_pool, INITIAL_FRAME_POOL_PAGE_SIZE); - init_amqp_pool(&state->decoding_pool, INITIAL_DECODING_POOL_PAGE_SIZE); - - state->state = CONNECTION_STATE_IDLE; - - state->inbound_buffer.bytes = NULL; - state->outbound_buffer.bytes = NULL; - if (amqp_tune_connection(state, 0, INITIAL_FRAME_POOL_PAGE_SIZE, 0) != 0) { - empty_amqp_pool(&state->frame_pool); - empty_amqp_pool(&state->decoding_pool); - free(state); - return NULL; + res = amqp_tune_connection(state, 0, AMQP_INITIAL_FRAME_POOL_PAGE_SIZE, 0); + if (0 != res) { + goto out_nomem; } - state->inbound_offset = 0; - state->target_size = HEADER_SIZE; + 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->sockfd = -1; - state->sock_inbound_buffer.len = INITIAL_INBOUND_SOCK_BUFFER_SIZE; - state->sock_inbound_buffer.bytes = malloc(INITIAL_INBOUND_SOCK_BUFFER_SIZE); + 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) { - amqp_destroy_connection(state); - return NULL; + goto out_nomem; } - state->sock_inbound_offset = 0; - state->sock_inbound_limit = 0; + init_amqp_pool(&state->properties_pool, 512); - state->first_queued_frame = NULL; - state->last_queued_frame = NULL; + /* 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->sockfd; + return state->socket ? amqp_socket_get_sockfd(state->socket) : -1; } -void amqp_set_sockfd(amqp_connection_state_t state, - int sockfd) -{ - state->sockfd = sockfd; +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; } -int amqp_tune_connection(amqp_connection_state_t state, - int channel_max, - int frame_max, - int heartbeat) -{ +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; + } - empty_amqp_pool(&state->frame_pool); - init_amqp_pool(&state->frame_pool, frame_max); + 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->inbound_buffer.len = frame_max; state->outbound_buffer.len = frame_max; newbuf = realloc(state->outbound_buffer.bytes, frame_max); if (newbuf == NULL) { - amqp_destroy_connection(state); - return -ENOMEM; + return AMQP_STATUS_NO_MEMORY; } state->outbound_buffer.bytes = newbuf; - return 0; + return AMQP_STATUS_OK; } int amqp_get_channel_max(amqp_connection_state_t state) { return state->channel_max; } -void amqp_destroy_connection(amqp_connection_state_t state) { - empty_amqp_pool(&state->frame_pool); - empty_amqp_pool(&state->decoding_pool); - free(state->outbound_buffer.bytes); - free(state->sock_inbound_buffer.bytes); - free(state); +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.bytes = NULL; + 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; } -void amqp_set_basic_return_cb(amqp_connection_state_t state, - amqp_basic_return_fn_t f, void *data) { - state->basic_return_callback = f; - state->basic_return_callback_data = data; +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) -{ - int total_bytes_consumed = 0; - int 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; - read_more: if (received_data.len == 0) { - return total_bytes_consumed; + return AMQP_STATUS_OK; } if (state->state == CONNECTION_STATE_IDLE) { - state->inbound_buffer.bytes = amqp_pool_alloc(&state->frame_pool, state->inbound_buffer.len); - state->state = CONNECTION_STATE_WAITING_FOR_HEADER; - } - - bytes_consumed = state->target_size - state->inbound_offset; - if (received_data.len < bytes_consumed) { - bytes_consumed = received_data.len; + state->state = CONNECTION_STATE_HEADER; } - E_BYTES(state->inbound_buffer, state->inbound_offset, bytes_consumed, received_data.bytes); - state->inbound_offset += bytes_consumed; - total_bytes_consumed += bytes_consumed; - - assert(state->inbound_offset <= state->target_size); + bytes_consumed = consume_data(state, &received_data); + /* do we have target_size data yet? if not, return with the + expectation that more will arrive */ if (state->inbound_offset < state->target_size) { - return total_bytes_consumed; + return (int)bytes_consumed; } + raw_frame = state->inbound_buffer.bytes; + switch (state->state) { - case CONNECTION_STATE_WAITING_FOR_HEADER: - if (D_8(state->inbound_buffer, 0) == AMQP_PSEUDOFRAME_PROTOCOL_HEADER && - D_16(state->inbound_buffer, 1) == AMQP_PSEUDOFRAME_PROTOCOL_CHANNEL) - { - state->target_size = 8; - state->state = CONNECTION_STATE_WAITING_FOR_PROTOCOL_HEADER; - } else { - state->target_size = D_32(state->inbound_buffer, 3) + HEADER_SIZE + FOOTER_SIZE; - state->state = CONNECTION_STATE_WAITING_FOR_BODY; + 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; } - /* Wind buffer forward, and try to read some body out of it. */ - received_data.len -= bytes_consumed; - received_data.bytes = ((char *) received_data.bytes) + bytes_consumed; - goto read_more; + /* it's not a protocol header; fall through to process it as a + regular frame header */ - case CONNECTION_STATE_WAITING_FOR_BODY: { - int frame_type = D_8(state->inbound_buffer, 0); + 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)); -#if 0 - printf("recving:\n"); - amqp_dump(state->inbound_buffer.bytes, state->target_size); -#endif + state->target_size = + amqp_d32(amqp_offset(raw_frame, 3)) + HEADER_SIZE + FOOTER_SIZE; - /* Check frame end marker (footer) */ - if (D_8(state->inbound_buffer, state->target_size - 1) != AMQP_FRAME_END) { - return -EINVAL; + if ((size_t)state->frame_max < state->target_size) { + return AMQP_STATUS_BAD_AMQP_DATA; } - decoded_frame->channel = D_16(state->inbound_buffer, 1); - - switch (frame_type) { - case AMQP_FRAME_METHOD: { - amqp_bytes_t encoded; - - /* Four bytes of method ID before the method args. */ - encoded.len = state->target_size - (HEADER_SIZE + 4 + FOOTER_SIZE); - encoded.bytes = D_BYTES(state->inbound_buffer, HEADER_SIZE + 4, encoded.len); - - decoded_frame->frame_type = AMQP_FRAME_METHOD; - decoded_frame->payload.method.id = D_32(state->inbound_buffer, HEADER_SIZE); - AMQP_CHECK_RESULT(amqp_decode_method(decoded_frame->payload.method.id, - &state->decoding_pool, - encoded, - &decoded_frame->payload.method.decoded)); - break; - } - - case AMQP_FRAME_HEADER: { - amqp_bytes_t encoded; - - /* 12 bytes for properties header. */ - encoded.len = state->target_size - (HEADER_SIZE + 12 + FOOTER_SIZE); - encoded.bytes = D_BYTES(state->inbound_buffer, HEADER_SIZE + 12, encoded.len); - - decoded_frame->frame_type = AMQP_FRAME_HEADER; - decoded_frame->payload.properties.class_id = D_16(state->inbound_buffer, HEADER_SIZE); - decoded_frame->payload.properties.body_size = D_64(state->inbound_buffer, HEADER_SIZE+4); - AMQP_CHECK_RESULT(amqp_decode_properties(decoded_frame->payload.properties.class_id, - &state->decoding_pool, - encoded, - &decoded_frame->payload.properties.decoded)); - break; - } - - case AMQP_FRAME_BODY: { - size_t fragment_len = state->target_size - (HEADER_SIZE + FOOTER_SIZE); - - decoded_frame->frame_type = AMQP_FRAME_BODY; - decoded_frame->payload.body_fragment.len = fragment_len; - decoded_frame->payload.body_fragment.bytes = - D_BYTES(state->inbound_buffer, HEADER_SIZE, fragment_len); - break; - } - - case AMQP_FRAME_HEARTBEAT: - decoded_frame->frame_type = AMQP_FRAME_HEARTBEAT; - break; - - default: - /* Ignore the frame by not changing frame_type away from 0. */ - break; + channel_pool = amqp_get_or_create_channel_pool(state, channel); + if (NULL == channel_pool) { + return AMQP_STATUS_NO_MEMORY; } - return_to_idle(state); - - if(decoded_frame->frame_type == AMQP_FRAME_METHOD && - decoded_frame->payload.method.id == AMQP_BASIC_RETURN_METHOD) { - amqp_basic_return_t *m = decoded_frame->payload.method.decoded; - if(state->basic_return_callback) - state->basic_return_callback(decoded_frame->channel, m, - state->basic_return_callback_data); + 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; - return total_bytes_consumed; + 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; + } - case CONNECTION_STATE_WAITING_FOR_PROTOCOL_HEADER: - decoded_frame->frame_type = AMQP_PSEUDOFRAME_PROTOCOL_HEADER; - decoded_frame->channel = AMQP_PSEUDOFRAME_PROTOCOL_CHANNEL; - amqp_assert(D_8(state->inbound_buffer, 3) == (uint8_t) 'P', - "Invalid protocol header received"); - decoded_frame->payload.protocol_header.transport_high = D_8(state->inbound_buffer, 4); - decoded_frame->payload.protocol_header.transport_low = D_8(state->inbound_buffer, 5); - decoded_frame->payload.protocol_header.protocol_version_major = D_8(state->inbound_buffer, 6); - decoded_frame->payload.protocol_header.protocol_version_minor = D_8(state->inbound_buffer, 7); + 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 total_bytes_consumed; + return (int)bytes_consumed; + } default: - amqp_assert(0, "Internal error: invalid amqp_connection_state_t->state %d", state->state); + 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) && (state->first_queued_frame == NULL); + return (state->state == CONNECTION_STATE_IDLE); } void amqp_release_buffers(amqp_connection_state_t state) { + int i; ENFORCE_STATE(state, CONNECTION_STATE_IDLE); - amqp_assert(state->first_queued_frame == NULL, - "Programming error: attempt to amqp_release_buffers while waiting events enqueued"); + for (i = 0; i < POOL_TABLE_SIZE; ++i) { + amqp_pool_table_entry_t *entry = state->pool_table[i]; - recycle_amqp_pool(&state->frame_pool); - recycle_amqp_pool(&state->decoding_pool); + 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) { @@ -303,132 +426,170 @@ void amqp_maybe_release_buffers(amqp_connection_state_t state) { } } -static int inner_send_frame(amqp_connection_state_t state, - amqp_frame_t const *frame, - amqp_bytes_t *encoded, - int *payload_len) -{ - int separate_body; +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)); - E_8(state->outbound_buffer, 0, frame->frame_type); - E_16(state->outbound_buffer, 1, frame->channel); switch (frame->frame_type) { - case AMQP_FRAME_METHOD: - E_32(state->outbound_buffer, HEADER_SIZE, frame->payload.method.id); - encoded->len = state->outbound_buffer.len - (HEADER_SIZE + 4 + FOOTER_SIZE); - encoded->bytes = D_BYTES(state->outbound_buffer, HEADER_SIZE + 4, encoded->len); - *payload_len = AMQP_CHECK_RESULT(amqp_encode_method(frame->payload.method.id, - frame->payload.method.decoded, - *encoded)) + 4; - separate_body = 0; + 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; + } - case AMQP_FRAME_HEADER: - E_16(state->outbound_buffer, HEADER_SIZE, frame->payload.properties.class_id); - E_16(state->outbound_buffer, HEADER_SIZE+2, 0); /* "weight" */ - E_64(state->outbound_buffer, HEADER_SIZE+4, frame->payload.properties.body_size); - encoded->len = state->outbound_buffer.len - (HEADER_SIZE + 12 + FOOTER_SIZE); - encoded->bytes = D_BYTES(state->outbound_buffer, HEADER_SIZE + 12, encoded->len); - *payload_len = AMQP_CHECK_RESULT(amqp_encode_properties(frame->payload.properties.class_id, - frame->payload.properties.decoded, - *encoded)) + 12; - separate_body = 0; + 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)); - case AMQP_FRAME_BODY: - *encoded = frame->payload.body_fragment; - *payload_len = encoded->len; - separate_body = 1; + 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: - *encoded = AMQP_EMPTY_BYTES; - *payload_len = 0; - separate_body = 0; + out_frame_len = 0; break; default: - return -EINVAL; + return AMQP_STATUS_INVALID_PARAMETER; } - E_32(state->outbound_buffer, 3, *payload_len); - if (!separate_body) { - E_8(state->outbound_buffer, *payload_len + HEADER_SIZE, AMQP_FRAME_END); - } + 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)); -#if 0 - if (separate_body) { - printf("sending body frame (header):\n"); - amqp_dump(state->outbound_buffer.bytes, HEADER_SIZE); - printf("sending body frame (payload):\n"); - amqp_dump(encoded->bytes, *payload_len); - } else { - printf("sending:\n"); - amqp_dump(state->outbound_buffer.bytes, *payload_len + HEADER_SIZE + FOOTER_SIZE); - } -#endif + encoded->bytes = out_frame; + encoded->len = out_frame_len + HEADER_SIZE + FOOTER_SIZE; + + return AMQP_STATUS_OK; +} - return separate_body; +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(amqp_connection_state_t state, - amqp_frame_t const *frame) -{ +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; - int payload_len; - int separate_body; - - separate_body = inner_send_frame(state, frame, &encoded, &payload_len); - switch (separate_body) { - case 0: - AMQP_CHECK_RESULT(write(state->sockfd, - state->outbound_buffer.bytes, - payload_len + (HEADER_SIZE + FOOTER_SIZE))); - return 0; - - case 1: - AMQP_CHECK_RESULT(write(state->sockfd, state->outbound_buffer.bytes, HEADER_SIZE)); - AMQP_CHECK_RESULT(write(state->sockfd, encoded.bytes, payload_len)); - { - unsigned char frame_end_byte = AMQP_FRAME_END; - assert(FOOTER_SIZE == 1); - AMQP_CHECK_RESULT(write(state->sockfd, &frame_end_byte, FOOTER_SIZE)); - } - return 0; + 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; + } - default: - return separate_body; +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; } -} -int amqp_send_frame_to(amqp_connection_state_t state, - amqp_frame_t const *frame, - amqp_output_fn_t fn, - void *context) -{ - amqp_bytes_t encoded; - int payload_len; - int separate_body; - - separate_body = inner_send_frame(state, frame, &encoded, &payload_len); - switch (separate_body) { - case 0: - AMQP_CHECK_RESULT(fn(context, - state->outbound_buffer.bytes, - payload_len + (HEADER_SIZE + FOOTER_SIZE))); - return 0; - - case 1: - AMQP_CHECK_RESULT(fn(context, state->outbound_buffer.bytes, HEADER_SIZE)); - AMQP_CHECK_RESULT(fn(context, encoded.bytes, payload_len)); - { - unsigned char frame_end_byte = AMQP_FRAME_END; - assert(FOOTER_SIZE == 1); - AMQP_CHECK_RESULT(fn(context, &frame_end_byte, FOOTER_SIZE)); - } - return 0; + /* 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; + } - default: - return separate_body; + 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; } diff --git a/src/librabbitmq/amqp_consumer.c b/src/librabbitmq/amqp_consumer.c new file mode 100644 index 0000000..bb9095f --- /dev/null +++ b/src/librabbitmq/amqp_consumer.c @@ -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 +#include + +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; +} diff --git a/src/librabbitmq/amqp_debug.c b/src/librabbitmq/amqp_debug.c index 64db9ec..e33a4fa 100644 --- a/src/librabbitmq/amqp_debug.c +++ b/src/librabbitmq/amqp_debug.c @@ -1,3 +1,5 @@ +#include "amqp_debug.h" + #include #include #include @@ -80,4 +82,4 @@ void amqp_dump(void const *buffer, size_t len) { if (numinrow != 0) printf("%08lX:\n", count); -} +} \ No newline at end of file diff --git a/src/librabbitmq/amqp_debug.h b/src/librabbitmq/amqp_debug.h new file mode 100644 index 0000000..3a48e2b --- /dev/null +++ b/src/librabbitmq/amqp_debug.h @@ -0,0 +1,5 @@ +#include "amqp.h" + +#include + +void amqp_dump(void const *buffer, size_t len); \ No newline at end of file diff --git a/src/librabbitmq/amqp_framing.c b/src/librabbitmq/amqp_framing.c index 258bd66..bdeb01c 100644 --- a/src/librabbitmq/amqp_framing.c +++ b/src/librabbitmq/amqp_framing.c @@ -1,345 +1,611 @@ +/* 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 +#include #include #include -#include -#include -#include /* ntohl, htonl, ntohs, htons */ -#include "amqp.h" -#include "amqp_framing.h" -#include "amqp_private.h" +char const *amqp_constant_name(int constantNumber) { + switch (constantNumber) { + case AMQP_FRAME_METHOD: + return "AMQP_FRAME_METHOD"; + case AMQP_FRAME_HEADER: + return "AMQP_FRAME_HEADER"; + case AMQP_FRAME_BODY: + return "AMQP_FRAME_BODY"; + case AMQP_FRAME_HEARTBEAT: + return "AMQP_FRAME_HEARTBEAT"; + case AMQP_FRAME_MIN_SIZE: + return "AMQP_FRAME_MIN_SIZE"; + case AMQP_FRAME_END: + return "AMQP_FRAME_END"; + case AMQP_REPLY_SUCCESS: + return "AMQP_REPLY_SUCCESS"; + case AMQP_CONTENT_TOO_LARGE: + return "AMQP_CONTENT_TOO_LARGE"; + case AMQP_NO_ROUTE: + return "AMQP_NO_ROUTE"; + case AMQP_NO_CONSUMERS: + return "AMQP_NO_CONSUMERS"; + case AMQP_ACCESS_REFUSED: + return "AMQP_ACCESS_REFUSED"; + case AMQP_NOT_FOUND: + return "AMQP_NOT_FOUND"; + case AMQP_RESOURCE_LOCKED: + return "AMQP_RESOURCE_LOCKED"; + case AMQP_PRECONDITION_FAILED: + return "AMQP_PRECONDITION_FAILED"; + case AMQP_CONNECTION_FORCED: + return "AMQP_CONNECTION_FORCED"; + case AMQP_INVALID_PATH: + return "AMQP_INVALID_PATH"; + case AMQP_FRAME_ERROR: + return "AMQP_FRAME_ERROR"; + case AMQP_SYNTAX_ERROR: + return "AMQP_SYNTAX_ERROR"; + case AMQP_COMMAND_INVALID: + return "AMQP_COMMAND_INVALID"; + case AMQP_CHANNEL_ERROR: + return "AMQP_CHANNEL_ERROR"; + case AMQP_UNEXPECTED_FRAME: + return "AMQP_UNEXPECTED_FRAME"; + case AMQP_RESOURCE_ERROR: + return "AMQP_RESOURCE_ERROR"; + case AMQP_NOT_ALLOWED: + return "AMQP_NOT_ALLOWED"; + case AMQP_NOT_IMPLEMENTED: + return "AMQP_NOT_IMPLEMENTED"; + case AMQP_INTERNAL_ERROR: + return "AMQP_INTERNAL_ERROR"; + default: + return "(unknown)"; + } +} + +amqp_boolean_t amqp_constant_is_hard_error(int constantNumber) { + switch (constantNumber) { + case AMQP_CONNECTION_FORCED: + return 1; + case AMQP_INVALID_PATH: + return 1; + case AMQP_FRAME_ERROR: + return 1; + case AMQP_SYNTAX_ERROR: + return 1; + case AMQP_COMMAND_INVALID: + return 1; + case AMQP_CHANNEL_ERROR: + return 1; + case AMQP_UNEXPECTED_FRAME: + return 1; + case AMQP_RESOURCE_ERROR: + return 1; + case AMQP_NOT_ALLOWED: + return 1; + case AMQP_NOT_IMPLEMENTED: + return 1; + case AMQP_INTERNAL_ERROR: + return 1; + default: + return 0; + } +} char const *amqp_method_name(amqp_method_number_t methodNumber) { switch (methodNumber) { - case AMQP_CONNECTION_START_METHOD: return "AMQP_CONNECTION_START_METHOD"; - case AMQP_CONNECTION_START_OK_METHOD: return "AMQP_CONNECTION_START_OK_METHOD"; - case AMQP_CONNECTION_SECURE_METHOD: return "AMQP_CONNECTION_SECURE_METHOD"; - case AMQP_CONNECTION_SECURE_OK_METHOD: return "AMQP_CONNECTION_SECURE_OK_METHOD"; - case AMQP_CONNECTION_TUNE_METHOD: return "AMQP_CONNECTION_TUNE_METHOD"; - case AMQP_CONNECTION_TUNE_OK_METHOD: return "AMQP_CONNECTION_TUNE_OK_METHOD"; - case AMQP_CONNECTION_OPEN_METHOD: return "AMQP_CONNECTION_OPEN_METHOD"; - case AMQP_CONNECTION_OPEN_OK_METHOD: return "AMQP_CONNECTION_OPEN_OK_METHOD"; - case AMQP_CONNECTION_REDIRECT_METHOD: return "AMQP_CONNECTION_REDIRECT_METHOD"; - case AMQP_CONNECTION_CLOSE_METHOD: return "AMQP_CONNECTION_CLOSE_METHOD"; - case AMQP_CONNECTION_CLOSE_OK_METHOD: return "AMQP_CONNECTION_CLOSE_OK_METHOD"; - case AMQP_CHANNEL_OPEN_METHOD: return "AMQP_CHANNEL_OPEN_METHOD"; - case AMQP_CHANNEL_OPEN_OK_METHOD: return "AMQP_CHANNEL_OPEN_OK_METHOD"; - case AMQP_CHANNEL_FLOW_METHOD: return "AMQP_CHANNEL_FLOW_METHOD"; - case AMQP_CHANNEL_FLOW_OK_METHOD: return "AMQP_CHANNEL_FLOW_OK_METHOD"; - case AMQP_CHANNEL_ALERT_METHOD: return "AMQP_CHANNEL_ALERT_METHOD"; - case AMQP_CHANNEL_CLOSE_METHOD: return "AMQP_CHANNEL_CLOSE_METHOD"; - case AMQP_CHANNEL_CLOSE_OK_METHOD: return "AMQP_CHANNEL_CLOSE_OK_METHOD"; - case AMQP_ACCESS_REQUEST_METHOD: return "AMQP_ACCESS_REQUEST_METHOD"; - case AMQP_ACCESS_REQUEST_OK_METHOD: return "AMQP_ACCESS_REQUEST_OK_METHOD"; - case AMQP_EXCHANGE_DECLARE_METHOD: return "AMQP_EXCHANGE_DECLARE_METHOD"; - case AMQP_EXCHANGE_DECLARE_OK_METHOD: return "AMQP_EXCHANGE_DECLARE_OK_METHOD"; - case AMQP_EXCHANGE_DELETE_METHOD: return "AMQP_EXCHANGE_DELETE_METHOD"; - case AMQP_EXCHANGE_DELETE_OK_METHOD: return "AMQP_EXCHANGE_DELETE_OK_METHOD"; - case AMQP_QUEUE_DECLARE_METHOD: return "AMQP_QUEUE_DECLARE_METHOD"; - case AMQP_QUEUE_DECLARE_OK_METHOD: return "AMQP_QUEUE_DECLARE_OK_METHOD"; - case AMQP_QUEUE_BIND_METHOD: return "AMQP_QUEUE_BIND_METHOD"; - case AMQP_QUEUE_BIND_OK_METHOD: return "AMQP_QUEUE_BIND_OK_METHOD"; - case AMQP_QUEUE_PURGE_METHOD: return "AMQP_QUEUE_PURGE_METHOD"; - case AMQP_QUEUE_PURGE_OK_METHOD: return "AMQP_QUEUE_PURGE_OK_METHOD"; - case AMQP_QUEUE_DELETE_METHOD: return "AMQP_QUEUE_DELETE_METHOD"; - case AMQP_QUEUE_DELETE_OK_METHOD: return "AMQP_QUEUE_DELETE_OK_METHOD"; - case AMQP_QUEUE_UNBIND_METHOD: return "AMQP_QUEUE_UNBIND_METHOD"; - case AMQP_QUEUE_UNBIND_OK_METHOD: return "AMQP_QUEUE_UNBIND_OK_METHOD"; - case AMQP_BASIC_QOS_METHOD: return "AMQP_BASIC_QOS_METHOD"; - case AMQP_BASIC_QOS_OK_METHOD: return "AMQP_BASIC_QOS_OK_METHOD"; - case AMQP_BASIC_CONSUME_METHOD: return "AMQP_BASIC_CONSUME_METHOD"; - case AMQP_BASIC_CONSUME_OK_METHOD: return "AMQP_BASIC_CONSUME_OK_METHOD"; - case AMQP_BASIC_CANCEL_METHOD: return "AMQP_BASIC_CANCEL_METHOD"; - case AMQP_BASIC_CANCEL_OK_METHOD: return "AMQP_BASIC_CANCEL_OK_METHOD"; - case AMQP_BASIC_PUBLISH_METHOD: return "AMQP_BASIC_PUBLISH_METHOD"; - case AMQP_BASIC_RETURN_METHOD: return "AMQP_BASIC_RETURN_METHOD"; - case AMQP_BASIC_DELIVER_METHOD: return "AMQP_BASIC_DELIVER_METHOD"; - case AMQP_BASIC_GET_METHOD: return "AMQP_BASIC_GET_METHOD"; - case AMQP_BASIC_GET_OK_METHOD: return "AMQP_BASIC_GET_OK_METHOD"; - case AMQP_BASIC_GET_EMPTY_METHOD: return "AMQP_BASIC_GET_EMPTY_METHOD"; - case AMQP_BASIC_ACK_METHOD: return "AMQP_BASIC_ACK_METHOD"; - case AMQP_BASIC_REJECT_METHOD: return "AMQP_BASIC_REJECT_METHOD"; - case AMQP_BASIC_RECOVER_METHOD: return "AMQP_BASIC_RECOVER_METHOD"; - case AMQP_FILE_QOS_METHOD: return "AMQP_FILE_QOS_METHOD"; - case AMQP_FILE_QOS_OK_METHOD: return "AMQP_FILE_QOS_OK_METHOD"; - case AMQP_FILE_CONSUME_METHOD: return "AMQP_FILE_CONSUME_METHOD"; - case AMQP_FILE_CONSUME_OK_METHOD: return "AMQP_FILE_CONSUME_OK_METHOD"; - case AMQP_FILE_CANCEL_METHOD: return "AMQP_FILE_CANCEL_METHOD"; - case AMQP_FILE_CANCEL_OK_METHOD: return "AMQP_FILE_CANCEL_OK_METHOD"; - case AMQP_FILE_OPEN_METHOD: return "AMQP_FILE_OPEN_METHOD"; - case AMQP_FILE_OPEN_OK_METHOD: return "AMQP_FILE_OPEN_OK_METHOD"; - case AMQP_FILE_STAGE_METHOD: return "AMQP_FILE_STAGE_METHOD"; - case AMQP_FILE_PUBLISH_METHOD: return "AMQP_FILE_PUBLISH_METHOD"; - case AMQP_FILE_RETURN_METHOD: return "AMQP_FILE_RETURN_METHOD"; - case AMQP_FILE_DELIVER_METHOD: return "AMQP_FILE_DELIVER_METHOD"; - case AMQP_FILE_ACK_METHOD: return "AMQP_FILE_ACK_METHOD"; - case AMQP_FILE_REJECT_METHOD: return "AMQP_FILE_REJECT_METHOD"; - case AMQP_STREAM_QOS_METHOD: return "AMQP_STREAM_QOS_METHOD"; - case AMQP_STREAM_QOS_OK_METHOD: return "AMQP_STREAM_QOS_OK_METHOD"; - case AMQP_STREAM_CONSUME_METHOD: return "AMQP_STREAM_CONSUME_METHOD"; - case AMQP_STREAM_CONSUME_OK_METHOD: return "AMQP_STREAM_CONSUME_OK_METHOD"; - case AMQP_STREAM_CANCEL_METHOD: return "AMQP_STREAM_CANCEL_METHOD"; - case AMQP_STREAM_CANCEL_OK_METHOD: return "AMQP_STREAM_CANCEL_OK_METHOD"; - case AMQP_STREAM_PUBLISH_METHOD: return "AMQP_STREAM_PUBLISH_METHOD"; - case AMQP_STREAM_RETURN_METHOD: return "AMQP_STREAM_RETURN_METHOD"; - case AMQP_STREAM_DELIVER_METHOD: return "AMQP_STREAM_DELIVER_METHOD"; - case AMQP_TX_SELECT_METHOD: return "AMQP_TX_SELECT_METHOD"; - case AMQP_TX_SELECT_OK_METHOD: return "AMQP_TX_SELECT_OK_METHOD"; - case AMQP_TX_COMMIT_METHOD: return "AMQP_TX_COMMIT_METHOD"; - case AMQP_TX_COMMIT_OK_METHOD: return "AMQP_TX_COMMIT_OK_METHOD"; - case AMQP_TX_ROLLBACK_METHOD: return "AMQP_TX_ROLLBACK_METHOD"; - case AMQP_TX_ROLLBACK_OK_METHOD: return "AMQP_TX_ROLLBACK_OK_METHOD"; - case AMQP_DTX_SELECT_METHOD: return "AMQP_DTX_SELECT_METHOD"; - case AMQP_DTX_SELECT_OK_METHOD: return "AMQP_DTX_SELECT_OK_METHOD"; - case AMQP_DTX_START_METHOD: return "AMQP_DTX_START_METHOD"; - case AMQP_DTX_START_OK_METHOD: return "AMQP_DTX_START_OK_METHOD"; - case AMQP_TUNNEL_REQUEST_METHOD: return "AMQP_TUNNEL_REQUEST_METHOD"; - case AMQP_TEST_INTEGER_METHOD: return "AMQP_TEST_INTEGER_METHOD"; - case AMQP_TEST_INTEGER_OK_METHOD: return "AMQP_TEST_INTEGER_OK_METHOD"; - case AMQP_TEST_STRING_METHOD: return "AMQP_TEST_STRING_METHOD"; - case AMQP_TEST_STRING_OK_METHOD: return "AMQP_TEST_STRING_OK_METHOD"; - case AMQP_TEST_TABLE_METHOD: return "AMQP_TEST_TABLE_METHOD"; - case AMQP_TEST_TABLE_OK_METHOD: return "AMQP_TEST_TABLE_OK_METHOD"; - case AMQP_TEST_CONTENT_METHOD: return "AMQP_TEST_CONTENT_METHOD"; - case AMQP_TEST_CONTENT_OK_METHOD: return "AMQP_TEST_CONTENT_OK_METHOD"; - default: return NULL; + case AMQP_CONNECTION_START_METHOD: + return "AMQP_CONNECTION_START_METHOD"; + case AMQP_CONNECTION_START_OK_METHOD: + return "AMQP_CONNECTION_START_OK_METHOD"; + case AMQP_CONNECTION_SECURE_METHOD: + return "AMQP_CONNECTION_SECURE_METHOD"; + case AMQP_CONNECTION_SECURE_OK_METHOD: + return "AMQP_CONNECTION_SECURE_OK_METHOD"; + case AMQP_CONNECTION_TUNE_METHOD: + return "AMQP_CONNECTION_TUNE_METHOD"; + case AMQP_CONNECTION_TUNE_OK_METHOD: + return "AMQP_CONNECTION_TUNE_OK_METHOD"; + case AMQP_CONNECTION_OPEN_METHOD: + return "AMQP_CONNECTION_OPEN_METHOD"; + case AMQP_CONNECTION_OPEN_OK_METHOD: + return "AMQP_CONNECTION_OPEN_OK_METHOD"; + case AMQP_CONNECTION_CLOSE_METHOD: + return "AMQP_CONNECTION_CLOSE_METHOD"; + case AMQP_CONNECTION_CLOSE_OK_METHOD: + return "AMQP_CONNECTION_CLOSE_OK_METHOD"; + case AMQP_CONNECTION_BLOCKED_METHOD: + return "AMQP_CONNECTION_BLOCKED_METHOD"; + case AMQP_CONNECTION_UNBLOCKED_METHOD: + return "AMQP_CONNECTION_UNBLOCKED_METHOD"; + case AMQP_CHANNEL_OPEN_METHOD: + return "AMQP_CHANNEL_OPEN_METHOD"; + case AMQP_CHANNEL_OPEN_OK_METHOD: + return "AMQP_CHANNEL_OPEN_OK_METHOD"; + case AMQP_CHANNEL_FLOW_METHOD: + return "AMQP_CHANNEL_FLOW_METHOD"; + case AMQP_CHANNEL_FLOW_OK_METHOD: + return "AMQP_CHANNEL_FLOW_OK_METHOD"; + case AMQP_CHANNEL_CLOSE_METHOD: + return "AMQP_CHANNEL_CLOSE_METHOD"; + case AMQP_CHANNEL_CLOSE_OK_METHOD: + return "AMQP_CHANNEL_CLOSE_OK_METHOD"; + case AMQP_ACCESS_REQUEST_METHOD: + return "AMQP_ACCESS_REQUEST_METHOD"; + case AMQP_ACCESS_REQUEST_OK_METHOD: + return "AMQP_ACCESS_REQUEST_OK_METHOD"; + case AMQP_EXCHANGE_DECLARE_METHOD: + return "AMQP_EXCHANGE_DECLARE_METHOD"; + case AMQP_EXCHANGE_DECLARE_OK_METHOD: + return "AMQP_EXCHANGE_DECLARE_OK_METHOD"; + case AMQP_EXCHANGE_DELETE_METHOD: + return "AMQP_EXCHANGE_DELETE_METHOD"; + case AMQP_EXCHANGE_DELETE_OK_METHOD: + return "AMQP_EXCHANGE_DELETE_OK_METHOD"; + case AMQP_EXCHANGE_BIND_METHOD: + return "AMQP_EXCHANGE_BIND_METHOD"; + case AMQP_EXCHANGE_BIND_OK_METHOD: + return "AMQP_EXCHANGE_BIND_OK_METHOD"; + case AMQP_EXCHANGE_UNBIND_METHOD: + return "AMQP_EXCHANGE_UNBIND_METHOD"; + case AMQP_EXCHANGE_UNBIND_OK_METHOD: + return "AMQP_EXCHANGE_UNBIND_OK_METHOD"; + case AMQP_QUEUE_DECLARE_METHOD: + return "AMQP_QUEUE_DECLARE_METHOD"; + case AMQP_QUEUE_DECLARE_OK_METHOD: + return "AMQP_QUEUE_DECLARE_OK_METHOD"; + case AMQP_QUEUE_BIND_METHOD: + return "AMQP_QUEUE_BIND_METHOD"; + case AMQP_QUEUE_BIND_OK_METHOD: + return "AMQP_QUEUE_BIND_OK_METHOD"; + case AMQP_QUEUE_PURGE_METHOD: + return "AMQP_QUEUE_PURGE_METHOD"; + case AMQP_QUEUE_PURGE_OK_METHOD: + return "AMQP_QUEUE_PURGE_OK_METHOD"; + case AMQP_QUEUE_DELETE_METHOD: + return "AMQP_QUEUE_DELETE_METHOD"; + case AMQP_QUEUE_DELETE_OK_METHOD: + return "AMQP_QUEUE_DELETE_OK_METHOD"; + case AMQP_QUEUE_UNBIND_METHOD: + return "AMQP_QUEUE_UNBIND_METHOD"; + case AMQP_QUEUE_UNBIND_OK_METHOD: + return "AMQP_QUEUE_UNBIND_OK_METHOD"; + case AMQP_BASIC_QOS_METHOD: + return "AMQP_BASIC_QOS_METHOD"; + case AMQP_BASIC_QOS_OK_METHOD: + return "AMQP_BASIC_QOS_OK_METHOD"; + case AMQP_BASIC_CONSUME_METHOD: + return "AMQP_BASIC_CONSUME_METHOD"; + case AMQP_BASIC_CONSUME_OK_METHOD: + return "AMQP_BASIC_CONSUME_OK_METHOD"; + case AMQP_BASIC_CANCEL_METHOD: + return "AMQP_BASIC_CANCEL_METHOD"; + case AMQP_BASIC_CANCEL_OK_METHOD: + return "AMQP_BASIC_CANCEL_OK_METHOD"; + case AMQP_BASIC_PUBLISH_METHOD: + return "AMQP_BASIC_PUBLISH_METHOD"; + case AMQP_BASIC_RETURN_METHOD: + return "AMQP_BASIC_RETURN_METHOD"; + case AMQP_BASIC_DELIVER_METHOD: + return "AMQP_BASIC_DELIVER_METHOD"; + case AMQP_BASIC_GET_METHOD: + return "AMQP_BASIC_GET_METHOD"; + case AMQP_BASIC_GET_OK_METHOD: + return "AMQP_BASIC_GET_OK_METHOD"; + case AMQP_BASIC_GET_EMPTY_METHOD: + return "AMQP_BASIC_GET_EMPTY_METHOD"; + case AMQP_BASIC_ACK_METHOD: + return "AMQP_BASIC_ACK_METHOD"; + case AMQP_BASIC_REJECT_METHOD: + return "AMQP_BASIC_REJECT_METHOD"; + case AMQP_BASIC_RECOVER_ASYNC_METHOD: + return "AMQP_BASIC_RECOVER_ASYNC_METHOD"; + case AMQP_BASIC_RECOVER_METHOD: + return "AMQP_BASIC_RECOVER_METHOD"; + case AMQP_BASIC_RECOVER_OK_METHOD: + return "AMQP_BASIC_RECOVER_OK_METHOD"; + case AMQP_BASIC_NACK_METHOD: + return "AMQP_BASIC_NACK_METHOD"; + case AMQP_TX_SELECT_METHOD: + return "AMQP_TX_SELECT_METHOD"; + case AMQP_TX_SELECT_OK_METHOD: + return "AMQP_TX_SELECT_OK_METHOD"; + case AMQP_TX_COMMIT_METHOD: + return "AMQP_TX_COMMIT_METHOD"; + case AMQP_TX_COMMIT_OK_METHOD: + return "AMQP_TX_COMMIT_OK_METHOD"; + case AMQP_TX_ROLLBACK_METHOD: + return "AMQP_TX_ROLLBACK_METHOD"; + case AMQP_TX_ROLLBACK_OK_METHOD: + return "AMQP_TX_ROLLBACK_OK_METHOD"; + case AMQP_CONFIRM_SELECT_METHOD: + return "AMQP_CONFIRM_SELECT_METHOD"; + case AMQP_CONFIRM_SELECT_OK_METHOD: + return "AMQP_CONFIRM_SELECT_OK_METHOD"; + default: + return NULL; } } amqp_boolean_t amqp_method_has_content(amqp_method_number_t methodNumber) { switch (methodNumber) { - case AMQP_BASIC_PUBLISH_METHOD: return 1; - case AMQP_BASIC_RETURN_METHOD: return 1; - case AMQP_BASIC_DELIVER_METHOD: return 1; - case AMQP_BASIC_GET_OK_METHOD: return 1; - case AMQP_FILE_STAGE_METHOD: return 1; - case AMQP_FILE_RETURN_METHOD: return 1; - case AMQP_STREAM_PUBLISH_METHOD: return 1; - case AMQP_STREAM_RETURN_METHOD: return 1; - case AMQP_STREAM_DELIVER_METHOD: return 1; - case AMQP_TUNNEL_REQUEST_METHOD: return 1; - case AMQP_TEST_CONTENT_METHOD: return 1; - case AMQP_TEST_CONTENT_OK_METHOD: return 1; - default: return 0; + case AMQP_BASIC_PUBLISH_METHOD: + return 1; + case AMQP_BASIC_RETURN_METHOD: + return 1; + case AMQP_BASIC_DELIVER_METHOD: + return 1; + case AMQP_BASIC_GET_OK_METHOD: + return 1; + default: + return 0; } } -int amqp_decode_method(amqp_method_number_t methodNumber, - amqp_pool_t *pool, - amqp_bytes_t encoded, - void **decoded) -{ - int offset = 0; - int table_result; +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) { case AMQP_CONNECTION_START_METHOD: { - amqp_connection_start_t *m = (amqp_connection_start_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_start_t)); - m->version_major = D_8(encoded, offset); - offset++; - m->version_minor = D_8(encoded, offset); - offset++; - table_result = amqp_decode_table(encoded, pool, &(m->server_properties), &offset); - AMQP_CHECK_RESULT(table_result); - m->mechanisms.len = D_32(encoded, offset); - offset += 4; - m->mechanisms.bytes = D_BYTES(encoded, offset, m->mechanisms.len); - offset += m->mechanisms.len; - m->locales.len = D_32(encoded, offset); - offset += 4; - m->locales.bytes = D_BYTES(encoded, offset, m->locales.len); - offset += m->locales.len; + amqp_connection_start_t *m = (amqp_connection_start_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_start_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &m->version_major)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &m->version_minor)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = + amqp_decode_table(encoded, pool, &(m->server_properties), &offset); + if (res < 0) return res; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->mechanisms, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->locales, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } case AMQP_CONNECTION_START_OK_METHOD: { - amqp_connection_start_ok_t *m = (amqp_connection_start_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_start_ok_t)); - table_result = amqp_decode_table(encoded, pool, &(m->client_properties), &offset); - AMQP_CHECK_RESULT(table_result); - m->mechanism.len = D_8(encoded, offset); - offset++; - m->mechanism.bytes = D_BYTES(encoded, offset, m->mechanism.len); - offset += m->mechanism.len; - m->response.len = D_32(encoded, offset); - offset += 4; - m->response.bytes = D_BYTES(encoded, offset, m->response.len); - offset += m->response.len; - m->locale.len = D_8(encoded, offset); - offset++; - m->locale.bytes = D_BYTES(encoded, offset, m->locale.len); - offset += m->locale.len; + amqp_connection_start_ok_t *m = + (amqp_connection_start_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_start_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + int res = + amqp_decode_table(encoded, pool, &(m->client_properties), &offset); + if (res < 0) return res; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->mechanism, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->response, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->locale, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } case AMQP_CONNECTION_SECURE_METHOD: { - amqp_connection_secure_t *m = (amqp_connection_secure_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_secure_t)); - m->challenge.len = D_32(encoded, offset); - offset += 4; - m->challenge.bytes = D_BYTES(encoded, offset, m->challenge.len); - offset += m->challenge.len; + amqp_connection_secure_t *m = (amqp_connection_secure_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_secure_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->challenge, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } case AMQP_CONNECTION_SECURE_OK_METHOD: { - amqp_connection_secure_ok_t *m = (amqp_connection_secure_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_secure_ok_t)); - m->response.len = D_32(encoded, offset); - offset += 4; - m->response.bytes = D_BYTES(encoded, offset, m->response.len); - offset += m->response.len; + amqp_connection_secure_ok_t *m = + (amqp_connection_secure_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_secure_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->response, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } case AMQP_CONNECTION_TUNE_METHOD: { - amqp_connection_tune_t *m = (amqp_connection_tune_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_tune_t)); - m->channel_max = D_16(encoded, offset); - offset += 2; - m->frame_max = D_32(encoded, offset); - offset += 4; - m->heartbeat = D_16(encoded, offset); - offset += 2; + amqp_connection_tune_t *m = (amqp_connection_tune_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_tune_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->channel_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_32(encoded, &offset, &m->frame_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_16(encoded, &offset, &m->heartbeat)) + return AMQP_STATUS_BAD_AMQP_DATA; *decoded = m; return 0; } case AMQP_CONNECTION_TUNE_OK_METHOD: { - amqp_connection_tune_ok_t *m = (amqp_connection_tune_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_tune_ok_t)); - m->channel_max = D_16(encoded, offset); - offset += 2; - m->frame_max = D_32(encoded, offset); - offset += 4; - m->heartbeat = D_16(encoded, offset); - offset += 2; + amqp_connection_tune_ok_t *m = + (amqp_connection_tune_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_tune_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->channel_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_32(encoded, &offset, &m->frame_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_16(encoded, &offset, &m->heartbeat)) + return AMQP_STATUS_BAD_AMQP_DATA; *decoded = m; return 0; } case AMQP_CONNECTION_OPEN_METHOD: { - amqp_connection_open_t *m = (amqp_connection_open_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_open_t)); - m->virtual_host.len = D_8(encoded, offset); - offset++; - m->virtual_host.bytes = D_BYTES(encoded, offset, m->virtual_host.len); - offset += m->virtual_host.len; - m->capabilities.len = D_8(encoded, offset); - offset++; - m->capabilities.bytes = D_BYTES(encoded, offset, m->capabilities.len); - offset += m->capabilities.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_connection_open_t *m = (amqp_connection_open_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_open_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->virtual_host, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->capabilities, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->insist = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } case AMQP_CONNECTION_OPEN_OK_METHOD: { - amqp_connection_open_ok_t *m = (amqp_connection_open_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_open_ok_t)); - m->known_hosts.len = D_8(encoded, offset); - offset++; - m->known_hosts.bytes = D_BYTES(encoded, offset, m->known_hosts.len); - offset += m->known_hosts.len; + amqp_connection_open_ok_t *m = + (amqp_connection_open_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_open_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->known_hosts, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } - case AMQP_CONNECTION_REDIRECT_METHOD: { - amqp_connection_redirect_t *m = (amqp_connection_redirect_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_redirect_t)); - m->host.len = D_8(encoded, offset); - offset++; - m->host.bytes = D_BYTES(encoded, offset, m->host.len); - offset += m->host.len; - m->known_hosts.len = D_8(encoded, offset); - offset++; - m->known_hosts.bytes = D_BYTES(encoded, offset, m->known_hosts.len); - offset += m->known_hosts.len; + case AMQP_CONNECTION_CLOSE_METHOD: { + amqp_connection_close_t *m = (amqp_connection_close_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_close_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->reply_text, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_16(encoded, &offset, &m->class_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_16(encoded, &offset, &m->method_id)) + return AMQP_STATUS_BAD_AMQP_DATA; *decoded = m; return 0; } - case AMQP_CONNECTION_CLOSE_METHOD: { - amqp_connection_close_t *m = (amqp_connection_close_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_close_t)); - m->reply_code = D_16(encoded, offset); - offset += 2; - m->reply_text.len = D_8(encoded, offset); - offset++; - m->reply_text.bytes = D_BYTES(encoded, offset, m->reply_text.len); - offset += m->reply_text.len; - m->class_id = D_16(encoded, offset); - offset += 2; - m->method_id = D_16(encoded, offset); - offset += 2; + case AMQP_CONNECTION_CLOSE_OK_METHOD: { + amqp_connection_close_ok_t *m = + (amqp_connection_close_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_close_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } - case AMQP_CONNECTION_CLOSE_OK_METHOD: { - amqp_connection_close_ok_t *m = (amqp_connection_close_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_close_ok_t)); + case AMQP_CONNECTION_BLOCKED_METHOD: { + amqp_connection_blocked_t *m = + (amqp_connection_blocked_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_blocked_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->reason, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_CONNECTION_UNBLOCKED_METHOD: { + amqp_connection_unblocked_t *m = + (amqp_connection_unblocked_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_unblocked_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_CHANNEL_OPEN_METHOD: { - amqp_channel_open_t *m = (amqp_channel_open_t *) amqp_pool_alloc(pool, sizeof(amqp_channel_open_t)); - m->out_of_band.len = D_8(encoded, offset); - offset++; - m->out_of_band.bytes = D_BYTES(encoded, offset, m->out_of_band.len); - offset += m->out_of_band.len; + amqp_channel_open_t *m = (amqp_channel_open_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_open_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->out_of_band, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } case AMQP_CHANNEL_OPEN_OK_METHOD: { - amqp_channel_open_ok_t *m = (amqp_channel_open_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_channel_open_ok_t)); + amqp_channel_open_ok_t *m = (amqp_channel_open_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_open_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->channel_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } case AMQP_CHANNEL_FLOW_METHOD: { - amqp_channel_flow_t *m = (amqp_channel_flow_t *) amqp_pool_alloc(pool, sizeof(amqp_channel_flow_t)); - bit_buffer = D_8(encoded, offset); - offset++; + amqp_channel_flow_t *m = (amqp_channel_flow_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_flow_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->active = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } case AMQP_CHANNEL_FLOW_OK_METHOD: { - amqp_channel_flow_ok_t *m = (amqp_channel_flow_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_channel_flow_ok_t)); - bit_buffer = D_8(encoded, offset); - offset++; + amqp_channel_flow_ok_t *m = (amqp_channel_flow_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_flow_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->active = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } - case AMQP_CHANNEL_ALERT_METHOD: { - amqp_channel_alert_t *m = (amqp_channel_alert_t *) amqp_pool_alloc(pool, sizeof(amqp_channel_alert_t)); - m->reply_code = D_16(encoded, offset); - offset += 2; - m->reply_text.len = D_8(encoded, offset); - offset++; - m->reply_text.bytes = D_BYTES(encoded, offset, m->reply_text.len); - offset += m->reply_text.len; - table_result = amqp_decode_table(encoded, pool, &(m->details), &offset); - AMQP_CHECK_RESULT(table_result); - *decoded = m; - return 0; - } case AMQP_CHANNEL_CLOSE_METHOD: { - amqp_channel_close_t *m = (amqp_channel_close_t *) amqp_pool_alloc(pool, sizeof(amqp_channel_close_t)); - m->reply_code = D_16(encoded, offset); - offset += 2; - m->reply_text.len = D_8(encoded, offset); - offset++; - m->reply_text.bytes = D_BYTES(encoded, offset, m->reply_text.len); - offset += m->reply_text.len; - m->class_id = D_16(encoded, offset); - offset += 2; - m->method_id = D_16(encoded, offset); - offset += 2; + amqp_channel_close_t *m = (amqp_channel_close_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_close_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->reply_text, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_16(encoded, &offset, &m->class_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_16(encoded, &offset, &m->method_id)) + return AMQP_STATUS_BAD_AMQP_DATA; *decoded = m; return 0; } case AMQP_CHANNEL_CLOSE_OK_METHOD: { - amqp_channel_close_ok_t *m = (amqp_channel_close_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_channel_close_ok_t)); + amqp_channel_close_ok_t *m = (amqp_channel_close_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_close_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_ACCESS_REQUEST_METHOD: { - amqp_access_request_t *m = (amqp_access_request_t *) amqp_pool_alloc(pool, sizeof(amqp_access_request_t)); - m->realm.len = D_8(encoded, offset); - offset++; - m->realm.bytes = D_BYTES(encoded, offset, m->realm.len); - offset += m->realm.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_access_request_t *m = (amqp_access_request_t *)amqp_pool_alloc( + pool, sizeof(amqp_access_request_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->realm, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->exclusive = (bit_buffer & (1 << 0)) ? 1 : 0; m->passive = (bit_buffer & (1 << 1)) ? 1 : 0; m->active = (bit_buffer & (1 << 2)) ? 1 : 0; @@ -349,154 +615,321 @@ int amqp_decode_method(amqp_method_number_t methodNumber, return 0; } case AMQP_ACCESS_REQUEST_OK_METHOD: { - amqp_access_request_ok_t *m = (amqp_access_request_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_access_request_ok_t)); - m->ticket = D_16(encoded, offset); - offset += 2; + amqp_access_request_ok_t *m = (amqp_access_request_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_access_request_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; *decoded = m; return 0; } case AMQP_EXCHANGE_DECLARE_METHOD: { - amqp_exchange_declare_t *m = (amqp_exchange_declare_t *) amqp_pool_alloc(pool, sizeof(amqp_exchange_declare_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->type.len = D_8(encoded, offset); - offset++; - m->type.bytes = D_BYTES(encoded, offset, m->type.len); - offset += m->type.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_exchange_declare_t *m = (amqp_exchange_declare_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_declare_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->type, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->passive = (bit_buffer & (1 << 0)) ? 1 : 0; m->durable = (bit_buffer & (1 << 1)) ? 1 : 0; m->auto_delete = (bit_buffer & (1 << 2)) ? 1 : 0; m->internal = (bit_buffer & (1 << 3)) ? 1 : 0; m->nowait = (bit_buffer & (1 << 4)) ? 1 : 0; - table_result = amqp_decode_table(encoded, pool, &(m->arguments), &offset); - AMQP_CHECK_RESULT(table_result); + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } *decoded = m; return 0; } case AMQP_EXCHANGE_DECLARE_OK_METHOD: { - amqp_exchange_declare_ok_t *m = (amqp_exchange_declare_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_exchange_declare_ok_t)); + amqp_exchange_declare_ok_t *m = + (amqp_exchange_declare_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_declare_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_EXCHANGE_DELETE_METHOD: { - amqp_exchange_delete_t *m = (amqp_exchange_delete_t *) amqp_pool_alloc(pool, sizeof(amqp_exchange_delete_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_exchange_delete_t *m = (amqp_exchange_delete_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_delete_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->if_unused = (bit_buffer & (1 << 0)) ? 1 : 0; m->nowait = (bit_buffer & (1 << 1)) ? 1 : 0; *decoded = m; return 0; } case AMQP_EXCHANGE_DELETE_OK_METHOD: { - amqp_exchange_delete_ok_t *m = (amqp_exchange_delete_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_exchange_delete_ok_t)); + amqp_exchange_delete_ok_t *m = + (amqp_exchange_delete_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_delete_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_BIND_METHOD: { + amqp_exchange_bind_t *m = (amqp_exchange_bind_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_bind_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->destination, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->source, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_BIND_OK_METHOD: { + amqp_exchange_bind_ok_t *m = (amqp_exchange_bind_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_bind_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_UNBIND_METHOD: { + amqp_exchange_unbind_t *m = (amqp_exchange_unbind_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_unbind_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->destination, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->source, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_UNBIND_OK_METHOD: { + amqp_exchange_unbind_ok_t *m = + (amqp_exchange_unbind_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_unbind_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_QUEUE_DECLARE_METHOD: { - amqp_queue_declare_t *m = (amqp_queue_declare_t *) amqp_pool_alloc(pool, sizeof(amqp_queue_declare_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->queue.len = D_8(encoded, offset); - offset++; - m->queue.bytes = D_BYTES(encoded, offset, m->queue.len); - offset += m->queue.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_queue_declare_t *m = (amqp_queue_declare_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_declare_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->passive = (bit_buffer & (1 << 0)) ? 1 : 0; m->durable = (bit_buffer & (1 << 1)) ? 1 : 0; m->exclusive = (bit_buffer & (1 << 2)) ? 1 : 0; m->auto_delete = (bit_buffer & (1 << 3)) ? 1 : 0; m->nowait = (bit_buffer & (1 << 4)) ? 1 : 0; - table_result = amqp_decode_table(encoded, pool, &(m->arguments), &offset); - AMQP_CHECK_RESULT(table_result); + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } *decoded = m; return 0; } case AMQP_QUEUE_DECLARE_OK_METHOD: { - amqp_queue_declare_ok_t *m = (amqp_queue_declare_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_queue_declare_ok_t)); - m->queue.len = D_8(encoded, offset); - offset++; - m->queue.bytes = D_BYTES(encoded, offset, m->queue.len); - offset += m->queue.len; - m->message_count = D_32(encoded, offset); - offset += 4; - m->consumer_count = D_32(encoded, offset); - offset += 4; + amqp_queue_declare_ok_t *m = (amqp_queue_declare_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_declare_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_32(encoded, &offset, &m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_32(encoded, &offset, &m->consumer_count)) + return AMQP_STATUS_BAD_AMQP_DATA; *decoded = m; return 0; } case AMQP_QUEUE_BIND_METHOD: { - amqp_queue_bind_t *m = (amqp_queue_bind_t *) amqp_pool_alloc(pool, sizeof(amqp_queue_bind_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->queue.len = D_8(encoded, offset); - offset++; - m->queue.bytes = D_BYTES(encoded, offset, m->queue.len); - offset += m->queue.len; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->routing_key.len = D_8(encoded, offset); - offset++; - m->routing_key.bytes = D_BYTES(encoded, offset, m->routing_key.len); - offset += m->routing_key.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_queue_bind_t *m = + (amqp_queue_bind_t *)amqp_pool_alloc(pool, sizeof(amqp_queue_bind_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; - table_result = amqp_decode_table(encoded, pool, &(m->arguments), &offset); - AMQP_CHECK_RESULT(table_result); + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } *decoded = m; return 0; } case AMQP_QUEUE_BIND_OK_METHOD: { - amqp_queue_bind_ok_t *m = (amqp_queue_bind_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_queue_bind_ok_t)); + amqp_queue_bind_ok_t *m = (amqp_queue_bind_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_bind_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_QUEUE_PURGE_METHOD: { - amqp_queue_purge_t *m = (amqp_queue_purge_t *) amqp_pool_alloc(pool, sizeof(amqp_queue_purge_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->queue.len = D_8(encoded, offset); - offset++; - m->queue.bytes = D_BYTES(encoded, offset, m->queue.len); - offset += m->queue.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_queue_purge_t *m = (amqp_queue_purge_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_purge_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } case AMQP_QUEUE_PURGE_OK_METHOD: { - amqp_queue_purge_ok_t *m = (amqp_queue_purge_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_queue_purge_ok_t)); - m->message_count = D_32(encoded, offset); - offset += 4; + amqp_queue_purge_ok_t *m = (amqp_queue_purge_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_purge_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_32(encoded, &offset, &m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; *decoded = m; return 0; } case AMQP_QUEUE_DELETE_METHOD: { - amqp_queue_delete_t *m = (amqp_queue_delete_t *) amqp_pool_alloc(pool, sizeof(amqp_queue_delete_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->queue.len = D_8(encoded, offset); - offset++; - m->queue.bytes = D_BYTES(encoded, offset, m->queue.len); - offset += m->queue.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_queue_delete_t *m = (amqp_queue_delete_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_delete_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->if_unused = (bit_buffer & (1 << 0)) ? 1 : 0; m->if_empty = (bit_buffer & (1 << 1)) ? 1 : 0; m->nowait = (bit_buffer & (1 << 2)) ? 1 : 0; @@ -504,1945 +937,1375 @@ int amqp_decode_method(amqp_method_number_t methodNumber, return 0; } case AMQP_QUEUE_DELETE_OK_METHOD: { - amqp_queue_delete_ok_t *m = (amqp_queue_delete_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_queue_delete_ok_t)); - m->message_count = D_32(encoded, offset); - offset += 4; + amqp_queue_delete_ok_t *m = (amqp_queue_delete_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_delete_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_32(encoded, &offset, &m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; *decoded = m; return 0; } case AMQP_QUEUE_UNBIND_METHOD: { - amqp_queue_unbind_t *m = (amqp_queue_unbind_t *) amqp_pool_alloc(pool, sizeof(amqp_queue_unbind_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->queue.len = D_8(encoded, offset); - offset++; - m->queue.bytes = D_BYTES(encoded, offset, m->queue.len); - offset += m->queue.len; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->routing_key.len = D_8(encoded, offset); - offset++; - m->routing_key.bytes = D_BYTES(encoded, offset, m->routing_key.len); - offset += m->routing_key.len; - table_result = amqp_decode_table(encoded, pool, &(m->arguments), &offset); - AMQP_CHECK_RESULT(table_result); + amqp_queue_unbind_t *m = (amqp_queue_unbind_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_unbind_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } *decoded = m; return 0; } case AMQP_QUEUE_UNBIND_OK_METHOD: { - amqp_queue_unbind_ok_t *m = (amqp_queue_unbind_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_queue_unbind_ok_t)); + amqp_queue_unbind_ok_t *m = (amqp_queue_unbind_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_unbind_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_BASIC_QOS_METHOD: { - amqp_basic_qos_t *m = (amqp_basic_qos_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_qos_t)); - m->prefetch_size = D_32(encoded, offset); - offset += 4; - m->prefetch_count = D_16(encoded, offset); - offset += 2; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_basic_qos_t *m = + (amqp_basic_qos_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_qos_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_32(encoded, &offset, &m->prefetch_size)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_16(encoded, &offset, &m->prefetch_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->global = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } case AMQP_BASIC_QOS_OK_METHOD: { - amqp_basic_qos_ok_t *m = (amqp_basic_qos_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_qos_ok_t)); + amqp_basic_qos_ok_t *m = (amqp_basic_qos_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_qos_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_BASIC_CONSUME_METHOD: { - amqp_basic_consume_t *m = (amqp_basic_consume_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_consume_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->queue.len = D_8(encoded, offset); - offset++; - m->queue.bytes = D_BYTES(encoded, offset, m->queue.len); - offset += m->queue.len; - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_basic_consume_t *m = (amqp_basic_consume_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_consume_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->no_local = (bit_buffer & (1 << 0)) ? 1 : 0; m->no_ack = (bit_buffer & (1 << 1)) ? 1 : 0; m->exclusive = (bit_buffer & (1 << 2)) ? 1 : 0; m->nowait = (bit_buffer & (1 << 3)) ? 1 : 0; + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } *decoded = m; return 0; } case AMQP_BASIC_CONSUME_OK_METHOD: { - amqp_basic_consume_ok_t *m = (amqp_basic_consume_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_consume_ok_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; + amqp_basic_consume_ok_t *m = (amqp_basic_consume_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_consume_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } case AMQP_BASIC_CANCEL_METHOD: { - amqp_basic_cancel_t *m = (amqp_basic_cancel_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_cancel_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_basic_cancel_t *m = (amqp_basic_cancel_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_cancel_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } case AMQP_BASIC_CANCEL_OK_METHOD: { - amqp_basic_cancel_ok_t *m = (amqp_basic_cancel_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_cancel_ok_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; + amqp_basic_cancel_ok_t *m = (amqp_basic_cancel_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_cancel_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } case AMQP_BASIC_PUBLISH_METHOD: { - amqp_basic_publish_t *m = (amqp_basic_publish_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_publish_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->routing_key.len = D_8(encoded, offset); - offset++; - m->routing_key.bytes = D_BYTES(encoded, offset, m->routing_key.len); - offset += m->routing_key.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_basic_publish_t *m = (amqp_basic_publish_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_publish_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->mandatory = (bit_buffer & (1 << 0)) ? 1 : 0; m->immediate = (bit_buffer & (1 << 1)) ? 1 : 0; *decoded = m; return 0; } case AMQP_BASIC_RETURN_METHOD: { - amqp_basic_return_t *m = (amqp_basic_return_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_return_t)); - m->reply_code = D_16(encoded, offset); - offset += 2; - m->reply_text.len = D_8(encoded, offset); - offset++; - m->reply_text.bytes = D_BYTES(encoded, offset, m->reply_text.len); - offset += m->reply_text.len; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->routing_key.len = D_8(encoded, offset); - offset++; - m->routing_key.bytes = D_BYTES(encoded, offset, m->routing_key.len); - offset += m->routing_key.len; + amqp_basic_return_t *m = (amqp_basic_return_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_return_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->reply_text, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } case AMQP_BASIC_DELIVER_METHOD: { - amqp_basic_deliver_t *m = (amqp_basic_deliver_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_deliver_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - m->delivery_tag = D_64(encoded, offset); - offset += 8; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_basic_deliver_t *m = (amqp_basic_deliver_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_deliver_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_64(encoded, &offset, &m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->redelivered = (bit_buffer & (1 << 0)) ? 1 : 0; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->routing_key.len = D_8(encoded, offset); - offset++; - m->routing_key.bytes = D_BYTES(encoded, offset, m->routing_key.len); - offset += m->routing_key.len; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } case AMQP_BASIC_GET_METHOD: { - amqp_basic_get_t *m = (amqp_basic_get_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_get_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->queue.len = D_8(encoded, offset); - offset++; - m->queue.bytes = D_BYTES(encoded, offset, m->queue.len); - offset += m->queue.len; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_basic_get_t *m = + (amqp_basic_get_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_get_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->no_ack = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } case AMQP_BASIC_GET_OK_METHOD: { - amqp_basic_get_ok_t *m = (amqp_basic_get_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_get_ok_t)); - m->delivery_tag = D_64(encoded, offset); - offset += 8; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_basic_get_ok_t *m = (amqp_basic_get_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_get_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_64(encoded, &offset, &m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->redelivered = (bit_buffer & (1 << 0)) ? 1 : 0; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->routing_key.len = D_8(encoded, offset); - offset++; - m->routing_key.bytes = D_BYTES(encoded, offset, m->routing_key.len); - offset += m->routing_key.len; - m->message_count = D_32(encoded, offset); - offset += 4; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_32(encoded, &offset, &m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; *decoded = m; return 0; } case AMQP_BASIC_GET_EMPTY_METHOD: { - amqp_basic_get_empty_t *m = (amqp_basic_get_empty_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_get_empty_t)); - m->cluster_id.len = D_8(encoded, offset); - offset++; - m->cluster_id.bytes = D_BYTES(encoded, offset, m->cluster_id.len); - offset += m->cluster_id.len; + amqp_basic_get_empty_t *m = (amqp_basic_get_empty_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_get_empty_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->cluster_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } *decoded = m; return 0; } case AMQP_BASIC_ACK_METHOD: { - amqp_basic_ack_t *m = (amqp_basic_ack_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_ack_t)); - m->delivery_tag = D_64(encoded, offset); - offset += 8; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_basic_ack_t *m = + (amqp_basic_ack_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_ack_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_64(encoded, &offset, &m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->multiple = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } case AMQP_BASIC_REJECT_METHOD: { - amqp_basic_reject_t *m = (amqp_basic_reject_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_reject_t)); - m->delivery_tag = D_64(encoded, offset); - offset += 8; - bit_buffer = D_8(encoded, offset); - offset++; + amqp_basic_reject_t *m = (amqp_basic_reject_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_reject_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_64(encoded, &offset, &m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->requeue = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } - case AMQP_BASIC_RECOVER_METHOD: { - amqp_basic_recover_t *m = (amqp_basic_recover_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_recover_t)); - bit_buffer = D_8(encoded, offset); - offset++; + case AMQP_BASIC_RECOVER_ASYNC_METHOD: { + amqp_basic_recover_async_t *m = + (amqp_basic_recover_async_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_recover_async_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->requeue = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } - case AMQP_FILE_QOS_METHOD: { - amqp_file_qos_t *m = (amqp_file_qos_t *) amqp_pool_alloc(pool, sizeof(amqp_file_qos_t)); - m->prefetch_size = D_32(encoded, offset); - offset += 4; - m->prefetch_count = D_16(encoded, offset); - offset += 2; - bit_buffer = D_8(encoded, offset); - offset++; - m->global = (bit_buffer & (1 << 0)) ? 1 : 0; - *decoded = m; - return 0; - } - case AMQP_FILE_QOS_OK_METHOD: { - amqp_file_qos_ok_t *m = (amqp_file_qos_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_file_qos_ok_t)); - *decoded = m; - return 0; - } - case AMQP_FILE_CONSUME_METHOD: { - amqp_file_consume_t *m = (amqp_file_consume_t *) amqp_pool_alloc(pool, sizeof(amqp_file_consume_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->queue.len = D_8(encoded, offset); - offset++; - m->queue.bytes = D_BYTES(encoded, offset, m->queue.len); - offset += m->queue.len; - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - bit_buffer = D_8(encoded, offset); - offset++; - m->no_local = (bit_buffer & (1 << 0)) ? 1 : 0; - m->no_ack = (bit_buffer & (1 << 1)) ? 1 : 0; - m->exclusive = (bit_buffer & (1 << 2)) ? 1 : 0; - m->nowait = (bit_buffer & (1 << 3)) ? 1 : 0; - *decoded = m; - return 0; - } - case AMQP_FILE_CONSUME_OK_METHOD: { - amqp_file_consume_ok_t *m = (amqp_file_consume_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_file_consume_ok_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - *decoded = m; - return 0; - } - case AMQP_FILE_CANCEL_METHOD: { - amqp_file_cancel_t *m = (amqp_file_cancel_t *) amqp_pool_alloc(pool, sizeof(amqp_file_cancel_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - bit_buffer = D_8(encoded, offset); - offset++; - m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; - *decoded = m; - return 0; - } - case AMQP_FILE_CANCEL_OK_METHOD: { - amqp_file_cancel_ok_t *m = (amqp_file_cancel_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_file_cancel_ok_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - *decoded = m; - return 0; - } - case AMQP_FILE_OPEN_METHOD: { - amqp_file_open_t *m = (amqp_file_open_t *) amqp_pool_alloc(pool, sizeof(amqp_file_open_t)); - m->identifier.len = D_8(encoded, offset); - offset++; - m->identifier.bytes = D_BYTES(encoded, offset, m->identifier.len); - offset += m->identifier.len; - m->content_size = D_64(encoded, offset); - offset += 8; - *decoded = m; - return 0; - } - case AMQP_FILE_OPEN_OK_METHOD: { - amqp_file_open_ok_t *m = (amqp_file_open_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_file_open_ok_t)); - m->staged_size = D_64(encoded, offset); - offset += 8; - *decoded = m; - return 0; - } - case AMQP_FILE_STAGE_METHOD: { - amqp_file_stage_t *m = (amqp_file_stage_t *) amqp_pool_alloc(pool, sizeof(amqp_file_stage_t)); - *decoded = m; - return 0; - } - case AMQP_FILE_PUBLISH_METHOD: { - amqp_file_publish_t *m = (amqp_file_publish_t *) amqp_pool_alloc(pool, sizeof(amqp_file_publish_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->routing_key.len = D_8(encoded, offset); - offset++; - m->routing_key.bytes = D_BYTES(encoded, offset, m->routing_key.len); - offset += m->routing_key.len; - bit_buffer = D_8(encoded, offset); - offset++; - m->mandatory = (bit_buffer & (1 << 0)) ? 1 : 0; - m->immediate = (bit_buffer & (1 << 1)) ? 1 : 0; - m->identifier.len = D_8(encoded, offset); - offset++; - m->identifier.bytes = D_BYTES(encoded, offset, m->identifier.len); - offset += m->identifier.len; - *decoded = m; - return 0; - } - case AMQP_FILE_RETURN_METHOD: { - amqp_file_return_t *m = (amqp_file_return_t *) amqp_pool_alloc(pool, sizeof(amqp_file_return_t)); - m->reply_code = D_16(encoded, offset); - offset += 2; - m->reply_text.len = D_8(encoded, offset); - offset++; - m->reply_text.bytes = D_BYTES(encoded, offset, m->reply_text.len); - offset += m->reply_text.len; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->routing_key.len = D_8(encoded, offset); - offset++; - m->routing_key.bytes = D_BYTES(encoded, offset, m->routing_key.len); - offset += m->routing_key.len; - *decoded = m; - return 0; - } - case AMQP_FILE_DELIVER_METHOD: { - amqp_file_deliver_t *m = (amqp_file_deliver_t *) amqp_pool_alloc(pool, sizeof(amqp_file_deliver_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - m->delivery_tag = D_64(encoded, offset); - offset += 8; - bit_buffer = D_8(encoded, offset); - offset++; - m->redelivered = (bit_buffer & (1 << 0)) ? 1 : 0; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->routing_key.len = D_8(encoded, offset); - offset++; - m->routing_key.bytes = D_BYTES(encoded, offset, m->routing_key.len); - offset += m->routing_key.len; - m->identifier.len = D_8(encoded, offset); - offset++; - m->identifier.bytes = D_BYTES(encoded, offset, m->identifier.len); - offset += m->identifier.len; - *decoded = m; - return 0; - } - case AMQP_FILE_ACK_METHOD: { - amqp_file_ack_t *m = (amqp_file_ack_t *) amqp_pool_alloc(pool, sizeof(amqp_file_ack_t)); - m->delivery_tag = D_64(encoded, offset); - offset += 8; - bit_buffer = D_8(encoded, offset); - offset++; - m->multiple = (bit_buffer & (1 << 0)) ? 1 : 0; - *decoded = m; - return 0; - } - case AMQP_FILE_REJECT_METHOD: { - amqp_file_reject_t *m = (amqp_file_reject_t *) amqp_pool_alloc(pool, sizeof(amqp_file_reject_t)); - m->delivery_tag = D_64(encoded, offset); - offset += 8; - bit_buffer = D_8(encoded, offset); - offset++; + case AMQP_BASIC_RECOVER_METHOD: { + amqp_basic_recover_t *m = (amqp_basic_recover_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_recover_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; m->requeue = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } - case AMQP_STREAM_QOS_METHOD: { - amqp_stream_qos_t *m = (amqp_stream_qos_t *) amqp_pool_alloc(pool, sizeof(amqp_stream_qos_t)); - m->prefetch_size = D_32(encoded, offset); - offset += 4; - m->prefetch_count = D_16(encoded, offset); - offset += 2; - m->consume_rate = D_32(encoded, offset); - offset += 4; - bit_buffer = D_8(encoded, offset); - offset++; - m->global = (bit_buffer & (1 << 0)) ? 1 : 0; - *decoded = m; - return 0; - } - case AMQP_STREAM_QOS_OK_METHOD: { - amqp_stream_qos_ok_t *m = (amqp_stream_qos_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_stream_qos_ok_t)); - *decoded = m; - return 0; - } - case AMQP_STREAM_CONSUME_METHOD: { - amqp_stream_consume_t *m = (amqp_stream_consume_t *) amqp_pool_alloc(pool, sizeof(amqp_stream_consume_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->queue.len = D_8(encoded, offset); - offset++; - m->queue.bytes = D_BYTES(encoded, offset, m->queue.len); - offset += m->queue.len; - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - bit_buffer = D_8(encoded, offset); - offset++; - m->no_local = (bit_buffer & (1 << 0)) ? 1 : 0; - m->exclusive = (bit_buffer & (1 << 1)) ? 1 : 0; - m->nowait = (bit_buffer & (1 << 2)) ? 1 : 0; - *decoded = m; - return 0; - } - case AMQP_STREAM_CONSUME_OK_METHOD: { - amqp_stream_consume_ok_t *m = (amqp_stream_consume_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_stream_consume_ok_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - *decoded = m; - return 0; - } - case AMQP_STREAM_CANCEL_METHOD: { - amqp_stream_cancel_t *m = (amqp_stream_cancel_t *) amqp_pool_alloc(pool, sizeof(amqp_stream_cancel_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - bit_buffer = D_8(encoded, offset); - offset++; - m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; - *decoded = m; - return 0; - } - case AMQP_STREAM_CANCEL_OK_METHOD: { - amqp_stream_cancel_ok_t *m = (amqp_stream_cancel_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_stream_cancel_ok_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - *decoded = m; - return 0; - } - case AMQP_STREAM_PUBLISH_METHOD: { - amqp_stream_publish_t *m = (amqp_stream_publish_t *) amqp_pool_alloc(pool, sizeof(amqp_stream_publish_t)); - m->ticket = D_16(encoded, offset); - offset += 2; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->routing_key.len = D_8(encoded, offset); - offset++; - m->routing_key.bytes = D_BYTES(encoded, offset, m->routing_key.len); - offset += m->routing_key.len; - bit_buffer = D_8(encoded, offset); - offset++; - m->mandatory = (bit_buffer & (1 << 0)) ? 1 : 0; - m->immediate = (bit_buffer & (1 << 1)) ? 1 : 0; + case AMQP_BASIC_RECOVER_OK_METHOD: { + amqp_basic_recover_ok_t *m = (amqp_basic_recover_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_recover_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } - case AMQP_STREAM_RETURN_METHOD: { - amqp_stream_return_t *m = (amqp_stream_return_t *) amqp_pool_alloc(pool, sizeof(amqp_stream_return_t)); - m->reply_code = D_16(encoded, offset); - offset += 2; - m->reply_text.len = D_8(encoded, offset); - offset++; - m->reply_text.bytes = D_BYTES(encoded, offset, m->reply_text.len); - offset += m->reply_text.len; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->routing_key.len = D_8(encoded, offset); - offset++; - m->routing_key.bytes = D_BYTES(encoded, offset, m->routing_key.len); - offset += m->routing_key.len; - *decoded = m; - return 0; - } - case AMQP_STREAM_DELIVER_METHOD: { - amqp_stream_deliver_t *m = (amqp_stream_deliver_t *) amqp_pool_alloc(pool, sizeof(amqp_stream_deliver_t)); - m->consumer_tag.len = D_8(encoded, offset); - offset++; - m->consumer_tag.bytes = D_BYTES(encoded, offset, m->consumer_tag.len); - offset += m->consumer_tag.len; - m->delivery_tag = D_64(encoded, offset); - offset += 8; - m->exchange.len = D_8(encoded, offset); - offset++; - m->exchange.bytes = D_BYTES(encoded, offset, m->exchange.len); - offset += m->exchange.len; - m->queue.len = D_8(encoded, offset); - offset++; - m->queue.bytes = D_BYTES(encoded, offset, m->queue.len); - offset += m->queue.len; + case AMQP_BASIC_NACK_METHOD: { + amqp_basic_nack_t *m = + (amqp_basic_nack_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_nack_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_64(encoded, &offset, &m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->multiple = (bit_buffer & (1 << 0)) ? 1 : 0; + m->requeue = (bit_buffer & (1 << 1)) ? 1 : 0; *decoded = m; return 0; } case AMQP_TX_SELECT_METHOD: { - amqp_tx_select_t *m = (amqp_tx_select_t *) amqp_pool_alloc(pool, sizeof(amqp_tx_select_t)); + amqp_tx_select_t *m = + (amqp_tx_select_t *)amqp_pool_alloc(pool, sizeof(amqp_tx_select_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_TX_SELECT_OK_METHOD: { - amqp_tx_select_ok_t *m = (amqp_tx_select_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_tx_select_ok_t)); + amqp_tx_select_ok_t *m = (amqp_tx_select_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_tx_select_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_TX_COMMIT_METHOD: { - amqp_tx_commit_t *m = (amqp_tx_commit_t *) amqp_pool_alloc(pool, sizeof(amqp_tx_commit_t)); + amqp_tx_commit_t *m = + (amqp_tx_commit_t *)amqp_pool_alloc(pool, sizeof(amqp_tx_commit_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_TX_COMMIT_OK_METHOD: { - amqp_tx_commit_ok_t *m = (amqp_tx_commit_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_tx_commit_ok_t)); + amqp_tx_commit_ok_t *m = (amqp_tx_commit_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_tx_commit_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_TX_ROLLBACK_METHOD: { - amqp_tx_rollback_t *m = (amqp_tx_rollback_t *) amqp_pool_alloc(pool, sizeof(amqp_tx_rollback_t)); + amqp_tx_rollback_t *m = (amqp_tx_rollback_t *)amqp_pool_alloc( + pool, sizeof(amqp_tx_rollback_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } case AMQP_TX_ROLLBACK_OK_METHOD: { - amqp_tx_rollback_ok_t *m = (amqp_tx_rollback_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_tx_rollback_ok_t)); - *decoded = m; - return 0; - } - case AMQP_DTX_SELECT_METHOD: { - amqp_dtx_select_t *m = (amqp_dtx_select_t *) amqp_pool_alloc(pool, sizeof(amqp_dtx_select_t)); - *decoded = m; - return 0; - } - case AMQP_DTX_SELECT_OK_METHOD: { - amqp_dtx_select_ok_t *m = (amqp_dtx_select_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_dtx_select_ok_t)); - *decoded = m; - return 0; - } - case AMQP_DTX_START_METHOD: { - amqp_dtx_start_t *m = (amqp_dtx_start_t *) amqp_pool_alloc(pool, sizeof(amqp_dtx_start_t)); - m->dtx_identifier.len = D_8(encoded, offset); - offset++; - m->dtx_identifier.bytes = D_BYTES(encoded, offset, m->dtx_identifier.len); - offset += m->dtx_identifier.len; - *decoded = m; - return 0; - } - case AMQP_DTX_START_OK_METHOD: { - amqp_dtx_start_ok_t *m = (amqp_dtx_start_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_dtx_start_ok_t)); - *decoded = m; - return 0; - } - case AMQP_TUNNEL_REQUEST_METHOD: { - amqp_tunnel_request_t *m = (amqp_tunnel_request_t *) amqp_pool_alloc(pool, sizeof(amqp_tunnel_request_t)); - table_result = amqp_decode_table(encoded, pool, &(m->meta_data), &offset); - AMQP_CHECK_RESULT(table_result); - *decoded = m; - return 0; - } - case AMQP_TEST_INTEGER_METHOD: { - amqp_test_integer_t *m = (amqp_test_integer_t *) amqp_pool_alloc(pool, sizeof(amqp_test_integer_t)); - m->integer_1 = D_8(encoded, offset); - offset++; - m->integer_2 = D_16(encoded, offset); - offset += 2; - m->integer_3 = D_32(encoded, offset); - offset += 4; - m->integer_4 = D_64(encoded, offset); - offset += 8; - m->operation = D_8(encoded, offset); - offset++; - *decoded = m; - return 0; - } - case AMQP_TEST_INTEGER_OK_METHOD: { - amqp_test_integer_ok_t *m = (amqp_test_integer_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_test_integer_ok_t)); - m->result = D_64(encoded, offset); - offset += 8; - *decoded = m; - return 0; - } - case AMQP_TEST_STRING_METHOD: { - amqp_test_string_t *m = (amqp_test_string_t *) amqp_pool_alloc(pool, sizeof(amqp_test_string_t)); - m->string_1.len = D_8(encoded, offset); - offset++; - m->string_1.bytes = D_BYTES(encoded, offset, m->string_1.len); - offset += m->string_1.len; - m->string_2.len = D_32(encoded, offset); - offset += 4; - m->string_2.bytes = D_BYTES(encoded, offset, m->string_2.len); - offset += m->string_2.len; - m->operation = D_8(encoded, offset); - offset++; - *decoded = m; - return 0; - } - case AMQP_TEST_STRING_OK_METHOD: { - amqp_test_string_ok_t *m = (amqp_test_string_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_test_string_ok_t)); - m->result.len = D_32(encoded, offset); - offset += 4; - m->result.bytes = D_BYTES(encoded, offset, m->result.len); - offset += m->result.len; - *decoded = m; - return 0; - } - case AMQP_TEST_TABLE_METHOD: { - amqp_test_table_t *m = (amqp_test_table_t *) amqp_pool_alloc(pool, sizeof(amqp_test_table_t)); - table_result = amqp_decode_table(encoded, pool, &(m->table), &offset); - AMQP_CHECK_RESULT(table_result); - m->integer_op = D_8(encoded, offset); - offset++; - m->string_op = D_8(encoded, offset); - offset++; - *decoded = m; - return 0; - } - case AMQP_TEST_TABLE_OK_METHOD: { - amqp_test_table_ok_t *m = (amqp_test_table_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_test_table_ok_t)); - m->integer_result = D_64(encoded, offset); - offset += 8; - m->string_result.len = D_32(encoded, offset); - offset += 4; - m->string_result.bytes = D_BYTES(encoded, offset, m->string_result.len); - offset += m->string_result.len; + amqp_tx_rollback_ok_t *m = (amqp_tx_rollback_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_tx_rollback_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } - case AMQP_TEST_CONTENT_METHOD: { - amqp_test_content_t *m = (amqp_test_content_t *) amqp_pool_alloc(pool, sizeof(amqp_test_content_t)); + case AMQP_CONFIRM_SELECT_METHOD: { + amqp_confirm_select_t *m = (amqp_confirm_select_t *)amqp_pool_alloc( + pool, sizeof(amqp_confirm_select_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; *decoded = m; return 0; } - case AMQP_TEST_CONTENT_OK_METHOD: { - amqp_test_content_ok_t *m = (amqp_test_content_ok_t *) amqp_pool_alloc(pool, sizeof(amqp_test_content_ok_t)); - m->content_checksum = D_32(encoded, offset); - offset += 4; + case AMQP_CONFIRM_SELECT_OK_METHOD: { + amqp_confirm_select_ok_t *m = (amqp_confirm_select_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_confirm_select_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } *decoded = m; return 0; } - default: return -ENOENT; + default: + return AMQP_STATUS_UNKNOWN_METHOD; } } -int amqp_decode_properties(uint16_t class_id, - amqp_pool_t *pool, - amqp_bytes_t encoded, - void **decoded) -{ - int offset = 0; - int table_result; +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; - amqp_flags_t partial_flags; + uint16_t partial_flags; do { - partial_flags = D_16(encoded, offset); - offset += 2; + 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) { case 10: { - amqp_connection_properties_t *p = (amqp_connection_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_connection_properties_t)); + amqp_connection_properties_t *p = + (amqp_connection_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } p->_flags = flags; *decoded = p; return 0; } case 20: { - amqp_channel_properties_t *p = (amqp_channel_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_channel_properties_t)); + amqp_channel_properties_t *p = + (amqp_channel_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } p->_flags = flags; *decoded = p; return 0; } case 30: { - amqp_access_properties_t *p = (amqp_access_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_access_properties_t)); + amqp_access_properties_t *p = (amqp_access_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_access_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } p->_flags = flags; *decoded = p; return 0; } case 40: { - amqp_exchange_properties_t *p = (amqp_exchange_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_exchange_properties_t)); + amqp_exchange_properties_t *p = + (amqp_exchange_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } p->_flags = flags; *decoded = p; return 0; } case 50: { - amqp_queue_properties_t *p = (amqp_queue_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_queue_properties_t)); + amqp_queue_properties_t *p = (amqp_queue_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } p->_flags = flags; *decoded = p; return 0; } case 60: { - amqp_basic_properties_t *p = (amqp_basic_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_basic_properties_t)); + amqp_basic_properties_t *p = (amqp_basic_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } p->_flags = flags; if (flags & AMQP_BASIC_CONTENT_TYPE_FLAG) { - p->content_type.len = D_8(encoded, offset); - offset++; - p->content_type.bytes = D_BYTES(encoded, offset, p->content_type.len); - offset += p->content_type.len; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->content_type, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } } if (flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) { - p->content_encoding.len = D_8(encoded, offset); - offset++; - p->content_encoding.bytes = D_BYTES(encoded, offset, p->content_encoding.len); - offset += p->content_encoding.len; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->content_encoding, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } } if (flags & AMQP_BASIC_HEADERS_FLAG) { - table_result = amqp_decode_table(encoded, pool, &(p->headers), &offset); - AMQP_CHECK_RESULT(table_result); + { + int res = amqp_decode_table(encoded, pool, &(p->headers), &offset); + if (res < 0) return res; + } } if (flags & AMQP_BASIC_DELIVERY_MODE_FLAG) { - p->delivery_mode = D_8(encoded, offset); - offset++; + if (!amqp_decode_8(encoded, &offset, &p->delivery_mode)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_PRIORITY_FLAG) { - p->priority = D_8(encoded, offset); - offset++; + if (!amqp_decode_8(encoded, &offset, &p->priority)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_CORRELATION_ID_FLAG) { - p->correlation_id.len = D_8(encoded, offset); - offset++; - p->correlation_id.bytes = D_BYTES(encoded, offset, p->correlation_id.len); - offset += p->correlation_id.len; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->correlation_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } } if (flags & AMQP_BASIC_REPLY_TO_FLAG) { - p->reply_to.len = D_8(encoded, offset); - offset++; - p->reply_to.bytes = D_BYTES(encoded, offset, p->reply_to.len); - offset += p->reply_to.len; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->reply_to, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } } if (flags & AMQP_BASIC_EXPIRATION_FLAG) { - p->expiration.len = D_8(encoded, offset); - offset++; - p->expiration.bytes = D_BYTES(encoded, offset, p->expiration.len); - offset += p->expiration.len; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->expiration, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } } if (flags & AMQP_BASIC_MESSAGE_ID_FLAG) { - p->message_id.len = D_8(encoded, offset); - offset++; - p->message_id.bytes = D_BYTES(encoded, offset, p->message_id.len); - offset += p->message_id.len; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->message_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } } if (flags & AMQP_BASIC_TIMESTAMP_FLAG) { - p->timestamp = D_64(encoded, offset); - offset += 8; + if (!amqp_decode_64(encoded, &offset, &p->timestamp)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_TYPE_FLAG) { - p->type.len = D_8(encoded, offset); - offset++; - p->type.bytes = D_BYTES(encoded, offset, p->type.len); - offset += p->type.len; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->type, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } } if (flags & AMQP_BASIC_USER_ID_FLAG) { - p->user_id.len = D_8(encoded, offset); - offset++; - p->user_id.bytes = D_BYTES(encoded, offset, p->user_id.len); - offset += p->user_id.len; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->user_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } } if (flags & AMQP_BASIC_APP_ID_FLAG) { - p->app_id.len = D_8(encoded, offset); - offset++; - p->app_id.bytes = D_BYTES(encoded, offset, p->app_id.len); - offset += p->app_id.len; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->app_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } } if (flags & AMQP_BASIC_CLUSTER_ID_FLAG) { - p->cluster_id.len = D_8(encoded, offset); - offset++; - p->cluster_id.bytes = D_BYTES(encoded, offset, p->cluster_id.len); - offset += p->cluster_id.len; - } - *decoded = p; - return 0; - } - case 70: { - amqp_file_properties_t *p = (amqp_file_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_file_properties_t)); - p->_flags = flags; - if (flags & AMQP_FILE_CONTENT_TYPE_FLAG) { - p->content_type.len = D_8(encoded, offset); - offset++; - p->content_type.bytes = D_BYTES(encoded, offset, p->content_type.len); - offset += p->content_type.len; - } - if (flags & AMQP_FILE_CONTENT_ENCODING_FLAG) { - p->content_encoding.len = D_8(encoded, offset); - offset++; - p->content_encoding.bytes = D_BYTES(encoded, offset, p->content_encoding.len); - offset += p->content_encoding.len; - } - if (flags & AMQP_FILE_HEADERS_FLAG) { - table_result = amqp_decode_table(encoded, pool, &(p->headers), &offset); - AMQP_CHECK_RESULT(table_result); - } - if (flags & AMQP_FILE_PRIORITY_FLAG) { - p->priority = D_8(encoded, offset); - offset++; - } - if (flags & AMQP_FILE_REPLY_TO_FLAG) { - p->reply_to.len = D_8(encoded, offset); - offset++; - p->reply_to.bytes = D_BYTES(encoded, offset, p->reply_to.len); - offset += p->reply_to.len; - } - if (flags & AMQP_FILE_MESSAGE_ID_FLAG) { - p->message_id.len = D_8(encoded, offset); - offset++; - p->message_id.bytes = D_BYTES(encoded, offset, p->message_id.len); - offset += p->message_id.len; - } - if (flags & AMQP_FILE_FILENAME_FLAG) { - p->filename.len = D_8(encoded, offset); - offset++; - p->filename.bytes = D_BYTES(encoded, offset, p->filename.len); - offset += p->filename.len; - } - if (flags & AMQP_FILE_TIMESTAMP_FLAG) { - p->timestamp = D_64(encoded, offset); - offset += 8; - } - if (flags & AMQP_FILE_CLUSTER_ID_FLAG) { - p->cluster_id.len = D_8(encoded, offset); - offset++; - p->cluster_id.bytes = D_BYTES(encoded, offset, p->cluster_id.len); - offset += p->cluster_id.len; - } - *decoded = p; - return 0; - } - case 80: { - amqp_stream_properties_t *p = (amqp_stream_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_stream_properties_t)); - p->_flags = flags; - if (flags & AMQP_STREAM_CONTENT_TYPE_FLAG) { - p->content_type.len = D_8(encoded, offset); - offset++; - p->content_type.bytes = D_BYTES(encoded, offset, p->content_type.len); - offset += p->content_type.len; - } - if (flags & AMQP_STREAM_CONTENT_ENCODING_FLAG) { - p->content_encoding.len = D_8(encoded, offset); - offset++; - p->content_encoding.bytes = D_BYTES(encoded, offset, p->content_encoding.len); - offset += p->content_encoding.len; - } - if (flags & AMQP_STREAM_HEADERS_FLAG) { - table_result = amqp_decode_table(encoded, pool, &(p->headers), &offset); - AMQP_CHECK_RESULT(table_result); - } - if (flags & AMQP_STREAM_PRIORITY_FLAG) { - p->priority = D_8(encoded, offset); - offset++; - } - if (flags & AMQP_STREAM_TIMESTAMP_FLAG) { - p->timestamp = D_64(encoded, offset); - offset += 8; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->cluster_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } } *decoded = p; return 0; } case 90: { - amqp_tx_properties_t *p = (amqp_tx_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_tx_properties_t)); - p->_flags = flags; - *decoded = p; - return 0; - } - case 100: { - amqp_dtx_properties_t *p = (amqp_dtx_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_dtx_properties_t)); + amqp_tx_properties_t *p = (amqp_tx_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_tx_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } p->_flags = flags; *decoded = p; return 0; } - case 110: { - amqp_tunnel_properties_t *p = (amqp_tunnel_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_tunnel_properties_t)); - p->_flags = flags; - if (flags & AMQP_TUNNEL_HEADERS_FLAG) { - table_result = amqp_decode_table(encoded, pool, &(p->headers), &offset); - AMQP_CHECK_RESULT(table_result); - } - if (flags & AMQP_TUNNEL_PROXY_NAME_FLAG) { - p->proxy_name.len = D_8(encoded, offset); - offset++; - p->proxy_name.bytes = D_BYTES(encoded, offset, p->proxy_name.len); - offset += p->proxy_name.len; - } - if (flags & AMQP_TUNNEL_DATA_NAME_FLAG) { - p->data_name.len = D_8(encoded, offset); - offset++; - p->data_name.bytes = D_BYTES(encoded, offset, p->data_name.len); - offset += p->data_name.len; - } - if (flags & AMQP_TUNNEL_DURABLE_FLAG) { - p->durable = D_8(encoded, offset); - offset++; - } - if (flags & AMQP_TUNNEL_BROADCAST_FLAG) { - p->broadcast = D_8(encoded, offset); - offset++; + case 85: { + amqp_confirm_properties_t *p = + (amqp_confirm_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_confirm_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; } - *decoded = p; - return 0; - } - case 120: { - amqp_test_properties_t *p = (amqp_test_properties_t *) amqp_pool_alloc(pool, sizeof(amqp_test_properties_t)); p->_flags = flags; *decoded = p; return 0; } - default: return -ENOENT; + default: + return AMQP_STATUS_UNKNOWN_CLASS; } } -int amqp_encode_method(amqp_method_number_t methodNumber, - void *decoded, - amqp_bytes_t encoded) -{ - int offset = 0; - int table_result; +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) { case AMQP_CONNECTION_START_METHOD: { - amqp_connection_start_t *m = (amqp_connection_start_t *) decoded; - E_8(encoded, offset, m->version_major); - offset++; - E_8(encoded, offset, m->version_minor); - offset++; - table_result = amqp_encode_table(encoded, &(m->server_properties), &offset); - if (table_result < 0) return table_result; - E_32(encoded, offset, m->mechanisms.len); - offset += 4; - E_BYTES(encoded, offset, m->mechanisms.len, m->mechanisms.bytes); - offset += m->mechanisms.len; - E_32(encoded, offset, m->locales.len); - offset += 4; - E_BYTES(encoded, offset, m->locales.len, m->locales.bytes); - offset += m->locales.len; - return offset; + amqp_connection_start_t *m = (amqp_connection_start_t *)decoded; + if (!amqp_encode_8(encoded, &offset, m->version_major)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_8(encoded, &offset, m->version_minor)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->server_properties), &offset); + if (res < 0) return res; + } + if (UINT32_MAX < m->mechanisms.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->mechanisms.len) || + !amqp_encode_bytes(encoded, &offset, m->mechanisms)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT32_MAX < m->locales.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->locales.len) || + !amqp_encode_bytes(encoded, &offset, m->locales)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CONNECTION_START_OK_METHOD: { - amqp_connection_start_ok_t *m = (amqp_connection_start_ok_t *) decoded; - table_result = amqp_encode_table(encoded, &(m->client_properties), &offset); - if (table_result < 0) return table_result; - E_8(encoded, offset, m->mechanism.len); - offset++; - E_BYTES(encoded, offset, m->mechanism.len, m->mechanism.bytes); - offset += m->mechanism.len; - E_32(encoded, offset, m->response.len); - offset += 4; - E_BYTES(encoded, offset, m->response.len, m->response.bytes); - offset += m->response.len; - E_8(encoded, offset, m->locale.len); - offset++; - E_BYTES(encoded, offset, m->locale.len, m->locale.bytes); - offset += m->locale.len; - return offset; + amqp_connection_start_ok_t *m = (amqp_connection_start_ok_t *)decoded; + { + int res = amqp_encode_table(encoded, &(m->client_properties), &offset); + if (res < 0) return res; + } + if (UINT8_MAX < m->mechanism.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->mechanism.len) || + !amqp_encode_bytes(encoded, &offset, m->mechanism)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT32_MAX < m->response.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->response.len) || + !amqp_encode_bytes(encoded, &offset, m->response)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->locale.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->locale.len) || + !amqp_encode_bytes(encoded, &offset, m->locale)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CONNECTION_SECURE_METHOD: { - amqp_connection_secure_t *m = (amqp_connection_secure_t *) decoded; - E_32(encoded, offset, m->challenge.len); - offset += 4; - E_BYTES(encoded, offset, m->challenge.len, m->challenge.bytes); - offset += m->challenge.len; - return offset; + amqp_connection_secure_t *m = (amqp_connection_secure_t *)decoded; + if (UINT32_MAX < m->challenge.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->challenge.len) || + !amqp_encode_bytes(encoded, &offset, m->challenge)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CONNECTION_SECURE_OK_METHOD: { - amqp_connection_secure_ok_t *m = (amqp_connection_secure_ok_t *) decoded; - E_32(encoded, offset, m->response.len); - offset += 4; - E_BYTES(encoded, offset, m->response.len, m->response.bytes); - offset += m->response.len; - return offset; + amqp_connection_secure_ok_t *m = (amqp_connection_secure_ok_t *)decoded; + if (UINT32_MAX < m->response.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->response.len) || + !amqp_encode_bytes(encoded, &offset, m->response)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CONNECTION_TUNE_METHOD: { - amqp_connection_tune_t *m = (amqp_connection_tune_t *) decoded; - E_16(encoded, offset, m->channel_max); - offset += 2; - E_32(encoded, offset, m->frame_max); - offset += 4; - E_16(encoded, offset, m->heartbeat); - offset += 2; - return offset; + amqp_connection_tune_t *m = (amqp_connection_tune_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->channel_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_32(encoded, &offset, m->frame_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->heartbeat)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CONNECTION_TUNE_OK_METHOD: { - amqp_connection_tune_ok_t *m = (amqp_connection_tune_ok_t *) decoded; - E_16(encoded, offset, m->channel_max); - offset += 2; - E_32(encoded, offset, m->frame_max); - offset += 4; - E_16(encoded, offset, m->heartbeat); - offset += 2; - return offset; + amqp_connection_tune_ok_t *m = (amqp_connection_tune_ok_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->channel_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_32(encoded, &offset, m->frame_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->heartbeat)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CONNECTION_OPEN_METHOD: { - amqp_connection_open_t *m = (amqp_connection_open_t *) decoded; - E_8(encoded, offset, m->virtual_host.len); - offset++; - E_BYTES(encoded, offset, m->virtual_host.len, m->virtual_host.bytes); - offset += m->virtual_host.len; - E_8(encoded, offset, m->capabilities.len); - offset++; - E_BYTES(encoded, offset, m->capabilities.len, m->capabilities.bytes); - offset += m->capabilities.len; + amqp_connection_open_t *m = (amqp_connection_open_t *)decoded; + if (UINT8_MAX < m->virtual_host.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->virtual_host.len) || + !amqp_encode_bytes(encoded, &offset, m->virtual_host)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->capabilities.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->capabilities.len) || + !amqp_encode_bytes(encoded, &offset, m->capabilities)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->insist) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->insist) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CONNECTION_OPEN_OK_METHOD: { - amqp_connection_open_ok_t *m = (amqp_connection_open_ok_t *) decoded; - E_8(encoded, offset, m->known_hosts.len); - offset++; - E_BYTES(encoded, offset, m->known_hosts.len, m->known_hosts.bytes); - offset += m->known_hosts.len; - return offset; - } - case AMQP_CONNECTION_REDIRECT_METHOD: { - amqp_connection_redirect_t *m = (amqp_connection_redirect_t *) decoded; - E_8(encoded, offset, m->host.len); - offset++; - E_BYTES(encoded, offset, m->host.len, m->host.bytes); - offset += m->host.len; - E_8(encoded, offset, m->known_hosts.len); - offset++; - E_BYTES(encoded, offset, m->known_hosts.len, m->known_hosts.bytes); - offset += m->known_hosts.len; - return offset; + amqp_connection_open_ok_t *m = (amqp_connection_open_ok_t *)decoded; + if (UINT8_MAX < m->known_hosts.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->known_hosts.len) || + !amqp_encode_bytes(encoded, &offset, m->known_hosts)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CONNECTION_CLOSE_METHOD: { - amqp_connection_close_t *m = (amqp_connection_close_t *) decoded; - E_16(encoded, offset, m->reply_code); - offset += 2; - E_8(encoded, offset, m->reply_text.len); - offset++; - E_BYTES(encoded, offset, m->reply_text.len, m->reply_text.bytes); - offset += m->reply_text.len; - E_16(encoded, offset, m->class_id); - offset += 2; - E_16(encoded, offset, m->method_id); - offset += 2; - return offset; + amqp_connection_close_t *m = (amqp_connection_close_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->reply_text.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->reply_text.len) || + !amqp_encode_bytes(encoded, &offset, m->reply_text)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->class_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->method_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CONNECTION_CLOSE_OK_METHOD: { - return offset; + return (int)offset; + } + case AMQP_CONNECTION_BLOCKED_METHOD: { + amqp_connection_blocked_t *m = (amqp_connection_blocked_t *)decoded; + if (UINT8_MAX < m->reason.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->reason.len) || + !amqp_encode_bytes(encoded, &offset, m->reason)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONNECTION_UNBLOCKED_METHOD: { + return (int)offset; } case AMQP_CHANNEL_OPEN_METHOD: { - amqp_channel_open_t *m = (amqp_channel_open_t *) decoded; - E_8(encoded, offset, m->out_of_band.len); - offset++; - E_BYTES(encoded, offset, m->out_of_band.len, m->out_of_band.bytes); - offset += m->out_of_band.len; - return offset; + amqp_channel_open_t *m = (amqp_channel_open_t *)decoded; + if (UINT8_MAX < m->out_of_band.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->out_of_band.len) || + !amqp_encode_bytes(encoded, &offset, m->out_of_band)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CHANNEL_OPEN_OK_METHOD: { - return offset; + amqp_channel_open_ok_t *m = (amqp_channel_open_ok_t *)decoded; + if (UINT32_MAX < m->channel_id.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->channel_id.len) || + !amqp_encode_bytes(encoded, &offset, m->channel_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CHANNEL_FLOW_METHOD: { - amqp_channel_flow_t *m = (amqp_channel_flow_t *) decoded; + amqp_channel_flow_t *m = (amqp_channel_flow_t *)decoded; bit_buffer = 0; - if (m->active) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->active) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CHANNEL_FLOW_OK_METHOD: { - amqp_channel_flow_ok_t *m = (amqp_channel_flow_ok_t *) decoded; + amqp_channel_flow_ok_t *m = (amqp_channel_flow_ok_t *)decoded; bit_buffer = 0; - if (m->active) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; - } - case AMQP_CHANNEL_ALERT_METHOD: { - amqp_channel_alert_t *m = (amqp_channel_alert_t *) decoded; - E_16(encoded, offset, m->reply_code); - offset += 2; - E_8(encoded, offset, m->reply_text.len); - offset++; - E_BYTES(encoded, offset, m->reply_text.len, m->reply_text.bytes); - offset += m->reply_text.len; - table_result = amqp_encode_table(encoded, &(m->details), &offset); - if (table_result < 0) return table_result; - return offset; + if (m->active) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CHANNEL_CLOSE_METHOD: { - amqp_channel_close_t *m = (amqp_channel_close_t *) decoded; - E_16(encoded, offset, m->reply_code); - offset += 2; - E_8(encoded, offset, m->reply_text.len); - offset++; - E_BYTES(encoded, offset, m->reply_text.len, m->reply_text.bytes); - offset += m->reply_text.len; - E_16(encoded, offset, m->class_id); - offset += 2; - E_16(encoded, offset, m->method_id); - offset += 2; - return offset; + amqp_channel_close_t *m = (amqp_channel_close_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->reply_text.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->reply_text.len) || + !amqp_encode_bytes(encoded, &offset, m->reply_text)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->class_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->method_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_CHANNEL_CLOSE_OK_METHOD: { - return offset; + return (int)offset; } case AMQP_ACCESS_REQUEST_METHOD: { - amqp_access_request_t *m = (amqp_access_request_t *) decoded; - E_8(encoded, offset, m->realm.len); - offset++; - E_BYTES(encoded, offset, m->realm.len, m->realm.bytes); - offset += m->realm.len; + amqp_access_request_t *m = (amqp_access_request_t *)decoded; + if (UINT8_MAX < m->realm.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->realm.len) || + !amqp_encode_bytes(encoded, &offset, m->realm)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->exclusive) { bit_buffer |= (1 << 0); } - if (m->passive) { bit_buffer |= (1 << 1); } - if (m->active) { bit_buffer |= (1 << 2); } - if (m->write) { bit_buffer |= (1 << 3); } - if (m->read) { bit_buffer |= (1 << 4); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->exclusive) bit_buffer |= (1 << 0); + if (m->passive) bit_buffer |= (1 << 1); + if (m->active) bit_buffer |= (1 << 2); + if (m->write) bit_buffer |= (1 << 3); + if (m->read) bit_buffer |= (1 << 4); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_ACCESS_REQUEST_OK_METHOD: { - amqp_access_request_ok_t *m = (amqp_access_request_ok_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - return offset; + amqp_access_request_ok_t *m = (amqp_access_request_ok_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_EXCHANGE_DECLARE_METHOD: { - amqp_exchange_declare_t *m = (amqp_exchange_declare_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->type.len); - offset++; - E_BYTES(encoded, offset, m->type.len, m->type.bytes); - offset += m->type.len; + amqp_exchange_declare_t *m = (amqp_exchange_declare_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->type.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->type.len) || + !amqp_encode_bytes(encoded, &offset, m->type)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->passive) { bit_buffer |= (1 << 0); } - if (m->durable) { bit_buffer |= (1 << 1); } - if (m->auto_delete) { bit_buffer |= (1 << 2); } - if (m->internal) { bit_buffer |= (1 << 3); } - if (m->nowait) { bit_buffer |= (1 << 4); } - E_8(encoded, offset, bit_buffer); - offset++; - table_result = amqp_encode_table(encoded, &(m->arguments), &offset); - if (table_result < 0) return table_result; - return offset; + if (m->passive) bit_buffer |= (1 << 0); + if (m->durable) bit_buffer |= (1 << 1); + if (m->auto_delete) bit_buffer |= (1 << 2); + if (m->internal) bit_buffer |= (1 << 3); + if (m->nowait) bit_buffer |= (1 << 4); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; } case AMQP_EXCHANGE_DECLARE_OK_METHOD: { - return offset; + return (int)offset; } case AMQP_EXCHANGE_DELETE_METHOD: { - amqp_exchange_delete_t *m = (amqp_exchange_delete_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; + amqp_exchange_delete_t *m = (amqp_exchange_delete_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->if_unused) { bit_buffer |= (1 << 0); } - if (m->nowait) { bit_buffer |= (1 << 1); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->if_unused) bit_buffer |= (1 << 0); + if (m->nowait) bit_buffer |= (1 << 1); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_EXCHANGE_DELETE_OK_METHOD: { - return offset; + return (int)offset; + } + case AMQP_EXCHANGE_BIND_METHOD: { + amqp_exchange_bind_t *m = (amqp_exchange_bind_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->destination.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->destination.len) || + !amqp_encode_bytes(encoded, &offset, m->destination)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->source.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->source.len) || + !amqp_encode_bytes(encoded, &offset, m->source)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; + } + case AMQP_EXCHANGE_BIND_OK_METHOD: { + return (int)offset; + } + case AMQP_EXCHANGE_UNBIND_METHOD: { + amqp_exchange_unbind_t *m = (amqp_exchange_unbind_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->destination.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->destination.len) || + !amqp_encode_bytes(encoded, &offset, m->destination)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->source.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->source.len) || + !amqp_encode_bytes(encoded, &offset, m->source)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; + } + case AMQP_EXCHANGE_UNBIND_OK_METHOD: { + return (int)offset; } case AMQP_QUEUE_DECLARE_METHOD: { - amqp_queue_declare_t *m = (amqp_queue_declare_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->queue.len); - offset++; - E_BYTES(encoded, offset, m->queue.len, m->queue.bytes); - offset += m->queue.len; + amqp_queue_declare_t *m = (amqp_queue_declare_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->passive) { bit_buffer |= (1 << 0); } - if (m->durable) { bit_buffer |= (1 << 1); } - if (m->exclusive) { bit_buffer |= (1 << 2); } - if (m->auto_delete) { bit_buffer |= (1 << 3); } - if (m->nowait) { bit_buffer |= (1 << 4); } - E_8(encoded, offset, bit_buffer); - offset++; - table_result = amqp_encode_table(encoded, &(m->arguments), &offset); - if (table_result < 0) return table_result; - return offset; + if (m->passive) bit_buffer |= (1 << 0); + if (m->durable) bit_buffer |= (1 << 1); + if (m->exclusive) bit_buffer |= (1 << 2); + if (m->auto_delete) bit_buffer |= (1 << 3); + if (m->nowait) bit_buffer |= (1 << 4); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; } case AMQP_QUEUE_DECLARE_OK_METHOD: { - amqp_queue_declare_ok_t *m = (amqp_queue_declare_ok_t *) decoded; - E_8(encoded, offset, m->queue.len); - offset++; - E_BYTES(encoded, offset, m->queue.len, m->queue.bytes); - offset += m->queue.len; - E_32(encoded, offset, m->message_count); - offset += 4; - E_32(encoded, offset, m->consumer_count); - offset += 4; - return offset; + amqp_queue_declare_ok_t *m = (amqp_queue_declare_ok_t *)decoded; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_32(encoded, &offset, m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_32(encoded, &offset, m->consumer_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_QUEUE_BIND_METHOD: { - amqp_queue_bind_t *m = (amqp_queue_bind_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->queue.len); - offset++; - E_BYTES(encoded, offset, m->queue.len, m->queue.bytes); - offset += m->queue.len; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->routing_key.len); - offset++; - E_BYTES(encoded, offset, m->routing_key.len, m->routing_key.bytes); - offset += m->routing_key.len; + amqp_queue_bind_t *m = (amqp_queue_bind_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->nowait) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - table_result = amqp_encode_table(encoded, &(m->arguments), &offset); - if (table_result < 0) return table_result; - return offset; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; } case AMQP_QUEUE_BIND_OK_METHOD: { - return offset; + return (int)offset; } case AMQP_QUEUE_PURGE_METHOD: { - amqp_queue_purge_t *m = (amqp_queue_purge_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->queue.len); - offset++; - E_BYTES(encoded, offset, m->queue.len, m->queue.bytes); - offset += m->queue.len; + amqp_queue_purge_t *m = (amqp_queue_purge_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->nowait) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_QUEUE_PURGE_OK_METHOD: { - amqp_queue_purge_ok_t *m = (amqp_queue_purge_ok_t *) decoded; - E_32(encoded, offset, m->message_count); - offset += 4; - return offset; + amqp_queue_purge_ok_t *m = (amqp_queue_purge_ok_t *)decoded; + if (!amqp_encode_32(encoded, &offset, m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_QUEUE_DELETE_METHOD: { - amqp_queue_delete_t *m = (amqp_queue_delete_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->queue.len); - offset++; - E_BYTES(encoded, offset, m->queue.len, m->queue.bytes); - offset += m->queue.len; + amqp_queue_delete_t *m = (amqp_queue_delete_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->if_unused) { bit_buffer |= (1 << 0); } - if (m->if_empty) { bit_buffer |= (1 << 1); } - if (m->nowait) { bit_buffer |= (1 << 2); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->if_unused) bit_buffer |= (1 << 0); + if (m->if_empty) bit_buffer |= (1 << 1); + if (m->nowait) bit_buffer |= (1 << 2); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_QUEUE_DELETE_OK_METHOD: { - amqp_queue_delete_ok_t *m = (amqp_queue_delete_ok_t *) decoded; - E_32(encoded, offset, m->message_count); - offset += 4; - return offset; + amqp_queue_delete_ok_t *m = (amqp_queue_delete_ok_t *)decoded; + if (!amqp_encode_32(encoded, &offset, m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_QUEUE_UNBIND_METHOD: { - amqp_queue_unbind_t *m = (amqp_queue_unbind_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->queue.len); - offset++; - E_BYTES(encoded, offset, m->queue.len, m->queue.bytes); - offset += m->queue.len; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->routing_key.len); - offset++; - E_BYTES(encoded, offset, m->routing_key.len, m->routing_key.bytes); - offset += m->routing_key.len; - table_result = amqp_encode_table(encoded, &(m->arguments), &offset); - if (table_result < 0) return table_result; - return offset; + amqp_queue_unbind_t *m = (amqp_queue_unbind_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; } case AMQP_QUEUE_UNBIND_OK_METHOD: { - return offset; + return (int)offset; } case AMQP_BASIC_QOS_METHOD: { - amqp_basic_qos_t *m = (amqp_basic_qos_t *) decoded; - E_32(encoded, offset, m->prefetch_size); - offset += 4; - E_16(encoded, offset, m->prefetch_count); - offset += 2; + amqp_basic_qos_t *m = (amqp_basic_qos_t *)decoded; + if (!amqp_encode_32(encoded, &offset, m->prefetch_size)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->prefetch_count)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->global) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->global) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_BASIC_QOS_OK_METHOD: { - return offset; + return (int)offset; } case AMQP_BASIC_CONSUME_METHOD: { - amqp_basic_consume_t *m = (amqp_basic_consume_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->queue.len); - offset++; - E_BYTES(encoded, offset, m->queue.len, m->queue.bytes); - offset += m->queue.len; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; + amqp_basic_consume_t *m = (amqp_basic_consume_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->consumer_tag.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) || + !amqp_encode_bytes(encoded, &offset, m->consumer_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->no_local) { bit_buffer |= (1 << 0); } - if (m->no_ack) { bit_buffer |= (1 << 1); } - if (m->exclusive) { bit_buffer |= (1 << 2); } - if (m->nowait) { bit_buffer |= (1 << 3); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->no_local) bit_buffer |= (1 << 0); + if (m->no_ack) bit_buffer |= (1 << 1); + if (m->exclusive) bit_buffer |= (1 << 2); + if (m->nowait) bit_buffer |= (1 << 3); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; } case AMQP_BASIC_CONSUME_OK_METHOD: { - amqp_basic_consume_ok_t *m = (amqp_basic_consume_ok_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - return offset; + amqp_basic_consume_ok_t *m = (amqp_basic_consume_ok_t *)decoded; + if (UINT8_MAX < m->consumer_tag.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) || + !amqp_encode_bytes(encoded, &offset, m->consumer_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_BASIC_CANCEL_METHOD: { - amqp_basic_cancel_t *m = (amqp_basic_cancel_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; + amqp_basic_cancel_t *m = (amqp_basic_cancel_t *)decoded; + if (UINT8_MAX < m->consumer_tag.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) || + !amqp_encode_bytes(encoded, &offset, m->consumer_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->nowait) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_BASIC_CANCEL_OK_METHOD: { - amqp_basic_cancel_ok_t *m = (amqp_basic_cancel_ok_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - return offset; + amqp_basic_cancel_ok_t *m = (amqp_basic_cancel_ok_t *)decoded; + if (UINT8_MAX < m->consumer_tag.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) || + !amqp_encode_bytes(encoded, &offset, m->consumer_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_BASIC_PUBLISH_METHOD: { - amqp_basic_publish_t *m = (amqp_basic_publish_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->routing_key.len); - offset++; - E_BYTES(encoded, offset, m->routing_key.len, m->routing_key.bytes); - offset += m->routing_key.len; + amqp_basic_publish_t *m = (amqp_basic_publish_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->mandatory) { bit_buffer |= (1 << 0); } - if (m->immediate) { bit_buffer |= (1 << 1); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->mandatory) bit_buffer |= (1 << 0); + if (m->immediate) bit_buffer |= (1 << 1); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_BASIC_RETURN_METHOD: { - amqp_basic_return_t *m = (amqp_basic_return_t *) decoded; - E_16(encoded, offset, m->reply_code); - offset += 2; - E_8(encoded, offset, m->reply_text.len); - offset++; - E_BYTES(encoded, offset, m->reply_text.len, m->reply_text.bytes); - offset += m->reply_text.len; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->routing_key.len); - offset++; - E_BYTES(encoded, offset, m->routing_key.len, m->routing_key.bytes); - offset += m->routing_key.len; - return offset; + amqp_basic_return_t *m = (amqp_basic_return_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->reply_text.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->reply_text.len) || + !amqp_encode_bytes(encoded, &offset, m->reply_text)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_BASIC_DELIVER_METHOD: { - amqp_basic_deliver_t *m = (amqp_basic_deliver_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - E_64(encoded, offset, m->delivery_tag); - offset += 8; + amqp_basic_deliver_t *m = (amqp_basic_deliver_t *)decoded; + if (UINT8_MAX < m->consumer_tag.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) || + !amqp_encode_bytes(encoded, &offset, m->consumer_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_64(encoded, &offset, m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->redelivered) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->routing_key.len); - offset++; - E_BYTES(encoded, offset, m->routing_key.len, m->routing_key.bytes); - offset += m->routing_key.len; - return offset; + if (m->redelivered) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_BASIC_GET_METHOD: { - amqp_basic_get_t *m = (amqp_basic_get_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->queue.len); - offset++; - E_BYTES(encoded, offset, m->queue.len, m->queue.bytes); - offset += m->queue.len; + amqp_basic_get_t *m = (amqp_basic_get_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->no_ack) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->no_ack) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_BASIC_GET_OK_METHOD: { - amqp_basic_get_ok_t *m = (amqp_basic_get_ok_t *) decoded; - E_64(encoded, offset, m->delivery_tag); - offset += 8; + amqp_basic_get_ok_t *m = (amqp_basic_get_ok_t *)decoded; + if (!amqp_encode_64(encoded, &offset, m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->redelivered) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->routing_key.len); - offset++; - E_BYTES(encoded, offset, m->routing_key.len, m->routing_key.bytes); - offset += m->routing_key.len; - E_32(encoded, offset, m->message_count); - offset += 4; - return offset; + if (m->redelivered) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_32(encoded, &offset, m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_BASIC_GET_EMPTY_METHOD: { - amqp_basic_get_empty_t *m = (amqp_basic_get_empty_t *) decoded; - E_8(encoded, offset, m->cluster_id.len); - offset++; - E_BYTES(encoded, offset, m->cluster_id.len, m->cluster_id.bytes); - offset += m->cluster_id.len; - return offset; + amqp_basic_get_empty_t *m = (amqp_basic_get_empty_t *)decoded; + if (UINT8_MAX < m->cluster_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->cluster_id.len) || + !amqp_encode_bytes(encoded, &offset, m->cluster_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_BASIC_ACK_METHOD: { - amqp_basic_ack_t *m = (amqp_basic_ack_t *) decoded; - E_64(encoded, offset, m->delivery_tag); - offset += 8; + amqp_basic_ack_t *m = (amqp_basic_ack_t *)decoded; + if (!amqp_encode_64(encoded, &offset, m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->multiple) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->multiple) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_BASIC_REJECT_METHOD: { - amqp_basic_reject_t *m = (amqp_basic_reject_t *) decoded; - E_64(encoded, offset, m->delivery_tag); - offset += 8; + amqp_basic_reject_t *m = (amqp_basic_reject_t *)decoded; + if (!amqp_encode_64(encoded, &offset, m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->requeue) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; + if (m->requeue) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } - case AMQP_BASIC_RECOVER_METHOD: { - amqp_basic_recover_t *m = (amqp_basic_recover_t *) decoded; - bit_buffer = 0; - if (m->requeue) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; - } - case AMQP_FILE_QOS_METHOD: { - amqp_file_qos_t *m = (amqp_file_qos_t *) decoded; - E_32(encoded, offset, m->prefetch_size); - offset += 4; - E_16(encoded, offset, m->prefetch_count); - offset += 2; - bit_buffer = 0; - if (m->global) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; - } - case AMQP_FILE_QOS_OK_METHOD: { - return offset; - } - case AMQP_FILE_CONSUME_METHOD: { - amqp_file_consume_t *m = (amqp_file_consume_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->queue.len); - offset++; - E_BYTES(encoded, offset, m->queue.len, m->queue.bytes); - offset += m->queue.len; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - bit_buffer = 0; - if (m->no_local) { bit_buffer |= (1 << 0); } - if (m->no_ack) { bit_buffer |= (1 << 1); } - if (m->exclusive) { bit_buffer |= (1 << 2); } - if (m->nowait) { bit_buffer |= (1 << 3); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; - } - case AMQP_FILE_CONSUME_OK_METHOD: { - amqp_file_consume_ok_t *m = (amqp_file_consume_ok_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - return offset; - } - case AMQP_FILE_CANCEL_METHOD: { - amqp_file_cancel_t *m = (amqp_file_cancel_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - bit_buffer = 0; - if (m->nowait) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; - } - case AMQP_FILE_CANCEL_OK_METHOD: { - amqp_file_cancel_ok_t *m = (amqp_file_cancel_ok_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - return offset; - } - case AMQP_FILE_OPEN_METHOD: { - amqp_file_open_t *m = (amqp_file_open_t *) decoded; - E_8(encoded, offset, m->identifier.len); - offset++; - E_BYTES(encoded, offset, m->identifier.len, m->identifier.bytes); - offset += m->identifier.len; - E_64(encoded, offset, m->content_size); - offset += 8; - return offset; - } - case AMQP_FILE_OPEN_OK_METHOD: { - amqp_file_open_ok_t *m = (amqp_file_open_ok_t *) decoded; - E_64(encoded, offset, m->staged_size); - offset += 8; - return offset; - } - case AMQP_FILE_STAGE_METHOD: { - return offset; - } - case AMQP_FILE_PUBLISH_METHOD: { - amqp_file_publish_t *m = (amqp_file_publish_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->routing_key.len); - offset++; - E_BYTES(encoded, offset, m->routing_key.len, m->routing_key.bytes); - offset += m->routing_key.len; - bit_buffer = 0; - if (m->mandatory) { bit_buffer |= (1 << 0); } - if (m->immediate) { bit_buffer |= (1 << 1); } - E_8(encoded, offset, bit_buffer); - offset++; - E_8(encoded, offset, m->identifier.len); - offset++; - E_BYTES(encoded, offset, m->identifier.len, m->identifier.bytes); - offset += m->identifier.len; - return offset; - } - case AMQP_FILE_RETURN_METHOD: { - amqp_file_return_t *m = (amqp_file_return_t *) decoded; - E_16(encoded, offset, m->reply_code); - offset += 2; - E_8(encoded, offset, m->reply_text.len); - offset++; - E_BYTES(encoded, offset, m->reply_text.len, m->reply_text.bytes); - offset += m->reply_text.len; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->routing_key.len); - offset++; - E_BYTES(encoded, offset, m->routing_key.len, m->routing_key.bytes); - offset += m->routing_key.len; - return offset; - } - case AMQP_FILE_DELIVER_METHOD: { - amqp_file_deliver_t *m = (amqp_file_deliver_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - E_64(encoded, offset, m->delivery_tag); - offset += 8; + case AMQP_BASIC_RECOVER_ASYNC_METHOD: { + amqp_basic_recover_async_t *m = (amqp_basic_recover_async_t *)decoded; bit_buffer = 0; - if (m->redelivered) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->routing_key.len); - offset++; - E_BYTES(encoded, offset, m->routing_key.len, m->routing_key.bytes); - offset += m->routing_key.len; - E_8(encoded, offset, m->identifier.len); - offset++; - E_BYTES(encoded, offset, m->identifier.len, m->identifier.bytes); - offset += m->identifier.len; - return offset; - } - case AMQP_FILE_ACK_METHOD: { - amqp_file_ack_t *m = (amqp_file_ack_t *) decoded; - E_64(encoded, offset, m->delivery_tag); - offset += 8; - bit_buffer = 0; - if (m->multiple) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; - } - case AMQP_FILE_REJECT_METHOD: { - amqp_file_reject_t *m = (amqp_file_reject_t *) decoded; - E_64(encoded, offset, m->delivery_tag); - offset += 8; - bit_buffer = 0; - if (m->requeue) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; - } - case AMQP_STREAM_QOS_METHOD: { - amqp_stream_qos_t *m = (amqp_stream_qos_t *) decoded; - E_32(encoded, offset, m->prefetch_size); - offset += 4; - E_16(encoded, offset, m->prefetch_count); - offset += 2; - E_32(encoded, offset, m->consume_rate); - offset += 4; - bit_buffer = 0; - if (m->global) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; - } - case AMQP_STREAM_QOS_OK_METHOD: { - return offset; - } - case AMQP_STREAM_CONSUME_METHOD: { - amqp_stream_consume_t *m = (amqp_stream_consume_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->queue.len); - offset++; - E_BYTES(encoded, offset, m->queue.len, m->queue.bytes); - offset += m->queue.len; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - bit_buffer = 0; - if (m->no_local) { bit_buffer |= (1 << 0); } - if (m->exclusive) { bit_buffer |= (1 << 1); } - if (m->nowait) { bit_buffer |= (1 << 2); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; - } - case AMQP_STREAM_CONSUME_OK_METHOD: { - amqp_stream_consume_ok_t *m = (amqp_stream_consume_ok_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - return offset; - } - case AMQP_STREAM_CANCEL_METHOD: { - amqp_stream_cancel_t *m = (amqp_stream_cancel_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; + if (m->requeue) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_RECOVER_METHOD: { + amqp_basic_recover_t *m = (amqp_basic_recover_t *)decoded; bit_buffer = 0; - if (m->nowait) { bit_buffer |= (1 << 0); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; - } - case AMQP_STREAM_CANCEL_OK_METHOD: { - amqp_stream_cancel_ok_t *m = (amqp_stream_cancel_ok_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - return offset; - } - case AMQP_STREAM_PUBLISH_METHOD: { - amqp_stream_publish_t *m = (amqp_stream_publish_t *) decoded; - E_16(encoded, offset, m->ticket); - offset += 2; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->routing_key.len); - offset++; - E_BYTES(encoded, offset, m->routing_key.len, m->routing_key.bytes); - offset += m->routing_key.len; + if (m->requeue) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_RECOVER_OK_METHOD: { + return (int)offset; + } + case AMQP_BASIC_NACK_METHOD: { + amqp_basic_nack_t *m = (amqp_basic_nack_t *)decoded; + if (!amqp_encode_64(encoded, &offset, m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; bit_buffer = 0; - if (m->mandatory) { bit_buffer |= (1 << 0); } - if (m->immediate) { bit_buffer |= (1 << 1); } - E_8(encoded, offset, bit_buffer); - offset++; - return offset; - } - case AMQP_STREAM_RETURN_METHOD: { - amqp_stream_return_t *m = (amqp_stream_return_t *) decoded; - E_16(encoded, offset, m->reply_code); - offset += 2; - E_8(encoded, offset, m->reply_text.len); - offset++; - E_BYTES(encoded, offset, m->reply_text.len, m->reply_text.bytes); - offset += m->reply_text.len; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->routing_key.len); - offset++; - E_BYTES(encoded, offset, m->routing_key.len, m->routing_key.bytes); - offset += m->routing_key.len; - return offset; - } - case AMQP_STREAM_DELIVER_METHOD: { - amqp_stream_deliver_t *m = (amqp_stream_deliver_t *) decoded; - E_8(encoded, offset, m->consumer_tag.len); - offset++; - E_BYTES(encoded, offset, m->consumer_tag.len, m->consumer_tag.bytes); - offset += m->consumer_tag.len; - E_64(encoded, offset, m->delivery_tag); - offset += 8; - E_8(encoded, offset, m->exchange.len); - offset++; - E_BYTES(encoded, offset, m->exchange.len, m->exchange.bytes); - offset += m->exchange.len; - E_8(encoded, offset, m->queue.len); - offset++; - E_BYTES(encoded, offset, m->queue.len, m->queue.bytes); - offset += m->queue.len; - return offset; + if (m->multiple) bit_buffer |= (1 << 0); + if (m->requeue) bit_buffer |= (1 << 1); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; } case AMQP_TX_SELECT_METHOD: { - return offset; + return (int)offset; } case AMQP_TX_SELECT_OK_METHOD: { - return offset; + return (int)offset; } case AMQP_TX_COMMIT_METHOD: { - return offset; + return (int)offset; } case AMQP_TX_COMMIT_OK_METHOD: { - return offset; + return (int)offset; } case AMQP_TX_ROLLBACK_METHOD: { - return offset; + return (int)offset; } case AMQP_TX_ROLLBACK_OK_METHOD: { - return offset; - } - case AMQP_DTX_SELECT_METHOD: { - return offset; - } - case AMQP_DTX_SELECT_OK_METHOD: { - return offset; - } - case AMQP_DTX_START_METHOD: { - amqp_dtx_start_t *m = (amqp_dtx_start_t *) decoded; - E_8(encoded, offset, m->dtx_identifier.len); - offset++; - E_BYTES(encoded, offset, m->dtx_identifier.len, m->dtx_identifier.bytes); - offset += m->dtx_identifier.len; - return offset; - } - case AMQP_DTX_START_OK_METHOD: { - return offset; - } - case AMQP_TUNNEL_REQUEST_METHOD: { - amqp_tunnel_request_t *m = (amqp_tunnel_request_t *) decoded; - table_result = amqp_encode_table(encoded, &(m->meta_data), &offset); - if (table_result < 0) return table_result; - return offset; - } - case AMQP_TEST_INTEGER_METHOD: { - amqp_test_integer_t *m = (amqp_test_integer_t *) decoded; - E_8(encoded, offset, m->integer_1); - offset++; - E_16(encoded, offset, m->integer_2); - offset += 2; - E_32(encoded, offset, m->integer_3); - offset += 4; - E_64(encoded, offset, m->integer_4); - offset += 8; - E_8(encoded, offset, m->operation); - offset++; - return offset; - } - case AMQP_TEST_INTEGER_OK_METHOD: { - amqp_test_integer_ok_t *m = (amqp_test_integer_ok_t *) decoded; - E_64(encoded, offset, m->result); - offset += 8; - return offset; - } - case AMQP_TEST_STRING_METHOD: { - amqp_test_string_t *m = (amqp_test_string_t *) decoded; - E_8(encoded, offset, m->string_1.len); - offset++; - E_BYTES(encoded, offset, m->string_1.len, m->string_1.bytes); - offset += m->string_1.len; - E_32(encoded, offset, m->string_2.len); - offset += 4; - E_BYTES(encoded, offset, m->string_2.len, m->string_2.bytes); - offset += m->string_2.len; - E_8(encoded, offset, m->operation); - offset++; - return offset; - } - case AMQP_TEST_STRING_OK_METHOD: { - amqp_test_string_ok_t *m = (amqp_test_string_ok_t *) decoded; - E_32(encoded, offset, m->result.len); - offset += 4; - E_BYTES(encoded, offset, m->result.len, m->result.bytes); - offset += m->result.len; - return offset; - } - case AMQP_TEST_TABLE_METHOD: { - amqp_test_table_t *m = (amqp_test_table_t *) decoded; - table_result = amqp_encode_table(encoded, &(m->table), &offset); - if (table_result < 0) return table_result; - E_8(encoded, offset, m->integer_op); - offset++; - E_8(encoded, offset, m->string_op); - offset++; - return offset; - } - case AMQP_TEST_TABLE_OK_METHOD: { - amqp_test_table_ok_t *m = (amqp_test_table_ok_t *) decoded; - E_64(encoded, offset, m->integer_result); - offset += 8; - E_32(encoded, offset, m->string_result.len); - offset += 4; - E_BYTES(encoded, offset, m->string_result.len, m->string_result.bytes); - offset += m->string_result.len; - return offset; - } - case AMQP_TEST_CONTENT_METHOD: { - return offset; - } - case AMQP_TEST_CONTENT_OK_METHOD: { - amqp_test_content_ok_t *m = (amqp_test_content_ok_t *) decoded; - E_32(encoded, offset, m->content_checksum); - offset += 4; - return offset; - } - default: return -ENOENT; + return (int)offset; + } + case AMQP_CONFIRM_SELECT_METHOD: { + amqp_confirm_select_t *m = (amqp_confirm_select_t *)decoded; + bit_buffer = 0; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONFIRM_SELECT_OK_METHOD: { + return (int)offset; + } + default: + return AMQP_STATUS_UNKNOWN_METHOD; } } -int amqp_encode_properties(uint16_t class_id, - void *decoded, - amqp_bytes_t encoded) -{ - int offset = 0; - int table_result; +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! */ + amqp_flags_t flags = *(amqp_flags_t *)decoded; /* cheating! */ { /* We take a copy of flags to avoid destroying it, as it is used @@ -2451,226 +2314,563 @@ int amqp_encode_properties(uint16_t class_id, do { amqp_flags_t remainder = remaining_flags >> 16; uint16_t partial_flags = remaining_flags & 0xFFFE; - if (remainder != 0) { partial_flags |= 1; } - E_16(encoded, offset, partial_flags); - offset += 2; + 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) { case 10: { - return offset; + return (int)offset; } case 20: { - return offset; + return (int)offset; } case 30: { - return offset; + return (int)offset; } case 40: { - return offset; + return (int)offset; } case 50: { - return offset; + return (int)offset; } case 60: { - amqp_basic_properties_t *p = (amqp_basic_properties_t *) decoded; + amqp_basic_properties_t *p = (amqp_basic_properties_t *)decoded; if (flags & AMQP_BASIC_CONTENT_TYPE_FLAG) { - E_8(encoded, offset, p->content_type.len); - offset++; - E_BYTES(encoded, offset, p->content_type.len, p->content_type.bytes); - offset += p->content_type.len; + if (UINT8_MAX < p->content_type.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->content_type.len) || + !amqp_encode_bytes(encoded, &offset, p->content_type)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) { - E_8(encoded, offset, p->content_encoding.len); - offset++; - E_BYTES(encoded, offset, p->content_encoding.len, p->content_encoding.bytes); - offset += p->content_encoding.len; + if (UINT8_MAX < p->content_encoding.len || + !amqp_encode_8(encoded, &offset, + (uint8_t)p->content_encoding.len) || + !amqp_encode_bytes(encoded, &offset, p->content_encoding)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_HEADERS_FLAG) { - table_result = amqp_encode_table(encoded, &(p->headers), &offset); - if (table_result < 0) return table_result; + { + int res = amqp_encode_table(encoded, &(p->headers), &offset); + if (res < 0) return res; + } } if (flags & AMQP_BASIC_DELIVERY_MODE_FLAG) { - E_8(encoded, offset, p->delivery_mode); - offset++; + if (!amqp_encode_8(encoded, &offset, p->delivery_mode)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_PRIORITY_FLAG) { - E_8(encoded, offset, p->priority); - offset++; + if (!amqp_encode_8(encoded, &offset, p->priority)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_CORRELATION_ID_FLAG) { - E_8(encoded, offset, p->correlation_id.len); - offset++; - E_BYTES(encoded, offset, p->correlation_id.len, p->correlation_id.bytes); - offset += p->correlation_id.len; + if (UINT8_MAX < p->correlation_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->correlation_id.len) || + !amqp_encode_bytes(encoded, &offset, p->correlation_id)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_REPLY_TO_FLAG) { - E_8(encoded, offset, p->reply_to.len); - offset++; - E_BYTES(encoded, offset, p->reply_to.len, p->reply_to.bytes); - offset += p->reply_to.len; + if (UINT8_MAX < p->reply_to.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->reply_to.len) || + !amqp_encode_bytes(encoded, &offset, p->reply_to)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_EXPIRATION_FLAG) { - E_8(encoded, offset, p->expiration.len); - offset++; - E_BYTES(encoded, offset, p->expiration.len, p->expiration.bytes); - offset += p->expiration.len; + if (UINT8_MAX < p->expiration.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->expiration.len) || + !amqp_encode_bytes(encoded, &offset, p->expiration)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_MESSAGE_ID_FLAG) { - E_8(encoded, offset, p->message_id.len); - offset++; - E_BYTES(encoded, offset, p->message_id.len, p->message_id.bytes); - offset += p->message_id.len; + if (UINT8_MAX < p->message_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->message_id.len) || + !amqp_encode_bytes(encoded, &offset, p->message_id)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_TIMESTAMP_FLAG) { - E_64(encoded, offset, p->timestamp); - offset += 8; + if (!amqp_encode_64(encoded, &offset, p->timestamp)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_TYPE_FLAG) { - E_8(encoded, offset, p->type.len); - offset++; - E_BYTES(encoded, offset, p->type.len, p->type.bytes); - offset += p->type.len; + if (UINT8_MAX < p->type.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->type.len) || + !amqp_encode_bytes(encoded, &offset, p->type)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_USER_ID_FLAG) { - E_8(encoded, offset, p->user_id.len); - offset++; - E_BYTES(encoded, offset, p->user_id.len, p->user_id.bytes); - offset += p->user_id.len; + if (UINT8_MAX < p->user_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->user_id.len) || + !amqp_encode_bytes(encoded, &offset, p->user_id)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_APP_ID_FLAG) { - E_8(encoded, offset, p->app_id.len); - offset++; - E_BYTES(encoded, offset, p->app_id.len, p->app_id.bytes); - offset += p->app_id.len; + if (UINT8_MAX < p->app_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->app_id.len) || + !amqp_encode_bytes(encoded, &offset, p->app_id)) + return AMQP_STATUS_BAD_AMQP_DATA; } if (flags & AMQP_BASIC_CLUSTER_ID_FLAG) { - E_8(encoded, offset, p->cluster_id.len); - offset++; - E_BYTES(encoded, offset, p->cluster_id.len, p->cluster_id.bytes); - offset += p->cluster_id.len; - } - return offset; - } - case 70: { - amqp_file_properties_t *p = (amqp_file_properties_t *) decoded; - if (flags & AMQP_FILE_CONTENT_TYPE_FLAG) { - E_8(encoded, offset, p->content_type.len); - offset++; - E_BYTES(encoded, offset, p->content_type.len, p->content_type.bytes); - offset += p->content_type.len; - } - if (flags & AMQP_FILE_CONTENT_ENCODING_FLAG) { - E_8(encoded, offset, p->content_encoding.len); - offset++; - E_BYTES(encoded, offset, p->content_encoding.len, p->content_encoding.bytes); - offset += p->content_encoding.len; - } - if (flags & AMQP_FILE_HEADERS_FLAG) { - table_result = amqp_encode_table(encoded, &(p->headers), &offset); - if (table_result < 0) return table_result; - } - if (flags & AMQP_FILE_PRIORITY_FLAG) { - E_8(encoded, offset, p->priority); - offset++; - } - if (flags & AMQP_FILE_REPLY_TO_FLAG) { - E_8(encoded, offset, p->reply_to.len); - offset++; - E_BYTES(encoded, offset, p->reply_to.len, p->reply_to.bytes); - offset += p->reply_to.len; - } - if (flags & AMQP_FILE_MESSAGE_ID_FLAG) { - E_8(encoded, offset, p->message_id.len); - offset++; - E_BYTES(encoded, offset, p->message_id.len, p->message_id.bytes); - offset += p->message_id.len; - } - if (flags & AMQP_FILE_FILENAME_FLAG) { - E_8(encoded, offset, p->filename.len); - offset++; - E_BYTES(encoded, offset, p->filename.len, p->filename.bytes); - offset += p->filename.len; - } - if (flags & AMQP_FILE_TIMESTAMP_FLAG) { - E_64(encoded, offset, p->timestamp); - offset += 8; - } - if (flags & AMQP_FILE_CLUSTER_ID_FLAG) { - E_8(encoded, offset, p->cluster_id.len); - offset++; - E_BYTES(encoded, offset, p->cluster_id.len, p->cluster_id.bytes); - offset += p->cluster_id.len; - } - return offset; - } - case 80: { - amqp_stream_properties_t *p = (amqp_stream_properties_t *) decoded; - if (flags & AMQP_STREAM_CONTENT_TYPE_FLAG) { - E_8(encoded, offset, p->content_type.len); - offset++; - E_BYTES(encoded, offset, p->content_type.len, p->content_type.bytes); - offset += p->content_type.len; - } - if (flags & AMQP_STREAM_CONTENT_ENCODING_FLAG) { - E_8(encoded, offset, p->content_encoding.len); - offset++; - E_BYTES(encoded, offset, p->content_encoding.len, p->content_encoding.bytes); - offset += p->content_encoding.len; - } - if (flags & AMQP_STREAM_HEADERS_FLAG) { - table_result = amqp_encode_table(encoded, &(p->headers), &offset); - if (table_result < 0) return table_result; - } - if (flags & AMQP_STREAM_PRIORITY_FLAG) { - E_8(encoded, offset, p->priority); - offset++; - } - if (flags & AMQP_STREAM_TIMESTAMP_FLAG) { - E_64(encoded, offset, p->timestamp); - offset += 8; - } - return offset; + if (UINT8_MAX < p->cluster_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->cluster_id.len) || + !amqp_encode_bytes(encoded, &offset, p->cluster_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + return (int)offset; } case 90: { - return offset; - } - case 100: { - return offset; - } - case 110: { - amqp_tunnel_properties_t *p = (amqp_tunnel_properties_t *) decoded; - if (flags & AMQP_TUNNEL_HEADERS_FLAG) { - table_result = amqp_encode_table(encoded, &(p->headers), &offset); - if (table_result < 0) return table_result; - } - if (flags & AMQP_TUNNEL_PROXY_NAME_FLAG) { - E_8(encoded, offset, p->proxy_name.len); - offset++; - E_BYTES(encoded, offset, p->proxy_name.len, p->proxy_name.bytes); - offset += p->proxy_name.len; - } - if (flags & AMQP_TUNNEL_DATA_NAME_FLAG) { - E_8(encoded, offset, p->data_name.len); - offset++; - E_BYTES(encoded, offset, p->data_name.len, p->data_name.bytes); - offset += p->data_name.len; - } - if (flags & AMQP_TUNNEL_DURABLE_FLAG) { - E_8(encoded, offset, p->durable); - offset++; - } - if (flags & AMQP_TUNNEL_BROADCAST_FLAG) { - E_8(encoded, offset, p->broadcast); - offset++; - } - return offset; + return (int)offset; } - case 120: { - return offset; + case 85: { + return (int)offset; } - default: return -ENOENT; + default: + return AMQP_STATUS_UNKNOWN_CLASS; } } + +/** + * amqp_channel_open + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_channel_open_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_channel_open_ok_t *AMQP_CALL + amqp_channel_open(amqp_connection_state_t state, amqp_channel_t channel) { + amqp_channel_open_t req; + req.out_of_band = amqp_empty_bytes; + + return amqp_simple_rpc_decoded(state, channel, AMQP_CHANNEL_OPEN_METHOD, + AMQP_CHANNEL_OPEN_OK_METHOD, &req); +} + +/** + * amqp_channel_flow + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] active active + * @returns amqp_channel_flow_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_channel_flow_ok_t *AMQP_CALL + amqp_channel_flow(amqp_connection_state_t state, amqp_channel_t channel, + amqp_boolean_t active) { + amqp_channel_flow_t req; + req.active = active; + + return amqp_simple_rpc_decoded(state, channel, AMQP_CHANNEL_FLOW_METHOD, + AMQP_CHANNEL_FLOW_OK_METHOD, &req); +} + +/** + * amqp_exchange_declare + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] exchange exchange + * @param [in] type type + * @param [in] passive passive + * @param [in] durable durable + * @param [in] auto_delete auto_delete + * @param [in] internal internal + * @param [in] arguments arguments + * @returns amqp_exchange_declare_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_declare_ok_t *AMQP_CALL amqp_exchange_declare( + amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t exchange, amqp_bytes_t type, amqp_boolean_t passive, + amqp_boolean_t durable, amqp_boolean_t auto_delete, amqp_boolean_t internal, + amqp_table_t arguments) { + amqp_exchange_declare_t req; + req.ticket = 0; + req.exchange = exchange; + req.type = type; + req.passive = passive; + req.durable = durable; + req.auto_delete = auto_delete; + req.internal = internal; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_DECLARE_METHOD, + AMQP_EXCHANGE_DECLARE_OK_METHOD, &req); +} + +/** + * amqp_exchange_delete + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] exchange exchange + * @param [in] if_unused if_unused + * @returns amqp_exchange_delete_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_delete_ok_t *AMQP_CALL + amqp_exchange_delete(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t exchange, amqp_boolean_t if_unused) { + amqp_exchange_delete_t req; + req.ticket = 0; + req.exchange = exchange; + req.if_unused = if_unused; + req.nowait = 0; + + return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_DELETE_METHOD, + AMQP_EXCHANGE_DELETE_OK_METHOD, &req); +} + +/** + * amqp_exchange_bind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] destination destination + * @param [in] source source + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_exchange_bind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_bind_ok_t *AMQP_CALL + amqp_exchange_bind(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t destination, amqp_bytes_t source, + amqp_bytes_t routing_key, amqp_table_t arguments) { + amqp_exchange_bind_t req; + req.ticket = 0; + req.destination = destination; + req.source = source; + req.routing_key = routing_key; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_BIND_METHOD, + AMQP_EXCHANGE_BIND_OK_METHOD, &req); +} + +/** + * amqp_exchange_unbind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] destination destination + * @param [in] source source + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_exchange_unbind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_unbind_ok_t *AMQP_CALL + amqp_exchange_unbind(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t destination, amqp_bytes_t source, + amqp_bytes_t routing_key, amqp_table_t arguments) { + amqp_exchange_unbind_t req; + req.ticket = 0; + req.destination = destination; + req.source = source; + req.routing_key = routing_key; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_UNBIND_METHOD, + AMQP_EXCHANGE_UNBIND_OK_METHOD, &req); +} + +/** + * amqp_queue_declare + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] passive passive + * @param [in] durable durable + * @param [in] exclusive exclusive + * @param [in] auto_delete auto_delete + * @param [in] arguments arguments + * @returns amqp_queue_declare_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_declare_ok_t *AMQP_CALL amqp_queue_declare( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_boolean_t passive, amqp_boolean_t durable, amqp_boolean_t exclusive, + amqp_boolean_t auto_delete, amqp_table_t arguments) { + amqp_queue_declare_t req; + req.ticket = 0; + req.queue = queue; + req.passive = passive; + req.durable = durable; + req.exclusive = exclusive; + req.auto_delete = auto_delete; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_DECLARE_METHOD, + AMQP_QUEUE_DECLARE_OK_METHOD, &req); +} + +/** + * amqp_queue_bind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] exchange exchange + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_queue_bind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_bind_ok_t *AMQP_CALL amqp_queue_bind( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments) { + amqp_queue_bind_t req; + req.ticket = 0; + req.queue = queue; + req.exchange = exchange; + req.routing_key = routing_key; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_BIND_METHOD, + AMQP_QUEUE_BIND_OK_METHOD, &req); +} + +/** + * amqp_queue_purge + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @returns amqp_queue_purge_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_purge_ok_t *AMQP_CALL amqp_queue_purge(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_bytes_t queue) { + amqp_queue_purge_t req; + req.ticket = 0; + req.queue = queue; + req.nowait = 0; + + return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_PURGE_METHOD, + AMQP_QUEUE_PURGE_OK_METHOD, &req); +} + +/** + * amqp_queue_delete + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] if_unused if_unused + * @param [in] if_empty if_empty + * @returns amqp_queue_delete_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_delete_ok_t *AMQP_CALL amqp_queue_delete( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_boolean_t if_unused, amqp_boolean_t if_empty) { + amqp_queue_delete_t req; + req.ticket = 0; + req.queue = queue; + req.if_unused = if_unused; + req.if_empty = if_empty; + req.nowait = 0; + + return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_DELETE_METHOD, + AMQP_QUEUE_DELETE_OK_METHOD, &req); +} + +/** + * amqp_queue_unbind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] exchange exchange + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_queue_unbind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_unbind_ok_t *AMQP_CALL amqp_queue_unbind( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments) { + amqp_queue_unbind_t req; + req.ticket = 0; + req.queue = queue; + req.exchange = exchange; + req.routing_key = routing_key; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_UNBIND_METHOD, + AMQP_QUEUE_UNBIND_OK_METHOD, &req); +} + +/** + * amqp_basic_qos + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] prefetch_size prefetch_size + * @param [in] prefetch_count prefetch_count + * @param [in] global global + * @returns amqp_basic_qos_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_qos_ok_t *AMQP_CALL amqp_basic_qos(amqp_connection_state_t state, + amqp_channel_t channel, + uint32_t prefetch_size, + uint16_t prefetch_count, + amqp_boolean_t global) { + amqp_basic_qos_t req; + req.prefetch_size = prefetch_size; + req.prefetch_count = prefetch_count; + req.global = global; + + return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_QOS_METHOD, + AMQP_BASIC_QOS_OK_METHOD, &req); +} + +/** + * amqp_basic_consume + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] consumer_tag consumer_tag + * @param [in] no_local no_local + * @param [in] no_ack no_ack + * @param [in] exclusive exclusive + * @param [in] arguments arguments + * @returns amqp_basic_consume_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_consume_ok_t *AMQP_CALL amqp_basic_consume( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_bytes_t consumer_tag, amqp_boolean_t no_local, amqp_boolean_t no_ack, + amqp_boolean_t exclusive, amqp_table_t arguments) { + amqp_basic_consume_t req; + req.ticket = 0; + req.queue = queue; + req.consumer_tag = consumer_tag; + req.no_local = no_local; + req.no_ack = no_ack; + req.exclusive = exclusive; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_CONSUME_METHOD, + AMQP_BASIC_CONSUME_OK_METHOD, &req); +} + +/** + * amqp_basic_cancel + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] consumer_tag consumer_tag + * @returns amqp_basic_cancel_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_cancel_ok_t *AMQP_CALL + amqp_basic_cancel(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t consumer_tag) { + amqp_basic_cancel_t req; + req.consumer_tag = consumer_tag; + req.nowait = 0; + + return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_CANCEL_METHOD, + AMQP_BASIC_CANCEL_OK_METHOD, &req); +} + +/** + * amqp_basic_recover + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] requeue requeue + * @returns amqp_basic_recover_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_recover_ok_t *AMQP_CALL + amqp_basic_recover(amqp_connection_state_t state, amqp_channel_t channel, + amqp_boolean_t requeue) { + amqp_basic_recover_t req; + req.requeue = requeue; + + return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_RECOVER_METHOD, + AMQP_BASIC_RECOVER_OK_METHOD, &req); +} + +/** + * amqp_tx_select + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_tx_select_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_tx_select_ok_t *AMQP_CALL amqp_tx_select(amqp_connection_state_t state, + amqp_channel_t channel) { + amqp_tx_select_t req; + + return amqp_simple_rpc_decoded(state, channel, AMQP_TX_SELECT_METHOD, + AMQP_TX_SELECT_OK_METHOD, &req); +} + +/** + * amqp_tx_commit + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_tx_commit_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_tx_commit_ok_t *AMQP_CALL amqp_tx_commit(amqp_connection_state_t state, + amqp_channel_t channel) { + amqp_tx_commit_t req; + + return amqp_simple_rpc_decoded(state, channel, AMQP_TX_COMMIT_METHOD, + AMQP_TX_COMMIT_OK_METHOD, &req); +} + +/** + * amqp_tx_rollback + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_tx_rollback_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_tx_rollback_ok_t *AMQP_CALL amqp_tx_rollback(amqp_connection_state_t state, + amqp_channel_t channel) { + amqp_tx_rollback_t req; + + return amqp_simple_rpc_decoded(state, channel, AMQP_TX_ROLLBACK_METHOD, + AMQP_TX_ROLLBACK_OK_METHOD, &req); +} + +/** + * amqp_confirm_select + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_confirm_select_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_confirm_select_ok_t *AMQP_CALL + amqp_confirm_select(amqp_connection_state_t state, amqp_channel_t channel) { + amqp_confirm_select_t req; + req.nowait = 0; + + return amqp_simple_rpc_decoded(state, channel, AMQP_CONFIRM_SELECT_METHOD, + AMQP_CONFIRM_SELECT_OK_METHOD, &req); +} diff --git a/src/librabbitmq/amqp_framing.h b/src/librabbitmq/amqp_framing.h index ecee963..836b7d1 100644 --- a/src/librabbitmq/amqp_framing.h +++ b/src/librabbitmq/amqp_framing.h @@ -1,688 +1,835 @@ -#ifndef librabbitmq_amqp_framing_h -#define librabbitmq_amqp_framing_h - -#ifdef __cplusplus -extern "C" { -#endif - -#define AMQP_PROTOCOL_VERSION_MAJOR 8 -#define AMQP_PROTOCOL_VERSION_MINOR 0 -#define AMQP_PROTOCOL_PORT 5672 -#define AMQP_FRAME_METHOD 1 -#define AMQP_FRAME_HEADER 2 -#define AMQP_FRAME_BODY 3 -#define AMQP_FRAME_OOB_METHOD 4 -#define AMQP_FRAME_OOB_HEADER 5 -#define AMQP_FRAME_OOB_BODY 6 -#define AMQP_FRAME_TRACE 7 -#define AMQP_FRAME_HEARTBEAT 8 -#define AMQP_FRAME_MIN_SIZE 4096 -#define AMQP_FRAME_END 206 -#define AMQP_REPLY_SUCCESS 200 -#define AMQP_NOT_DELIVERED 310 -#define AMQP_CONTENT_TOO_LARGE 311 -#define AMQP_NO_ROUTE 312 -#define AMQP_NO_CONSUMERS 313 -#define AMQP_ACCESS_REFUSED 403 -#define AMQP_NOT_FOUND 404 -#define AMQP_RESOURCE_LOCKED 405 -#define AMQP_PRECONDITION_FAILED 406 -#define AMQP_CONNECTION_FORCED 320 -#define AMQP_INVALID_PATH 402 -#define AMQP_FRAME_ERROR 501 -#define AMQP_SYNTAX_ERROR 502 -#define AMQP_COMMAND_INVALID 503 -#define AMQP_CHANNEL_ERROR 504 -#define AMQP_RESOURCE_ERROR 506 -#define AMQP_NOT_ALLOWED 530 -#define AMQP_NOT_IMPLEMENTED 540 -#define AMQP_INTERNAL_ERROR 541 +/* 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 + +#define AMQP_PROTOCOL_VERSION_MAJOR 0 /**< AMQP protocol version major */ +#define AMQP_PROTOCOL_VERSION_MINOR 9 /**< AMQP protocol version minor */ +#define AMQP_PROTOCOL_VERSION_REVISION \ + 1 /**< AMQP protocol version revision \ + */ +#define AMQP_PROTOCOL_PORT 5672 /**< Default AMQP Port */ +#define AMQP_FRAME_METHOD 1 /**< Constant: FRAME-METHOD */ +#define AMQP_FRAME_HEADER 2 /**< Constant: FRAME-HEADER */ +#define AMQP_FRAME_BODY 3 /**< Constant: FRAME-BODY */ +#define AMQP_FRAME_HEARTBEAT 8 /**< Constant: FRAME-HEARTBEAT */ +#define AMQP_FRAME_MIN_SIZE 4096 /**< Constant: FRAME-MIN-SIZE */ +#define AMQP_FRAME_END 206 /**< Constant: FRAME-END */ +#define AMQP_REPLY_SUCCESS 200 /**< Constant: REPLY-SUCCESS */ +#define AMQP_CONTENT_TOO_LARGE 311 /**< Constant: CONTENT-TOO-LARGE */ +#define AMQP_NO_ROUTE 312 /**< Constant: NO-ROUTE */ +#define AMQP_NO_CONSUMERS 313 /**< Constant: NO-CONSUMERS */ +#define AMQP_ACCESS_REFUSED 403 /**< Constant: ACCESS-REFUSED */ +#define AMQP_NOT_FOUND 404 /**< Constant: NOT-FOUND */ +#define AMQP_RESOURCE_LOCKED 405 /**< Constant: RESOURCE-LOCKED */ +#define AMQP_PRECONDITION_FAILED 406 /**< Constant: PRECONDITION-FAILED */ +#define AMQP_CONNECTION_FORCED 320 /**< Constant: CONNECTION-FORCED */ +#define AMQP_INVALID_PATH 402 /**< Constant: INVALID-PATH */ +#define AMQP_FRAME_ERROR 501 /**< Constant: FRAME-ERROR */ +#define AMQP_SYNTAX_ERROR 502 /**< Constant: SYNTAX-ERROR */ +#define AMQP_COMMAND_INVALID 503 /**< Constant: COMMAND-INVALID */ +#define AMQP_CHANNEL_ERROR 504 /**< Constant: CHANNEL-ERROR */ +#define AMQP_UNEXPECTED_FRAME 505 /**< Constant: UNEXPECTED-FRAME */ +#define AMQP_RESOURCE_ERROR 506 /**< Constant: RESOURCE-ERROR */ +#define AMQP_NOT_ALLOWED 530 /**< Constant: NOT-ALLOWED */ +#define AMQP_NOT_IMPLEMENTED 540 /**< Constant: NOT-IMPLEMENTED */ +#define AMQP_INTERNAL_ERROR 541 /**< Constant: INTERNAL-ERROR */ /* Function prototypes. */ -extern char const *amqp_method_name(amqp_method_number_t methodNumber); -extern amqp_boolean_t amqp_method_has_content(amqp_method_number_t methodNumber); -extern int amqp_decode_method(amqp_method_number_t methodNumber, - amqp_pool_t *pool, - amqp_bytes_t encoded, - void **decoded); -extern int amqp_decode_properties(uint16_t class_id, - amqp_pool_t *pool, - amqp_bytes_t encoded, - void **decoded); -extern int amqp_encode_method(amqp_method_number_t methodNumber, - void *decoded, - amqp_bytes_t encoded); -extern int amqp_encode_properties(uint16_t class_id, - void *decoded, - amqp_bytes_t encoded); + +/** + * 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); /* Method field records. */ -#define AMQP_CONNECTION_START_METHOD ((amqp_method_number_t) 0x000A000A) /* 10, 10; 655370 */ + +#define AMQP_CONNECTION_START_METHOD \ + ((amqp_method_number_t)0x000A000A) /**< connection.start method id \ + @internal 10, 10; 655370 */ +/** connection.start method fields */ typedef struct amqp_connection_start_t_ { - uint8_t version_major; - uint8_t version_minor; - amqp_table_t server_properties; - amqp_bytes_t mechanisms; - amqp_bytes_t locales; + uint8_t version_major; /**< version-major */ + uint8_t version_minor; /**< version-minor */ + amqp_table_t server_properties; /**< server-properties */ + amqp_bytes_t mechanisms; /**< mechanisms */ + amqp_bytes_t locales; /**< locales */ } amqp_connection_start_t; -#define AMQP_CONNECTION_START_OK_METHOD ((amqp_method_number_t) 0x000A000B) /* 10, 11; 655371 */ +#define AMQP_CONNECTION_START_OK_METHOD \ + ((amqp_method_number_t)0x000A000B) /**< connection.start-ok method id \ + @internal 10, 11; 655371 */ +/** connection.start-ok method fields */ typedef struct amqp_connection_start_ok_t_ { - amqp_table_t client_properties; - amqp_bytes_t mechanism; - amqp_bytes_t response; - amqp_bytes_t locale; + amqp_table_t client_properties; /**< client-properties */ + amqp_bytes_t mechanism; /**< mechanism */ + amqp_bytes_t response; /**< response */ + amqp_bytes_t locale; /**< locale */ } amqp_connection_start_ok_t; -#define AMQP_CONNECTION_SECURE_METHOD ((amqp_method_number_t) 0x000A0014) /* 10, 20; 655380 */ +#define AMQP_CONNECTION_SECURE_METHOD \ + ((amqp_method_number_t)0x000A0014) /**< connection.secure method id \ + @internal 10, 20; 655380 */ +/** connection.secure method fields */ typedef struct amqp_connection_secure_t_ { - amqp_bytes_t challenge; + amqp_bytes_t challenge; /**< challenge */ } amqp_connection_secure_t; -#define AMQP_CONNECTION_SECURE_OK_METHOD ((amqp_method_number_t) 0x000A0015) /* 10, 21; 655381 */ +#define AMQP_CONNECTION_SECURE_OK_METHOD \ + ((amqp_method_number_t)0x000A0015) /**< connection.secure-ok method id \ + @internal 10, 21; 655381 */ +/** connection.secure-ok method fields */ typedef struct amqp_connection_secure_ok_t_ { - amqp_bytes_t response; + amqp_bytes_t response; /**< response */ } amqp_connection_secure_ok_t; -#define AMQP_CONNECTION_TUNE_METHOD ((amqp_method_number_t) 0x000A001E) /* 10, 30; 655390 */ +#define AMQP_CONNECTION_TUNE_METHOD \ + ((amqp_method_number_t)0x000A001E) /**< connection.tune method id \ + @internal 10, 30; 655390 */ +/** connection.tune method fields */ typedef struct amqp_connection_tune_t_ { - uint16_t channel_max; - uint32_t frame_max; - uint16_t heartbeat; + uint16_t channel_max; /**< channel-max */ + uint32_t frame_max; /**< frame-max */ + uint16_t heartbeat; /**< heartbeat */ } amqp_connection_tune_t; -#define AMQP_CONNECTION_TUNE_OK_METHOD ((amqp_method_number_t) 0x000A001F) /* 10, 31; 655391 */ +#define AMQP_CONNECTION_TUNE_OK_METHOD \ + ((amqp_method_number_t)0x000A001F) /**< connection.tune-ok method id \ + @internal 10, 31; 655391 */ +/** connection.tune-ok method fields */ typedef struct amqp_connection_tune_ok_t_ { - uint16_t channel_max; - uint32_t frame_max; - uint16_t heartbeat; + uint16_t channel_max; /**< channel-max */ + uint32_t frame_max; /**< frame-max */ + uint16_t heartbeat; /**< heartbeat */ } amqp_connection_tune_ok_t; -#define AMQP_CONNECTION_OPEN_METHOD ((amqp_method_number_t) 0x000A0028) /* 10, 40; 655400 */ +#define AMQP_CONNECTION_OPEN_METHOD \ + ((amqp_method_number_t)0x000A0028) /**< connection.open method id \ + @internal 10, 40; 655400 */ +/** connection.open method fields */ typedef struct amqp_connection_open_t_ { - amqp_bytes_t virtual_host; - amqp_bytes_t capabilities; - amqp_boolean_t insist; + amqp_bytes_t virtual_host; /**< virtual-host */ + amqp_bytes_t capabilities; /**< capabilities */ + amqp_boolean_t insist; /**< insist */ } amqp_connection_open_t; -#define AMQP_CONNECTION_OPEN_OK_METHOD ((amqp_method_number_t) 0x000A0029) /* 10, 41; 655401 */ +#define AMQP_CONNECTION_OPEN_OK_METHOD \ + ((amqp_method_number_t)0x000A0029) /**< connection.open-ok method id \ + @internal 10, 41; 655401 */ +/** connection.open-ok method fields */ typedef struct amqp_connection_open_ok_t_ { - amqp_bytes_t known_hosts; + amqp_bytes_t known_hosts; /**< known-hosts */ } amqp_connection_open_ok_t; -#define AMQP_CONNECTION_REDIRECT_METHOD ((amqp_method_number_t) 0x000A0032) /* 10, 50; 655410 */ -typedef struct amqp_connection_redirect_t_ { - amqp_bytes_t host; - amqp_bytes_t known_hosts; -} amqp_connection_redirect_t; - -#define AMQP_CONNECTION_CLOSE_METHOD ((amqp_method_number_t) 0x000A003C) /* 10, 60; 655420 */ +#define AMQP_CONNECTION_CLOSE_METHOD \ + ((amqp_method_number_t)0x000A0032) /**< connection.close method id \ + @internal 10, 50; 655410 */ +/** connection.close method fields */ typedef struct amqp_connection_close_t_ { - uint16_t reply_code; - amqp_bytes_t reply_text; - uint16_t class_id; - uint16_t method_id; + uint16_t reply_code; /**< reply-code */ + amqp_bytes_t reply_text; /**< reply-text */ + uint16_t class_id; /**< class-id */ + uint16_t method_id; /**< method-id */ } amqp_connection_close_t; -#define AMQP_CONNECTION_CLOSE_OK_METHOD ((amqp_method_number_t) 0x000A003D) /* 10, 61; 655421 */ +#define AMQP_CONNECTION_CLOSE_OK_METHOD \ + ((amqp_method_number_t)0x000A0033) /**< connection.close-ok method id \ + @internal 10, 51; 655411 */ +/** connection.close-ok method fields */ typedef struct amqp_connection_close_ok_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_connection_close_ok_t; -#define AMQP_CHANNEL_OPEN_METHOD ((amqp_method_number_t) 0x0014000A) /* 20, 10; 1310730 */ +#define AMQP_CONNECTION_BLOCKED_METHOD \ + ((amqp_method_number_t)0x000A003C) /**< connection.blocked method id \ + @internal 10, 60; 655420 */ +/** connection.blocked method fields */ +typedef struct amqp_connection_blocked_t_ { + amqp_bytes_t reason; /**< reason */ +} amqp_connection_blocked_t; + +#define AMQP_CONNECTION_UNBLOCKED_METHOD \ + ((amqp_method_number_t)0x000A003D) /**< connection.unblocked method id \ + @internal 10, 61; 655421 */ +/** connection.unblocked method fields */ +typedef struct amqp_connection_unblocked_t_ { + char dummy; /**< Dummy field to avoid empty struct */ +} amqp_connection_unblocked_t; + +#define AMQP_CHANNEL_OPEN_METHOD \ + ((amqp_method_number_t)0x0014000A) /**< channel.open method id @internal \ + 20, 10; 1310730 */ +/** channel.open method fields */ typedef struct amqp_channel_open_t_ { - amqp_bytes_t out_of_band; + amqp_bytes_t out_of_band; /**< out-of-band */ } amqp_channel_open_t; -#define AMQP_CHANNEL_OPEN_OK_METHOD ((amqp_method_number_t) 0x0014000B) /* 20, 11; 1310731 */ +#define AMQP_CHANNEL_OPEN_OK_METHOD \ + ((amqp_method_number_t)0x0014000B) /**< channel.open-ok method id \ + @internal 20, 11; 1310731 */ +/** channel.open-ok method fields */ typedef struct amqp_channel_open_ok_t_ { - unsigned unused:1; + amqp_bytes_t channel_id; /**< channel-id */ } amqp_channel_open_ok_t; -#define AMQP_CHANNEL_FLOW_METHOD ((amqp_method_number_t) 0x00140014) /* 20, 20; 1310740 */ +#define AMQP_CHANNEL_FLOW_METHOD \ + ((amqp_method_number_t)0x00140014) /**< channel.flow method id @internal \ + 20, 20; 1310740 */ +/** channel.flow method fields */ typedef struct amqp_channel_flow_t_ { - amqp_boolean_t active; + amqp_boolean_t active; /**< active */ } amqp_channel_flow_t; -#define AMQP_CHANNEL_FLOW_OK_METHOD ((amqp_method_number_t) 0x00140015) /* 20, 21; 1310741 */ +#define AMQP_CHANNEL_FLOW_OK_METHOD \ + ((amqp_method_number_t)0x00140015) /**< channel.flow-ok method id \ + @internal 20, 21; 1310741 */ +/** channel.flow-ok method fields */ typedef struct amqp_channel_flow_ok_t_ { - amqp_boolean_t active; + amqp_boolean_t active; /**< active */ } amqp_channel_flow_ok_t; -#define AMQP_CHANNEL_ALERT_METHOD ((amqp_method_number_t) 0x0014001E) /* 20, 30; 1310750 */ -typedef struct amqp_channel_alert_t_ { - uint16_t reply_code; - amqp_bytes_t reply_text; - amqp_table_t details; -} amqp_channel_alert_t; - -#define AMQP_CHANNEL_CLOSE_METHOD ((amqp_method_number_t) 0x00140028) /* 20, 40; 1310760 */ +#define AMQP_CHANNEL_CLOSE_METHOD \ + ((amqp_method_number_t)0x00140028) /**< channel.close method id @internal \ + 20, 40; 1310760 */ +/** channel.close method fields */ typedef struct amqp_channel_close_t_ { - uint16_t reply_code; - amqp_bytes_t reply_text; - uint16_t class_id; - uint16_t method_id; + uint16_t reply_code; /**< reply-code */ + amqp_bytes_t reply_text; /**< reply-text */ + uint16_t class_id; /**< class-id */ + uint16_t method_id; /**< method-id */ } amqp_channel_close_t; -#define AMQP_CHANNEL_CLOSE_OK_METHOD ((amqp_method_number_t) 0x00140029) /* 20, 41; 1310761 */ +#define AMQP_CHANNEL_CLOSE_OK_METHOD \ + ((amqp_method_number_t)0x00140029) /**< channel.close-ok method id \ + @internal 20, 41; 1310761 */ +/** channel.close-ok method fields */ typedef struct amqp_channel_close_ok_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_channel_close_ok_t; -#define AMQP_ACCESS_REQUEST_METHOD ((amqp_method_number_t) 0x001E000A) /* 30, 10; 1966090 */ +#define AMQP_ACCESS_REQUEST_METHOD \ + ((amqp_method_number_t)0x001E000A) /**< access.request method id @internal \ + 30, 10; 1966090 */ +/** access.request method fields */ typedef struct amqp_access_request_t_ { - amqp_bytes_t realm; - amqp_boolean_t exclusive; - amqp_boolean_t passive; - amqp_boolean_t active; - amqp_boolean_t write; - amqp_boolean_t read; + amqp_bytes_t realm; /**< realm */ + amqp_boolean_t exclusive; /**< exclusive */ + amqp_boolean_t passive; /**< passive */ + amqp_boolean_t active; /**< active */ + amqp_boolean_t write; /**< write */ + amqp_boolean_t read; /**< read */ } amqp_access_request_t; -#define AMQP_ACCESS_REQUEST_OK_METHOD ((amqp_method_number_t) 0x001E000B) /* 30, 11; 1966091 */ +#define AMQP_ACCESS_REQUEST_OK_METHOD \ + ((amqp_method_number_t)0x001E000B) /**< access.request-ok method id \ + @internal 30, 11; 1966091 */ +/** access.request-ok method fields */ typedef struct amqp_access_request_ok_t_ { - uint16_t ticket; + uint16_t ticket; /**< ticket */ } amqp_access_request_ok_t; -#define AMQP_EXCHANGE_DECLARE_METHOD ((amqp_method_number_t) 0x0028000A) /* 40, 10; 2621450 */ +#define AMQP_EXCHANGE_DECLARE_METHOD \ + ((amqp_method_number_t)0x0028000A) /**< exchange.declare method id \ + @internal 40, 10; 2621450 */ +/** exchange.declare method fields */ typedef struct amqp_exchange_declare_t_ { - uint16_t ticket; - amqp_bytes_t exchange; - amqp_bytes_t type; - amqp_boolean_t passive; - amqp_boolean_t durable; - amqp_boolean_t auto_delete; - amqp_boolean_t internal; - amqp_boolean_t nowait; - amqp_table_t arguments; + uint16_t ticket; /**< ticket */ + amqp_bytes_t exchange; /**< exchange */ + amqp_bytes_t type; /**< type */ + amqp_boolean_t passive; /**< passive */ + amqp_boolean_t durable; /**< durable */ + amqp_boolean_t auto_delete; /**< auto-delete */ + amqp_boolean_t internal; /**< internal */ + amqp_boolean_t nowait; /**< nowait */ + amqp_table_t arguments; /**< arguments */ } amqp_exchange_declare_t; -#define AMQP_EXCHANGE_DECLARE_OK_METHOD ((amqp_method_number_t) 0x0028000B) /* 40, 11; 2621451 */ +#define AMQP_EXCHANGE_DECLARE_OK_METHOD \ + ((amqp_method_number_t)0x0028000B) /**< exchange.declare-ok method id \ + @internal 40, 11; 2621451 */ +/** exchange.declare-ok method fields */ typedef struct amqp_exchange_declare_ok_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_exchange_declare_ok_t; -#define AMQP_EXCHANGE_DELETE_METHOD ((amqp_method_number_t) 0x00280014) /* 40, 20; 2621460 */ +#define AMQP_EXCHANGE_DELETE_METHOD \ + ((amqp_method_number_t)0x00280014) /**< exchange.delete method id \ + @internal 40, 20; 2621460 */ +/** exchange.delete method fields */ typedef struct amqp_exchange_delete_t_ { - uint16_t ticket; - amqp_bytes_t exchange; - amqp_boolean_t if_unused; - amqp_boolean_t nowait; + uint16_t ticket; /**< ticket */ + amqp_bytes_t exchange; /**< exchange */ + amqp_boolean_t if_unused; /**< if-unused */ + amqp_boolean_t nowait; /**< nowait */ } amqp_exchange_delete_t; -#define AMQP_EXCHANGE_DELETE_OK_METHOD ((amqp_method_number_t) 0x00280015) /* 40, 21; 2621461 */ +#define AMQP_EXCHANGE_DELETE_OK_METHOD \ + ((amqp_method_number_t)0x00280015) /**< exchange.delete-ok method id \ + @internal 40, 21; 2621461 */ +/** exchange.delete-ok method fields */ typedef struct amqp_exchange_delete_ok_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_exchange_delete_ok_t; -#define AMQP_QUEUE_DECLARE_METHOD ((amqp_method_number_t) 0x0032000A) /* 50, 10; 3276810 */ +#define AMQP_EXCHANGE_BIND_METHOD \ + ((amqp_method_number_t)0x0028001E) /**< exchange.bind method id @internal \ + 40, 30; 2621470 */ +/** exchange.bind method fields */ +typedef struct amqp_exchange_bind_t_ { + uint16_t ticket; /**< ticket */ + amqp_bytes_t destination; /**< destination */ + amqp_bytes_t source; /**< source */ + amqp_bytes_t routing_key; /**< routing-key */ + amqp_boolean_t nowait; /**< nowait */ + amqp_table_t arguments; /**< arguments */ +} amqp_exchange_bind_t; + +#define AMQP_EXCHANGE_BIND_OK_METHOD \ + ((amqp_method_number_t)0x0028001F) /**< exchange.bind-ok method id \ + @internal 40, 31; 2621471 */ +/** exchange.bind-ok method fields */ +typedef struct amqp_exchange_bind_ok_t_ { + char dummy; /**< Dummy field to avoid empty struct */ +} amqp_exchange_bind_ok_t; + +#define AMQP_EXCHANGE_UNBIND_METHOD \ + ((amqp_method_number_t)0x00280028) /**< exchange.unbind method id \ + @internal 40, 40; 2621480 */ +/** exchange.unbind method fields */ +typedef struct amqp_exchange_unbind_t_ { + uint16_t ticket; /**< ticket */ + amqp_bytes_t destination; /**< destination */ + amqp_bytes_t source; /**< source */ + amqp_bytes_t routing_key; /**< routing-key */ + amqp_boolean_t nowait; /**< nowait */ + amqp_table_t arguments; /**< arguments */ +} amqp_exchange_unbind_t; + +#define AMQP_EXCHANGE_UNBIND_OK_METHOD \ + ((amqp_method_number_t)0x00280033) /**< exchange.unbind-ok method id \ + @internal 40, 51; 2621491 */ +/** exchange.unbind-ok method fields */ +typedef struct amqp_exchange_unbind_ok_t_ { + char dummy; /**< Dummy field to avoid empty struct */ +} amqp_exchange_unbind_ok_t; + +#define AMQP_QUEUE_DECLARE_METHOD \ + ((amqp_method_number_t)0x0032000A) /**< queue.declare method id @internal \ + 50, 10; 3276810 */ +/** queue.declare method fields */ typedef struct amqp_queue_declare_t_ { - uint16_t ticket; - amqp_bytes_t queue; - amqp_boolean_t passive; - amqp_boolean_t durable; - amqp_boolean_t exclusive; - amqp_boolean_t auto_delete; - amqp_boolean_t nowait; - amqp_table_t arguments; + uint16_t ticket; /**< ticket */ + amqp_bytes_t queue; /**< queue */ + amqp_boolean_t passive; /**< passive */ + amqp_boolean_t durable; /**< durable */ + amqp_boolean_t exclusive; /**< exclusive */ + amqp_boolean_t auto_delete; /**< auto-delete */ + amqp_boolean_t nowait; /**< nowait */ + amqp_table_t arguments; /**< arguments */ } amqp_queue_declare_t; -#define AMQP_QUEUE_DECLARE_OK_METHOD ((amqp_method_number_t) 0x0032000B) /* 50, 11; 3276811 */ +#define AMQP_QUEUE_DECLARE_OK_METHOD \ + ((amqp_method_number_t)0x0032000B) /**< queue.declare-ok method id \ + @internal 50, 11; 3276811 */ +/** queue.declare-ok method fields */ typedef struct amqp_queue_declare_ok_t_ { - amqp_bytes_t queue; - uint32_t message_count; - uint32_t consumer_count; + amqp_bytes_t queue; /**< queue */ + uint32_t message_count; /**< message-count */ + uint32_t consumer_count; /**< consumer-count */ } amqp_queue_declare_ok_t; -#define AMQP_QUEUE_BIND_METHOD ((amqp_method_number_t) 0x00320014) /* 50, 20; 3276820 */ +#define AMQP_QUEUE_BIND_METHOD \ + ((amqp_method_number_t)0x00320014) /**< queue.bind method id @internal 50, \ + 20; 3276820 */ +/** queue.bind method fields */ typedef struct amqp_queue_bind_t_ { - uint16_t ticket; - amqp_bytes_t queue; - amqp_bytes_t exchange; - amqp_bytes_t routing_key; - amqp_boolean_t nowait; - amqp_table_t arguments; + uint16_t ticket; /**< ticket */ + amqp_bytes_t queue; /**< queue */ + amqp_bytes_t exchange; /**< exchange */ + amqp_bytes_t routing_key; /**< routing-key */ + amqp_boolean_t nowait; /**< nowait */ + amqp_table_t arguments; /**< arguments */ } amqp_queue_bind_t; -#define AMQP_QUEUE_BIND_OK_METHOD ((amqp_method_number_t) 0x00320015) /* 50, 21; 3276821 */ +#define AMQP_QUEUE_BIND_OK_METHOD \ + ((amqp_method_number_t)0x00320015) /**< queue.bind-ok method id @internal \ + 50, 21; 3276821 */ +/** queue.bind-ok method fields */ typedef struct amqp_queue_bind_ok_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_queue_bind_ok_t; -#define AMQP_QUEUE_PURGE_METHOD ((amqp_method_number_t) 0x0032001E) /* 50, 30; 3276830 */ +#define AMQP_QUEUE_PURGE_METHOD \ + ((amqp_method_number_t)0x0032001E) /**< queue.purge method id @internal \ + 50, 30; 3276830 */ +/** queue.purge method fields */ typedef struct amqp_queue_purge_t_ { - uint16_t ticket; - amqp_bytes_t queue; - amqp_boolean_t nowait; + uint16_t ticket; /**< ticket */ + amqp_bytes_t queue; /**< queue */ + amqp_boolean_t nowait; /**< nowait */ } amqp_queue_purge_t; -#define AMQP_QUEUE_PURGE_OK_METHOD ((amqp_method_number_t) 0x0032001F) /* 50, 31; 3276831 */ +#define AMQP_QUEUE_PURGE_OK_METHOD \ + ((amqp_method_number_t)0x0032001F) /**< queue.purge-ok method id @internal \ + 50, 31; 3276831 */ +/** queue.purge-ok method fields */ typedef struct amqp_queue_purge_ok_t_ { - uint32_t message_count; + uint32_t message_count; /**< message-count */ } amqp_queue_purge_ok_t; -#define AMQP_QUEUE_DELETE_METHOD ((amqp_method_number_t) 0x00320028) /* 50, 40; 3276840 */ +#define AMQP_QUEUE_DELETE_METHOD \ + ((amqp_method_number_t)0x00320028) /**< queue.delete method id @internal \ + 50, 40; 3276840 */ +/** queue.delete method fields */ typedef struct amqp_queue_delete_t_ { - uint16_t ticket; - amqp_bytes_t queue; - amqp_boolean_t if_unused; - amqp_boolean_t if_empty; - amqp_boolean_t nowait; + uint16_t ticket; /**< ticket */ + amqp_bytes_t queue; /**< queue */ + amqp_boolean_t if_unused; /**< if-unused */ + amqp_boolean_t if_empty; /**< if-empty */ + amqp_boolean_t nowait; /**< nowait */ } amqp_queue_delete_t; -#define AMQP_QUEUE_DELETE_OK_METHOD ((amqp_method_number_t) 0x00320029) /* 50, 41; 3276841 */ +#define AMQP_QUEUE_DELETE_OK_METHOD \ + ((amqp_method_number_t)0x00320029) /**< queue.delete-ok method id \ + @internal 50, 41; 3276841 */ +/** queue.delete-ok method fields */ typedef struct amqp_queue_delete_ok_t_ { - uint32_t message_count; + uint32_t message_count; /**< message-count */ } amqp_queue_delete_ok_t; -#define AMQP_QUEUE_UNBIND_METHOD ((amqp_method_number_t) 0x00320032) /* 50, 50; 3276850 */ +#define AMQP_QUEUE_UNBIND_METHOD \ + ((amqp_method_number_t)0x00320032) /**< queue.unbind method id @internal \ + 50, 50; 3276850 */ +/** queue.unbind method fields */ typedef struct amqp_queue_unbind_t_ { - uint16_t ticket; - amqp_bytes_t queue; - amqp_bytes_t exchange; - amqp_bytes_t routing_key; - amqp_table_t arguments; + uint16_t ticket; /**< ticket */ + amqp_bytes_t queue; /**< queue */ + amqp_bytes_t exchange; /**< exchange */ + amqp_bytes_t routing_key; /**< routing-key */ + amqp_table_t arguments; /**< arguments */ } amqp_queue_unbind_t; -#define AMQP_QUEUE_UNBIND_OK_METHOD ((amqp_method_number_t) 0x00320033) /* 50, 51; 3276851 */ +#define AMQP_QUEUE_UNBIND_OK_METHOD \ + ((amqp_method_number_t)0x00320033) /**< queue.unbind-ok method id \ + @internal 50, 51; 3276851 */ +/** queue.unbind-ok method fields */ typedef struct amqp_queue_unbind_ok_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_queue_unbind_ok_t; -#define AMQP_BASIC_QOS_METHOD ((amqp_method_number_t) 0x003C000A) /* 60, 10; 3932170 */ +#define AMQP_BASIC_QOS_METHOD \ + ((amqp_method_number_t)0x003C000A) /**< basic.qos method id @internal 60, \ + 10; 3932170 */ +/** basic.qos method fields */ typedef struct amqp_basic_qos_t_ { - uint32_t prefetch_size; - uint16_t prefetch_count; - amqp_boolean_t global; + uint32_t prefetch_size; /**< prefetch-size */ + uint16_t prefetch_count; /**< prefetch-count */ + amqp_boolean_t global; /**< global */ } amqp_basic_qos_t; -#define AMQP_BASIC_QOS_OK_METHOD ((amqp_method_number_t) 0x003C000B) /* 60, 11; 3932171 */ +#define AMQP_BASIC_QOS_OK_METHOD \ + ((amqp_method_number_t)0x003C000B) /**< basic.qos-ok method id @internal \ + 60, 11; 3932171 */ +/** basic.qos-ok method fields */ typedef struct amqp_basic_qos_ok_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_basic_qos_ok_t; -#define AMQP_BASIC_CONSUME_METHOD ((amqp_method_number_t) 0x003C0014) /* 60, 20; 3932180 */ +#define AMQP_BASIC_CONSUME_METHOD \ + ((amqp_method_number_t)0x003C0014) /**< basic.consume method id @internal \ + 60, 20; 3932180 */ +/** basic.consume method fields */ typedef struct amqp_basic_consume_t_ { - uint16_t ticket; - amqp_bytes_t queue; - amqp_bytes_t consumer_tag; - amqp_boolean_t no_local; - amqp_boolean_t no_ack; - amqp_boolean_t exclusive; - amqp_boolean_t nowait; + uint16_t ticket; /**< ticket */ + amqp_bytes_t queue; /**< queue */ + amqp_bytes_t consumer_tag; /**< consumer-tag */ + amqp_boolean_t no_local; /**< no-local */ + amqp_boolean_t no_ack; /**< no-ack */ + amqp_boolean_t exclusive; /**< exclusive */ + amqp_boolean_t nowait; /**< nowait */ + amqp_table_t arguments; /**< arguments */ } amqp_basic_consume_t; -#define AMQP_BASIC_CONSUME_OK_METHOD ((amqp_method_number_t) 0x003C0015) /* 60, 21; 3932181 */ +#define AMQP_BASIC_CONSUME_OK_METHOD \ + ((amqp_method_number_t)0x003C0015) /**< basic.consume-ok method id \ + @internal 60, 21; 3932181 */ +/** basic.consume-ok method fields */ typedef struct amqp_basic_consume_ok_t_ { - amqp_bytes_t consumer_tag; + amqp_bytes_t consumer_tag; /**< consumer-tag */ } amqp_basic_consume_ok_t; -#define AMQP_BASIC_CANCEL_METHOD ((amqp_method_number_t) 0x003C001E) /* 60, 30; 3932190 */ +#define AMQP_BASIC_CANCEL_METHOD \ + ((amqp_method_number_t)0x003C001E) /**< basic.cancel method id @internal \ + 60, 30; 3932190 */ +/** basic.cancel method fields */ typedef struct amqp_basic_cancel_t_ { - amqp_bytes_t consumer_tag; - amqp_boolean_t nowait; + amqp_bytes_t consumer_tag; /**< consumer-tag */ + amqp_boolean_t nowait; /**< nowait */ } amqp_basic_cancel_t; -#define AMQP_BASIC_CANCEL_OK_METHOD ((amqp_method_number_t) 0x003C001F) /* 60, 31; 3932191 */ +#define AMQP_BASIC_CANCEL_OK_METHOD \ + ((amqp_method_number_t)0x003C001F) /**< basic.cancel-ok method id \ + @internal 60, 31; 3932191 */ +/** basic.cancel-ok method fields */ typedef struct amqp_basic_cancel_ok_t_ { - amqp_bytes_t consumer_tag; + amqp_bytes_t consumer_tag; /**< consumer-tag */ } amqp_basic_cancel_ok_t; -#define AMQP_BASIC_PUBLISH_METHOD ((amqp_method_number_t) 0x003C0028) /* 60, 40; 3932200 */ +#define AMQP_BASIC_PUBLISH_METHOD \ + ((amqp_method_number_t)0x003C0028) /**< basic.publish method id @internal \ + 60, 40; 3932200 */ +/** basic.publish method fields */ typedef struct amqp_basic_publish_t_ { - uint16_t ticket; - amqp_bytes_t exchange; - amqp_bytes_t routing_key; - amqp_boolean_t mandatory; - amqp_boolean_t immediate; + uint16_t ticket; /**< ticket */ + amqp_bytes_t exchange; /**< exchange */ + amqp_bytes_t routing_key; /**< routing-key */ + amqp_boolean_t mandatory; /**< mandatory */ + amqp_boolean_t immediate; /**< immediate */ } amqp_basic_publish_t; -#define AMQP_BASIC_RETURN_METHOD ((amqp_method_number_t) 0x003C0032) /* 60, 50; 3932210 */ -/* typedef in amqp.h for callback */ -struct amqp_basic_return_t_ { - uint16_t reply_code; - amqp_bytes_t reply_text; - amqp_bytes_t exchange; - amqp_bytes_t routing_key; -}; - -#define AMQP_BASIC_DELIVER_METHOD ((amqp_method_number_t) 0x003C003C) /* 60, 60; 3932220 */ +#define AMQP_BASIC_RETURN_METHOD \ + ((amqp_method_number_t)0x003C0032) /**< basic.return method id @internal \ + 60, 50; 3932210 */ +/** basic.return method fields */ +typedef struct amqp_basic_return_t_ { + uint16_t reply_code; /**< reply-code */ + amqp_bytes_t reply_text; /**< reply-text */ + amqp_bytes_t exchange; /**< exchange */ + amqp_bytes_t routing_key; /**< routing-key */ +} amqp_basic_return_t; + +#define AMQP_BASIC_DELIVER_METHOD \ + ((amqp_method_number_t)0x003C003C) /**< basic.deliver method id @internal \ + 60, 60; 3932220 */ +/** basic.deliver method fields */ typedef struct amqp_basic_deliver_t_ { - amqp_bytes_t consumer_tag; - uint64_t delivery_tag; - amqp_boolean_t redelivered; - amqp_bytes_t exchange; - amqp_bytes_t routing_key; + amqp_bytes_t consumer_tag; /**< consumer-tag */ + uint64_t delivery_tag; /**< delivery-tag */ + amqp_boolean_t redelivered; /**< redelivered */ + amqp_bytes_t exchange; /**< exchange */ + amqp_bytes_t routing_key; /**< routing-key */ } amqp_basic_deliver_t; -#define AMQP_BASIC_GET_METHOD ((amqp_method_number_t) 0x003C0046) /* 60, 70; 3932230 */ +#define AMQP_BASIC_GET_METHOD \ + ((amqp_method_number_t)0x003C0046) /**< basic.get method id @internal 60, \ + 70; 3932230 */ +/** basic.get method fields */ typedef struct amqp_basic_get_t_ { - uint16_t ticket; - amqp_bytes_t queue; - amqp_boolean_t no_ack; + uint16_t ticket; /**< ticket */ + amqp_bytes_t queue; /**< queue */ + amqp_boolean_t no_ack; /**< no-ack */ } amqp_basic_get_t; -#define AMQP_BASIC_GET_OK_METHOD ((amqp_method_number_t) 0x003C0047) /* 60, 71; 3932231 */ +#define AMQP_BASIC_GET_OK_METHOD \ + ((amqp_method_number_t)0x003C0047) /**< basic.get-ok method id @internal \ + 60, 71; 3932231 */ +/** basic.get-ok method fields */ typedef struct amqp_basic_get_ok_t_ { - uint64_t delivery_tag; - amqp_boolean_t redelivered; - amqp_bytes_t exchange; - amqp_bytes_t routing_key; - uint32_t message_count; + uint64_t delivery_tag; /**< delivery-tag */ + amqp_boolean_t redelivered; /**< redelivered */ + amqp_bytes_t exchange; /**< exchange */ + amqp_bytes_t routing_key; /**< routing-key */ + uint32_t message_count; /**< message-count */ } amqp_basic_get_ok_t; -#define AMQP_BASIC_GET_EMPTY_METHOD ((amqp_method_number_t) 0x003C0048) /* 60, 72; 3932232 */ +#define AMQP_BASIC_GET_EMPTY_METHOD \ + ((amqp_method_number_t)0x003C0048) /**< basic.get-empty method id \ + @internal 60, 72; 3932232 */ +/** basic.get-empty method fields */ typedef struct amqp_basic_get_empty_t_ { - amqp_bytes_t cluster_id; + amqp_bytes_t cluster_id; /**< cluster-id */ } amqp_basic_get_empty_t; -#define AMQP_BASIC_ACK_METHOD ((amqp_method_number_t) 0x003C0050) /* 60, 80; 3932240 */ +#define AMQP_BASIC_ACK_METHOD \ + ((amqp_method_number_t)0x003C0050) /**< basic.ack method id @internal 60, \ + 80; 3932240 */ +/** basic.ack method fields */ typedef struct amqp_basic_ack_t_ { - uint64_t delivery_tag; - amqp_boolean_t multiple; + uint64_t delivery_tag; /**< delivery-tag */ + amqp_boolean_t multiple; /**< multiple */ } amqp_basic_ack_t; -#define AMQP_BASIC_REJECT_METHOD ((amqp_method_number_t) 0x003C005A) /* 60, 90; 3932250 */ +#define AMQP_BASIC_REJECT_METHOD \ + ((amqp_method_number_t)0x003C005A) /**< basic.reject method id @internal \ + 60, 90; 3932250 */ +/** basic.reject method fields */ typedef struct amqp_basic_reject_t_ { - uint64_t delivery_tag; - amqp_boolean_t requeue; + uint64_t delivery_tag; /**< delivery-tag */ + amqp_boolean_t requeue; /**< requeue */ } amqp_basic_reject_t; -#define AMQP_BASIC_RECOVER_METHOD ((amqp_method_number_t) 0x003C0064) /* 60, 100; 3932260 */ +#define AMQP_BASIC_RECOVER_ASYNC_METHOD \ + ((amqp_method_number_t)0x003C0064) /**< basic.recover-async method id \ + @internal 60, 100; 3932260 */ +/** basic.recover-async method fields */ +typedef struct amqp_basic_recover_async_t_ { + amqp_boolean_t requeue; /**< requeue */ +} amqp_basic_recover_async_t; + +#define AMQP_BASIC_RECOVER_METHOD \ + ((amqp_method_number_t)0x003C006E) /**< basic.recover method id @internal \ + 60, 110; 3932270 */ +/** basic.recover method fields */ typedef struct amqp_basic_recover_t_ { - amqp_boolean_t requeue; + amqp_boolean_t requeue; /**< requeue */ } amqp_basic_recover_t; -#define AMQP_FILE_QOS_METHOD ((amqp_method_number_t) 0x0046000A) /* 70, 10; 4587530 */ -typedef struct amqp_file_qos_t_ { - uint32_t prefetch_size; - uint16_t prefetch_count; - amqp_boolean_t global; -} amqp_file_qos_t; - -#define AMQP_FILE_QOS_OK_METHOD ((amqp_method_number_t) 0x0046000B) /* 70, 11; 4587531 */ -typedef struct amqp_file_qos_ok_t_ { - unsigned unused:1; -} amqp_file_qos_ok_t; - -#define AMQP_FILE_CONSUME_METHOD ((amqp_method_number_t) 0x00460014) /* 70, 20; 4587540 */ -typedef struct amqp_file_consume_t_ { - uint16_t ticket; - amqp_bytes_t queue; - amqp_bytes_t consumer_tag; - amqp_boolean_t no_local; - amqp_boolean_t no_ack; - amqp_boolean_t exclusive; - amqp_boolean_t nowait; -} amqp_file_consume_t; - -#define AMQP_FILE_CONSUME_OK_METHOD ((amqp_method_number_t) 0x00460015) /* 70, 21; 4587541 */ -typedef struct amqp_file_consume_ok_t_ { - amqp_bytes_t consumer_tag; -} amqp_file_consume_ok_t; - -#define AMQP_FILE_CANCEL_METHOD ((amqp_method_number_t) 0x0046001E) /* 70, 30; 4587550 */ -typedef struct amqp_file_cancel_t_ { - amqp_bytes_t consumer_tag; - amqp_boolean_t nowait; -} amqp_file_cancel_t; - -#define AMQP_FILE_CANCEL_OK_METHOD ((amqp_method_number_t) 0x0046001F) /* 70, 31; 4587551 */ -typedef struct amqp_file_cancel_ok_t_ { - amqp_bytes_t consumer_tag; -} amqp_file_cancel_ok_t; - -#define AMQP_FILE_OPEN_METHOD ((amqp_method_number_t) 0x00460028) /* 70, 40; 4587560 */ -typedef struct amqp_file_open_t_ { - amqp_bytes_t identifier; - uint64_t content_size; -} amqp_file_open_t; - -#define AMQP_FILE_OPEN_OK_METHOD ((amqp_method_number_t) 0x00460029) /* 70, 41; 4587561 */ -typedef struct amqp_file_open_ok_t_ { - uint64_t staged_size; -} amqp_file_open_ok_t; - -#define AMQP_FILE_STAGE_METHOD ((amqp_method_number_t) 0x00460032) /* 70, 50; 4587570 */ -typedef struct amqp_file_stage_t_ { - unsigned unused:1; -} amqp_file_stage_t; - -#define AMQP_FILE_PUBLISH_METHOD ((amqp_method_number_t) 0x0046003C) /* 70, 60; 4587580 */ -typedef struct amqp_file_publish_t_ { - uint16_t ticket; - amqp_bytes_t exchange; - amqp_bytes_t routing_key; - amqp_boolean_t mandatory; - amqp_boolean_t immediate; - amqp_bytes_t identifier; -} amqp_file_publish_t; - -#define AMQP_FILE_RETURN_METHOD ((amqp_method_number_t) 0x00460046) /* 70, 70; 4587590 */ -typedef struct amqp_file_return_t_ { - uint16_t reply_code; - amqp_bytes_t reply_text; - amqp_bytes_t exchange; - amqp_bytes_t routing_key; -} amqp_file_return_t; - -#define AMQP_FILE_DELIVER_METHOD ((amqp_method_number_t) 0x00460050) /* 70, 80; 4587600 */ -typedef struct amqp_file_deliver_t_ { - amqp_bytes_t consumer_tag; - uint64_t delivery_tag; - amqp_boolean_t redelivered; - amqp_bytes_t exchange; - amqp_bytes_t routing_key; - amqp_bytes_t identifier; -} amqp_file_deliver_t; - -#define AMQP_FILE_ACK_METHOD ((amqp_method_number_t) 0x0046005A) /* 70, 90; 4587610 */ -typedef struct amqp_file_ack_t_ { - uint64_t delivery_tag; - amqp_boolean_t multiple; -} amqp_file_ack_t; - -#define AMQP_FILE_REJECT_METHOD ((amqp_method_number_t) 0x00460064) /* 70, 100; 4587620 */ -typedef struct amqp_file_reject_t_ { - uint64_t delivery_tag; - amqp_boolean_t requeue; -} amqp_file_reject_t; - -#define AMQP_STREAM_QOS_METHOD ((amqp_method_number_t) 0x0050000A) /* 80, 10; 5242890 */ -typedef struct amqp_stream_qos_t_ { - uint32_t prefetch_size; - uint16_t prefetch_count; - uint32_t consume_rate; - amqp_boolean_t global; -} amqp_stream_qos_t; - -#define AMQP_STREAM_QOS_OK_METHOD ((amqp_method_number_t) 0x0050000B) /* 80, 11; 5242891 */ -typedef struct amqp_stream_qos_ok_t_ { - unsigned unused:1; -} amqp_stream_qos_ok_t; - -#define AMQP_STREAM_CONSUME_METHOD ((amqp_method_number_t) 0x00500014) /* 80, 20; 5242900 */ -typedef struct amqp_stream_consume_t_ { - uint16_t ticket; - amqp_bytes_t queue; - amqp_bytes_t consumer_tag; - amqp_boolean_t no_local; - amqp_boolean_t exclusive; - amqp_boolean_t nowait; -} amqp_stream_consume_t; - -#define AMQP_STREAM_CONSUME_OK_METHOD ((amqp_method_number_t) 0x00500015) /* 80, 21; 5242901 */ -typedef struct amqp_stream_consume_ok_t_ { - amqp_bytes_t consumer_tag; -} amqp_stream_consume_ok_t; - -#define AMQP_STREAM_CANCEL_METHOD ((amqp_method_number_t) 0x0050001E) /* 80, 30; 5242910 */ -typedef struct amqp_stream_cancel_t_ { - amqp_bytes_t consumer_tag; - amqp_boolean_t nowait; -} amqp_stream_cancel_t; - -#define AMQP_STREAM_CANCEL_OK_METHOD ((amqp_method_number_t) 0x0050001F) /* 80, 31; 5242911 */ -typedef struct amqp_stream_cancel_ok_t_ { - amqp_bytes_t consumer_tag; -} amqp_stream_cancel_ok_t; - -#define AMQP_STREAM_PUBLISH_METHOD ((amqp_method_number_t) 0x00500028) /* 80, 40; 5242920 */ -typedef struct amqp_stream_publish_t_ { - uint16_t ticket; - amqp_bytes_t exchange; - amqp_bytes_t routing_key; - amqp_boolean_t mandatory; - amqp_boolean_t immediate; -} amqp_stream_publish_t; - -#define AMQP_STREAM_RETURN_METHOD ((amqp_method_number_t) 0x00500032) /* 80, 50; 5242930 */ -typedef struct amqp_stream_return_t_ { - uint16_t reply_code; - amqp_bytes_t reply_text; - amqp_bytes_t exchange; - amqp_bytes_t routing_key; -} amqp_stream_return_t; - -#define AMQP_STREAM_DELIVER_METHOD ((amqp_method_number_t) 0x0050003C) /* 80, 60; 5242940 */ -typedef struct amqp_stream_deliver_t_ { - amqp_bytes_t consumer_tag; - uint64_t delivery_tag; - amqp_bytes_t exchange; - amqp_bytes_t queue; -} amqp_stream_deliver_t; - -#define AMQP_TX_SELECT_METHOD ((amqp_method_number_t) 0x005A000A) /* 90, 10; 5898250 */ +#define AMQP_BASIC_RECOVER_OK_METHOD \ + ((amqp_method_number_t)0x003C006F) /**< basic.recover-ok method id \ + @internal 60, 111; 3932271 */ +/** basic.recover-ok method fields */ +typedef struct amqp_basic_recover_ok_t_ { + char dummy; /**< Dummy field to avoid empty struct */ +} amqp_basic_recover_ok_t; + +#define AMQP_BASIC_NACK_METHOD \ + ((amqp_method_number_t)0x003C0078) /**< basic.nack method id @internal 60, \ + 120; 3932280 */ +/** basic.nack method fields */ +typedef struct amqp_basic_nack_t_ { + uint64_t delivery_tag; /**< delivery-tag */ + amqp_boolean_t multiple; /**< multiple */ + amqp_boolean_t requeue; /**< requeue */ +} amqp_basic_nack_t; + +#define AMQP_TX_SELECT_METHOD \ + ((amqp_method_number_t)0x005A000A) /**< tx.select method id @internal 90, \ + 10; 5898250 */ +/** tx.select method fields */ typedef struct amqp_tx_select_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_tx_select_t; -#define AMQP_TX_SELECT_OK_METHOD ((amqp_method_number_t) 0x005A000B) /* 90, 11; 5898251 */ +#define AMQP_TX_SELECT_OK_METHOD \ + ((amqp_method_number_t)0x005A000B) /**< tx.select-ok method id @internal \ + 90, 11; 5898251 */ +/** tx.select-ok method fields */ typedef struct amqp_tx_select_ok_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_tx_select_ok_t; -#define AMQP_TX_COMMIT_METHOD ((amqp_method_number_t) 0x005A0014) /* 90, 20; 5898260 */ +#define AMQP_TX_COMMIT_METHOD \ + ((amqp_method_number_t)0x005A0014) /**< tx.commit method id @internal 90, \ + 20; 5898260 */ +/** tx.commit method fields */ typedef struct amqp_tx_commit_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_tx_commit_t; -#define AMQP_TX_COMMIT_OK_METHOD ((amqp_method_number_t) 0x005A0015) /* 90, 21; 5898261 */ +#define AMQP_TX_COMMIT_OK_METHOD \ + ((amqp_method_number_t)0x005A0015) /**< tx.commit-ok method id @internal \ + 90, 21; 5898261 */ +/** tx.commit-ok method fields */ typedef struct amqp_tx_commit_ok_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_tx_commit_ok_t; -#define AMQP_TX_ROLLBACK_METHOD ((amqp_method_number_t) 0x005A001E) /* 90, 30; 5898270 */ +#define AMQP_TX_ROLLBACK_METHOD \ + ((amqp_method_number_t)0x005A001E) /**< tx.rollback method id @internal \ + 90, 30; 5898270 */ +/** tx.rollback method fields */ typedef struct amqp_tx_rollback_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_tx_rollback_t; -#define AMQP_TX_ROLLBACK_OK_METHOD ((amqp_method_number_t) 0x005A001F) /* 90, 31; 5898271 */ +#define AMQP_TX_ROLLBACK_OK_METHOD \ + ((amqp_method_number_t)0x005A001F) /**< tx.rollback-ok method id @internal \ + 90, 31; 5898271 */ +/** tx.rollback-ok method fields */ typedef struct amqp_tx_rollback_ok_t_ { - unsigned unused:1; + char dummy; /**< Dummy field to avoid empty struct */ } amqp_tx_rollback_ok_t; -#define AMQP_DTX_SELECT_METHOD ((amqp_method_number_t) 0x0064000A) /* 100, 10; 6553610 */ -typedef struct amqp_dtx_select_t_ { - unsigned unused:1; -} amqp_dtx_select_t; - -#define AMQP_DTX_SELECT_OK_METHOD ((amqp_method_number_t) 0x0064000B) /* 100, 11; 6553611 */ -typedef struct amqp_dtx_select_ok_t_ { - unsigned unused:1; -} amqp_dtx_select_ok_t; - -#define AMQP_DTX_START_METHOD ((amqp_method_number_t) 0x00640014) /* 100, 20; 6553620 */ -typedef struct amqp_dtx_start_t_ { - amqp_bytes_t dtx_identifier; -} amqp_dtx_start_t; - -#define AMQP_DTX_START_OK_METHOD ((amqp_method_number_t) 0x00640015) /* 100, 21; 6553621 */ -typedef struct amqp_dtx_start_ok_t_ { - unsigned unused:1; -} amqp_dtx_start_ok_t; - -#define AMQP_TUNNEL_REQUEST_METHOD ((amqp_method_number_t) 0x006E000A) /* 110, 10; 7208970 */ -typedef struct amqp_tunnel_request_t_ { - amqp_table_t meta_data; -} amqp_tunnel_request_t; - -#define AMQP_TEST_INTEGER_METHOD ((amqp_method_number_t) 0x0078000A) /* 120, 10; 7864330 */ -typedef struct amqp_test_integer_t_ { - uint8_t integer_1; - uint16_t integer_2; - uint32_t integer_3; - uint64_t integer_4; - uint8_t operation; -} amqp_test_integer_t; - -#define AMQP_TEST_INTEGER_OK_METHOD ((amqp_method_number_t) 0x0078000B) /* 120, 11; 7864331 */ -typedef struct amqp_test_integer_ok_t_ { - uint64_t result; -} amqp_test_integer_ok_t; - -#define AMQP_TEST_STRING_METHOD ((amqp_method_number_t) 0x00780014) /* 120, 20; 7864340 */ -typedef struct amqp_test_string_t_ { - amqp_bytes_t string_1; - amqp_bytes_t string_2; - uint8_t operation; -} amqp_test_string_t; - -#define AMQP_TEST_STRING_OK_METHOD ((amqp_method_number_t) 0x00780015) /* 120, 21; 7864341 */ -typedef struct amqp_test_string_ok_t_ { - amqp_bytes_t result; -} amqp_test_string_ok_t; - -#define AMQP_TEST_TABLE_METHOD ((amqp_method_number_t) 0x0078001E) /* 120, 30; 7864350 */ -typedef struct amqp_test_table_t_ { - amqp_table_t table; - uint8_t integer_op; - uint8_t string_op; -} amqp_test_table_t; - -#define AMQP_TEST_TABLE_OK_METHOD ((amqp_method_number_t) 0x0078001F) /* 120, 31; 7864351 */ -typedef struct amqp_test_table_ok_t_ { - uint64_t integer_result; - amqp_bytes_t string_result; -} amqp_test_table_ok_t; - -#define AMQP_TEST_CONTENT_METHOD ((amqp_method_number_t) 0x00780028) /* 120, 40; 7864360 */ -typedef struct amqp_test_content_t_ { - unsigned unused:1; -} amqp_test_content_t; - -#define AMQP_TEST_CONTENT_OK_METHOD ((amqp_method_number_t) 0x00780029) /* 120, 41; 7864361 */ -typedef struct amqp_test_content_ok_t_ { - uint32_t content_checksum; -} amqp_test_content_ok_t; +#define AMQP_CONFIRM_SELECT_METHOD \ + ((amqp_method_number_t)0x0055000A) /**< confirm.select method id @internal \ + 85, 10; 5570570 */ +/** confirm.select method fields */ +typedef struct amqp_confirm_select_t_ { + amqp_boolean_t nowait; /**< nowait */ +} amqp_confirm_select_t; + +#define AMQP_CONFIRM_SELECT_OK_METHOD \ + ((amqp_method_number_t)0x0055000B) /**< confirm.select-ok method id \ + @internal 85, 11; 5570571 */ +/** confirm.select-ok method fields */ +typedef struct amqp_confirm_select_ok_t_ { + char dummy; /**< Dummy field to avoid empty struct */ +} amqp_confirm_select_ok_t; /* Class property records. */ -#define AMQP_CONNECTION_CLASS (0x000A) /* 10 */ +#define AMQP_CONNECTION_CLASS \ + (0x000A) /**< connection class id @internal 10 \ + */ +/** connection class properties */ typedef struct amqp_connection_properties_t_ { - amqp_flags_t _flags; + amqp_flags_t _flags; /**< bit-mask of set fields */ + char dummy; /**< Dummy field to avoid empty struct */ } amqp_connection_properties_t; -#define AMQP_CHANNEL_CLASS (0x0014) /* 20 */ +#define AMQP_CHANNEL_CLASS (0x0014) /**< channel class id @internal 20 */ +/** channel class properties */ typedef struct amqp_channel_properties_t_ { - amqp_flags_t _flags; + amqp_flags_t _flags; /**< bit-mask of set fields */ + char dummy; /**< Dummy field to avoid empty struct */ } amqp_channel_properties_t; -#define AMQP_ACCESS_CLASS (0x001E) /* 30 */ +#define AMQP_ACCESS_CLASS (0x001E) /**< access class id @internal 30 */ +/** access class properties */ typedef struct amqp_access_properties_t_ { - amqp_flags_t _flags; + amqp_flags_t _flags; /**< bit-mask of set fields */ + char dummy; /**< Dummy field to avoid empty struct */ } amqp_access_properties_t; -#define AMQP_EXCHANGE_CLASS (0x0028) /* 40 */ +#define AMQP_EXCHANGE_CLASS (0x0028) /**< exchange class id @internal 40 */ +/** exchange class properties */ typedef struct amqp_exchange_properties_t_ { - amqp_flags_t _flags; + amqp_flags_t _flags; /**< bit-mask of set fields */ + char dummy; /**< Dummy field to avoid empty struct */ } amqp_exchange_properties_t; -#define AMQP_QUEUE_CLASS (0x0032) /* 50 */ +#define AMQP_QUEUE_CLASS (0x0032) /**< queue class id @internal 50 */ +/** queue class properties */ typedef struct amqp_queue_properties_t_ { - amqp_flags_t _flags; + amqp_flags_t _flags; /**< bit-mask of set fields */ + char dummy; /**< Dummy field to avoid empty struct */ } amqp_queue_properties_t; -#define AMQP_BASIC_CLASS (0x003C) /* 60 */ +#define AMQP_BASIC_CLASS (0x003C) /**< basic class id @internal 60 */ #define AMQP_BASIC_CONTENT_TYPE_FLAG (1 << 15) #define AMQP_BASIC_CONTENT_ENCODING_FLAG (1 << 14) #define AMQP_BASIC_HEADERS_FLAG (1 << 13) @@ -697,94 +844,301 @@ typedef struct amqp_queue_properties_t_ { #define AMQP_BASIC_USER_ID_FLAG (1 << 4) #define AMQP_BASIC_APP_ID_FLAG (1 << 3) #define AMQP_BASIC_CLUSTER_ID_FLAG (1 << 2) +/** basic class properties */ typedef struct amqp_basic_properties_t_ { - amqp_flags_t _flags; - amqp_bytes_t content_type; - amqp_bytes_t content_encoding; - amqp_table_t headers; - uint8_t delivery_mode; - uint8_t priority; - amqp_bytes_t correlation_id; - amqp_bytes_t reply_to; - amqp_bytes_t expiration; - amqp_bytes_t message_id; - uint64_t timestamp; - amqp_bytes_t type; - amqp_bytes_t user_id; - amqp_bytes_t app_id; - amqp_bytes_t cluster_id; + amqp_flags_t _flags; /**< bit-mask of set fields */ + amqp_bytes_t content_type; /**< content-type */ + amqp_bytes_t content_encoding; /**< content-encoding */ + amqp_table_t headers; /**< headers */ + uint8_t delivery_mode; /**< delivery-mode */ + uint8_t priority; /**< priority */ + amqp_bytes_t correlation_id; /**< correlation-id */ + amqp_bytes_t reply_to; /**< reply-to */ + amqp_bytes_t expiration; /**< expiration */ + amqp_bytes_t message_id; /**< message-id */ + uint64_t timestamp; /**< timestamp */ + amqp_bytes_t type; /**< type */ + amqp_bytes_t user_id; /**< user-id */ + amqp_bytes_t app_id; /**< app-id */ + amqp_bytes_t cluster_id; /**< cluster-id */ } amqp_basic_properties_t; -#define AMQP_FILE_CLASS (0x0046) /* 70 */ -#define AMQP_FILE_CONTENT_TYPE_FLAG (1 << 15) -#define AMQP_FILE_CONTENT_ENCODING_FLAG (1 << 14) -#define AMQP_FILE_HEADERS_FLAG (1 << 13) -#define AMQP_FILE_PRIORITY_FLAG (1 << 12) -#define AMQP_FILE_REPLY_TO_FLAG (1 << 11) -#define AMQP_FILE_MESSAGE_ID_FLAG (1 << 10) -#define AMQP_FILE_FILENAME_FLAG (1 << 9) -#define AMQP_FILE_TIMESTAMP_FLAG (1 << 8) -#define AMQP_FILE_CLUSTER_ID_FLAG (1 << 7) -typedef struct amqp_file_properties_t_ { - amqp_flags_t _flags; - amqp_bytes_t content_type; - amqp_bytes_t content_encoding; - amqp_table_t headers; - uint8_t priority; - amqp_bytes_t reply_to; - amqp_bytes_t message_id; - amqp_bytes_t filename; - uint64_t timestamp; - amqp_bytes_t cluster_id; -} amqp_file_properties_t; - -#define AMQP_STREAM_CLASS (0x0050) /* 80 */ -#define AMQP_STREAM_CONTENT_TYPE_FLAG (1 << 15) -#define AMQP_STREAM_CONTENT_ENCODING_FLAG (1 << 14) -#define AMQP_STREAM_HEADERS_FLAG (1 << 13) -#define AMQP_STREAM_PRIORITY_FLAG (1 << 12) -#define AMQP_STREAM_TIMESTAMP_FLAG (1 << 11) -typedef struct amqp_stream_properties_t_ { - amqp_flags_t _flags; - amqp_bytes_t content_type; - amqp_bytes_t content_encoding; - amqp_table_t headers; - uint8_t priority; - uint64_t timestamp; -} amqp_stream_properties_t; - -#define AMQP_TX_CLASS (0x005A) /* 90 */ +#define AMQP_TX_CLASS (0x005A) /**< tx class id @internal 90 */ +/** tx class properties */ typedef struct amqp_tx_properties_t_ { - amqp_flags_t _flags; + amqp_flags_t _flags; /**< bit-mask of set fields */ + char dummy; /**< Dummy field to avoid empty struct */ } amqp_tx_properties_t; -#define AMQP_DTX_CLASS (0x0064) /* 100 */ -typedef struct amqp_dtx_properties_t_ { - amqp_flags_t _flags; -} amqp_dtx_properties_t; - -#define AMQP_TUNNEL_CLASS (0x006E) /* 110 */ -#define AMQP_TUNNEL_HEADERS_FLAG (1 << 15) -#define AMQP_TUNNEL_PROXY_NAME_FLAG (1 << 14) -#define AMQP_TUNNEL_DATA_NAME_FLAG (1 << 13) -#define AMQP_TUNNEL_DURABLE_FLAG (1 << 12) -#define AMQP_TUNNEL_BROADCAST_FLAG (1 << 11) -typedef struct amqp_tunnel_properties_t_ { - amqp_flags_t _flags; - amqp_table_t headers; - amqp_bytes_t proxy_name; - amqp_bytes_t data_name; - uint8_t durable; - uint8_t broadcast; -} amqp_tunnel_properties_t; - -#define AMQP_TEST_CLASS (0x0078) /* 120 */ -typedef struct amqp_test_properties_t_ { - amqp_flags_t _flags; -} amqp_test_properties_t; - -#ifdef __cplusplus -} -#endif - -#endif +#define AMQP_CONFIRM_CLASS (0x0055) /**< confirm class id @internal 85 */ +/** confirm class properties */ +typedef struct amqp_confirm_properties_t_ { + amqp_flags_t _flags; /**< bit-mask of set fields */ + char dummy; /**< Dummy field to avoid empty struct */ +} amqp_confirm_properties_t; + +/* API functions for methods */ + +/** + * amqp_channel_open + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_channel_open_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_channel_open_ok_t *AMQP_CALL + amqp_channel_open(amqp_connection_state_t state, amqp_channel_t channel); +/** + * amqp_channel_flow + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] active active + * @returns amqp_channel_flow_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_channel_flow_ok_t *AMQP_CALL + amqp_channel_flow(amqp_connection_state_t state, amqp_channel_t channel, + amqp_boolean_t active); +/** + * amqp_exchange_declare + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] exchange exchange + * @param [in] type type + * @param [in] passive passive + * @param [in] durable durable + * @param [in] auto_delete auto_delete + * @param [in] internal internal + * @param [in] arguments arguments + * @returns amqp_exchange_declare_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_declare_ok_t *AMQP_CALL amqp_exchange_declare( + amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t exchange, amqp_bytes_t type, amqp_boolean_t passive, + amqp_boolean_t durable, amqp_boolean_t auto_delete, amqp_boolean_t internal, + amqp_table_t arguments); +/** + * amqp_exchange_delete + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] exchange exchange + * @param [in] if_unused if_unused + * @returns amqp_exchange_delete_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_delete_ok_t *AMQP_CALL + amqp_exchange_delete(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t exchange, amqp_boolean_t if_unused); +/** + * amqp_exchange_bind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] destination destination + * @param [in] source source + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_exchange_bind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_bind_ok_t *AMQP_CALL + amqp_exchange_bind(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t destination, amqp_bytes_t source, + amqp_bytes_t routing_key, amqp_table_t arguments); +/** + * amqp_exchange_unbind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] destination destination + * @param [in] source source + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_exchange_unbind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_unbind_ok_t *AMQP_CALL + amqp_exchange_unbind(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t destination, amqp_bytes_t source, + amqp_bytes_t routing_key, amqp_table_t arguments); +/** + * amqp_queue_declare + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] passive passive + * @param [in] durable durable + * @param [in] exclusive exclusive + * @param [in] auto_delete auto_delete + * @param [in] arguments arguments + * @returns amqp_queue_declare_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_declare_ok_t *AMQP_CALL amqp_queue_declare( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_boolean_t passive, amqp_boolean_t durable, amqp_boolean_t exclusive, + amqp_boolean_t auto_delete, amqp_table_t arguments); +/** + * amqp_queue_bind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] exchange exchange + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_queue_bind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_bind_ok_t *AMQP_CALL amqp_queue_bind( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments); +/** + * amqp_queue_purge + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @returns amqp_queue_purge_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_purge_ok_t *AMQP_CALL amqp_queue_purge(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_bytes_t queue); +/** + * amqp_queue_delete + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] if_unused if_unused + * @param [in] if_empty if_empty + * @returns amqp_queue_delete_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_delete_ok_t *AMQP_CALL amqp_queue_delete( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_boolean_t if_unused, amqp_boolean_t if_empty); +/** + * amqp_queue_unbind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] exchange exchange + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_queue_unbind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_unbind_ok_t *AMQP_CALL amqp_queue_unbind( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments); +/** + * amqp_basic_qos + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] prefetch_size prefetch_size + * @param [in] prefetch_count prefetch_count + * @param [in] global global + * @returns amqp_basic_qos_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_qos_ok_t *AMQP_CALL amqp_basic_qos(amqp_connection_state_t state, + amqp_channel_t channel, + uint32_t prefetch_size, + uint16_t prefetch_count, + amqp_boolean_t global); +/** + * amqp_basic_consume + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] consumer_tag consumer_tag + * @param [in] no_local no_local + * @param [in] no_ack no_ack + * @param [in] exclusive exclusive + * @param [in] arguments arguments + * @returns amqp_basic_consume_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_consume_ok_t *AMQP_CALL amqp_basic_consume( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_bytes_t consumer_tag, amqp_boolean_t no_local, amqp_boolean_t no_ack, + amqp_boolean_t exclusive, amqp_table_t arguments); +/** + * amqp_basic_cancel + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] consumer_tag consumer_tag + * @returns amqp_basic_cancel_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_cancel_ok_t *AMQP_CALL + amqp_basic_cancel(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t consumer_tag); +/** + * amqp_basic_recover + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] requeue requeue + * @returns amqp_basic_recover_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_recover_ok_t *AMQP_CALL + amqp_basic_recover(amqp_connection_state_t state, amqp_channel_t channel, + amqp_boolean_t requeue); +/** + * amqp_tx_select + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_tx_select_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_tx_select_ok_t *AMQP_CALL amqp_tx_select(amqp_connection_state_t state, + amqp_channel_t channel); +/** + * amqp_tx_commit + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_tx_commit_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_tx_commit_ok_t *AMQP_CALL amqp_tx_commit(amqp_connection_state_t state, + amqp_channel_t channel); +/** + * amqp_tx_rollback + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_tx_rollback_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_tx_rollback_ok_t *AMQP_CALL amqp_tx_rollback(amqp_connection_state_t state, + amqp_channel_t channel); +/** + * amqp_confirm_select + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_confirm_select_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_confirm_select_ok_t *AMQP_CALL + amqp_confirm_select(amqp_connection_state_t state, amqp_channel_t channel); + +AMQP_END_DECLS + +#endif /* AMQP_FRAMING_H */ diff --git a/src/librabbitmq/amqp_hostcheck.c b/src/librabbitmq/amqp_hostcheck.c new file mode 100644 index 0000000..5a4f733 --- /dev/null +++ b/src/librabbitmq/amqp_hostcheck.c @@ -0,0 +1,195 @@ +/* + * Copyright 1996-2014 Daniel Stenberg . + * 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 + +/* 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); +} diff --git a/src/librabbitmq/amqp_hostcheck.h b/src/librabbitmq/amqp_hostcheck.h new file mode 100644 index 0000000..7ab5c26 --- /dev/null +++ b/src/librabbitmq/amqp_hostcheck.h @@ -0,0 +1,48 @@ +#ifndef librabbitmq_amqp_hostcheck_h +#define librabbitmq_amqp_hostcheck_h + +/* + * Copyright 1996-2014 Daniel Stenberg . + * 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 diff --git a/src/librabbitmq/amqp_mem.c b/src/librabbitmq/amqp_mem.c index da7c0d6..f0d47cc 100644 --- a/src/librabbitmq/amqp_mem.c +++ b/src/librabbitmq/amqp_mem.c @@ -1,17 +1,53 @@ -#include +/* + * ***** 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 +#include #include +#include #include -#include #include -#include -#include -#include "amqp.h" -#include "amqp_config.h" +char const *amqp_version(void) { return AMQP_VERSION_STRING; } -char const *amqp_version(void) { - return VERSION; /* defined in config.h */ -} +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; @@ -30,10 +66,10 @@ void init_amqp_pool(amqp_pool_t *pool, size_t pagesize) { static void empty_blocklist(amqp_pool_blocklist_t *x) { int i; - for (i = 0; i < x->num_blocks; i++) { - free(x->blocklist[i]); - } if (x->blocklist != NULL) { + for (i = 0; i < x->num_blocks; i++) { + free(x->blocklist[i]); + } free(x->blocklist); } x->num_blocks = 0; @@ -52,25 +88,26 @@ void empty_amqp_pool(amqp_pool_t *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 -ENOMEM; + return 0; } } else { void *newbl = realloc(x->blocklist, blocklistlength); if (newbl == NULL) { - return -ENOMEM; + return 0; } x->blocklist = newbl; } x->blocklist[x->num_blocks] = block; x->num_blocks++; - return 0; + return 1; } void *amqp_pool_alloc(amqp_pool_t *pool, size_t amount) { @@ -85,7 +122,8 @@ void *amqp_pool_alloc(amqp_pool_t *pool, size_t amount) { if (result == NULL) { return NULL; } - if (record_pool_block(&pool->large_blocks, result) != 0) { + if (!record_pool_block(&pool->large_blocks, result)) { + free(result); return NULL; } return result; @@ -106,7 +144,7 @@ void *amqp_pool_alloc(amqp_pool_t *pool, size_t amount) { if (pool->alloc_block == NULL) { return NULL; } - if (record_pool_block(&pool->pages, pool->alloc_block) != 0) { + if (!record_pool_block(&pool->pages, pool->alloc_block)) { return NULL; } pool->next_page = pool->pages.num_blocks; @@ -120,7 +158,8 @@ void *amqp_pool_alloc(amqp_pool_t *pool, size_t amount) { return pool->alloc_block; } -void amqp_pool_alloc_bytes(amqp_pool_t *pool, size_t amount, amqp_bytes_t *output) { +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); } @@ -128,7 +167,7 @@ void amqp_pool_alloc_bytes(amqp_pool_t *pool, size_t amount, amqp_bytes_t *outpu amqp_bytes_t amqp_cstring_bytes(char const *cstr) { amqp_bytes_t result; result.len = strlen(cstr); - result.bytes = (void *) cstr; + result.bytes = (void *)cstr; return result; } @@ -141,3 +180,63 @@ amqp_bytes_t amqp_bytes_malloc_dup(amqp_bytes_t src) { } 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; +} diff --git a/src/librabbitmq/amqp_openssl.c b/src/librabbitmq/amqp_openssl.c new file mode 100644 index 0000000..4915a6a --- /dev/null +++ b/src/librabbitmq/amqp_openssl.c @@ -0,0 +1,712 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + +void *amqp_ssl_socket_get_context(amqp_socket_t *base) { + if (base->klass != &amqp_ssl_socket_class) { + amqp_abort("<%p> is not of type amqp_ssl_socket_t", base); + } + return ((struct amqp_ssl_socket_t *)base)->ctx; +} + +int amqp_ssl_socket_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 | CONF_MFLAGS_IGNORE_MISSING_FILE) <= 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; +} diff --git a/src/librabbitmq/amqp_openssl_bio.c b/src/librabbitmq/amqp_openssl_bio.c new file mode 100644 index 0000000..3556d6f --- /dev/null +++ b/src/librabbitmq/amqp_openssl_bio.c @@ -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 +#include +#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__))) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#else +#include +#include +#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 +} diff --git a/src/librabbitmq/amqp_openssl_bio.h b/src/librabbitmq/amqp_openssl_bio.h new file mode 100644 index 0000000..ec09c5e --- /dev/null +++ b/src/librabbitmq/amqp_openssl_bio.h @@ -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 + +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 */ diff --git a/src/librabbitmq/amqp_openssl_hostname_validation.c b/src/librabbitmq/amqp_openssl_hostname_validation.c new file mode 100644 index 0000000..133d73c --- /dev/null +++ b/src/librabbitmq/amqp_openssl_hostname_validation.c @@ -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 +#include + +#include "amqp_hostcheck.h" +#include "amqp_openssl_bio.h" +#include "amqp_openssl_hostname_validation.h" + +#include + +#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; +} diff --git a/src/librabbitmq/amqp_openssl_hostname_validation.h b/src/librabbitmq/amqp_openssl_hostname_validation.h new file mode 100644 index 0000000..c994871 --- /dev/null +++ b/src/librabbitmq/amqp_openssl_hostname_validation.h @@ -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 + +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 diff --git a/src/librabbitmq/amqp_private.h b/src/librabbitmq/amqp_private.h index ce12f9d..e73776c 100644 --- a/src/librabbitmq/amqp_private.h +++ b/src/librabbitmq/amqp_private.h @@ -1,62 +1,172 @@ #ifndef librabbitmq_amqp_private_h #define librabbitmq_amqp_private_h -#ifdef __cplusplus -extern "C" { +/* + * ***** 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 -#include /* ntohl, htonl, ntohs, htons */ +#define AMQ_COPYRIGHT \ + "Copyright (c) 2007-2014 VMWare Inc, Tony Garnock-Jones," \ + " and Alan Antonuk." + +#include "amqp.h" +#include "amqp_framing.h" +#include + +#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 +#else +#include +#include +#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: + * Connection states: XXX FIX THIS * - * - CONNECTION_STATE_IDLE: initial state, and entered again after - * each frame is completed. Means that no bytes of the next frame - * have been seen yet. Connections may only be reconfigured, and the + * - 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_WAITING_FOR_HEADER: Some bytes of an incoming - * frame have been seen, but not a complete frame header's worth. - * - * - CONNECTION_STATE_WAITING_FOR_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. + * - CONNECTION_STATE_HEADER: Some bytes of an incoming frame have + * been seen, but not a complete frame header's worth. * - * - CONNECTION_STATE_WAITING_FOR_PROTOCOL_HEADER: The beginning of a - * protocol version header has been seen, but the full eight bytes - * hasn't yet been received. When it is completed, it will be + * - 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_WAITING_FOR_HEADER, - CONNECTION_STATE_WAITING_FOR_BODY, - CONNECTION_STATE_WAITING_FOR_PROTOCOL_HEADER + 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_t frame_pool; - amqp_pool_t decoding_pool; + 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; @@ -64,7 +174,8 @@ struct amqp_connection_state_t_ { amqp_bytes_t outbound_buffer; - int sockfd; + amqp_socket_t *socket; + amqp_bytes_t sock_inbound_buffer; size_t sock_inbound_offset; size_t sock_inbound_limit; @@ -72,74 +183,192 @@ struct amqp_connection_state_t_ { amqp_link_t *first_queued_frame; amqp_link_t *last_queued_frame; - amqp_basic_return_fn_t basic_return_callback; - void *basic_return_callback_data; + 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; }; -#define CHECK_LIMIT(b, o, l, v) ({ if ((o + l) > (b).len) { return -EFAULT; } (v); }) -#define BUF_AT(b, o) (&(((uint8_t *) (b).bytes)[o])) - -#define D_8(b, o) CHECK_LIMIT(b, o, 1, * (uint8_t *) BUF_AT(b, o)) -#define D_16(b, o) CHECK_LIMIT(b, o, 2, ({uint16_t v; memcpy(&v, BUF_AT(b, o), 2); ntohs(v);})) -#define D_32(b, o) CHECK_LIMIT(b, o, 4, ({uint32_t v; memcpy(&v, BUF_AT(b, o), 4); ntohl(v);})) -#define D_64(b, o) ({ \ - uint64_t hi = D_32(b, o); \ - uint64_t lo = D_32(b, o + 4); \ - hi << 32 | lo; \ -}) - -#define D_BYTES(b, o, l) CHECK_LIMIT(b, o, l, BUF_AT(b, o)) - -#define E_8(b, o, v) CHECK_LIMIT(b, o, 1, * (uint8_t *) BUF_AT(b, o) = (v)) -#define E_16(b, o, v) CHECK_LIMIT(b, o, 2, ({uint16_t vv = htons(v); memcpy(BUF_AT(b, o), &vv, 2);})) -#define E_32(b, o, v) CHECK_LIMIT(b, o, 4, ({uint32_t vv = htonl(v); memcpy(BUF_AT(b, o), &vv, 4);})) -#define E_64(b, o, v) ({ \ - E_32(b, o, (uint32_t) (((uint64_t) v) >> 32)); \ - E_32(b, o + 4, (uint32_t) (((uint64_t) v) & 0xFFFFFFFF)); \ - }) - -#define E_BYTES(b, o, l, v) CHECK_LIMIT(b, o, l, memcpy(BUF_AT(b, o), (v), (l))) - -extern int amqp_decode_table(amqp_bytes_t encoded, - amqp_pool_t *pool, - amqp_table_t *output, - int *offsetptr); - -extern int amqp_encode_table(amqp_bytes_t encoded, - amqp_table_t *input, - int *offsetptr); - -#define amqp_assert(condition, ...) \ - ({ \ - if (!(condition)) { \ - fprintf(stderr, __VA_ARGS__); \ - fputc('\n', stderr); \ - abort(); \ - } \ - }) - -#define AMQP_CHECK_RESULT(expr) \ - ({ \ - int _result = (expr); \ - if (_result < 0) return _result; \ - _result; \ - }) - -#define AMQP_CHECK_EOF_RESULT(expr) \ - ({ \ - int _result = (expr); \ - if (_result <= 0) return _result; \ - _result; \ - }) - -#ifndef NDEBUG -extern void amqp_dump(void const *buffer, size_t len); -#else -#define amqp_dump(buffer, len) ((void) 0) -#endif +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); -#ifdef __cplusplus +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; } -#endif +int amqp_send_frame_inner(amqp_connection_state_t state, + const amqp_frame_t *frame, int flags, + amqp_time_t deadline); #endif diff --git a/src/librabbitmq/amqp_socket.c b/src/librabbitmq/amqp_socket.c index 17c7a74..cfb18ea 100644 --- a/src/librabbitmq/amqp_socket.c +++ b/src/librabbitmq/amqp_socket.c @@ -1,125 +1,607 @@ -#include +/* + * ***** 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_socket.h" +#include "amqp_table.h" +#include "amqp_time.h" + +#include +#include +#include +#include #include +#include #include -#include -#include -#include -#include -#include -#include "amqp.h" -#include "amqp_framing.h" -#include "amqp_private.h" +#include +#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__))) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#else #include +/* On older BSD types.h must come before net includes */ +#include +#include +#ifdef HAVE_SELECT +#include +#endif +#include +#include +#include #include +#ifdef HAVE_POLL +#include +#endif #include -#include -#include -#include +#endif -#include +static int amqp_id_in_reply_list(amqp_method_number_t expected, + amqp_method_number_t *list); -int amqp_open_socket(char const *hostname, - int portnumber, struct timeval *timeout) -{ - int result = -1; - int sockfd; - int flags; - struct sockaddr_in addr; - struct hostent *he; +static int amqp_os_socket_init(void) { +#ifdef _WIN32 + static int called_wsastartup = 0; + if (!called_wsastartup) { + WSADATA data; + int res = WSAStartup(0x0202, &data); + if (res) { + return AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR; + } - he = gethostbyname(hostname); - if (he == NULL) { - return -ENOENT; + called_wsastartup = 1; } + return AMQP_STATUS_OK; + +#else + return AMQP_STATUS_OK; +#endif +} + +int amqp_os_socket_error(void) { +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif +} + +int amqp_os_socket_close(int sockfd) { +#ifdef _WIN32 + return closesocket(sockfd); +#else + return close(sockfd); +#endif +} + +ssize_t amqp_socket_send(amqp_socket_t *self, const void *buf, size_t len, + int flags) { + assert(self); + assert(self->klass->send); + return self->klass->send(self, buf, len, flags); +} + +ssize_t amqp_socket_recv(amqp_socket_t *self, void *buf, size_t len, + int flags) { + assert(self); + assert(self->klass->recv); + return self->klass->recv(self, buf, len, flags); +} + +int amqp_socket_open(amqp_socket_t *self, const char *host, int port) { + assert(self); + assert(self->klass->open); + return self->klass->open(self, host, port, NULL); +} - addr.sin_family = AF_INET; - addr.sin_port = htons(portnumber); - addr.sin_addr.s_addr = * (uint32_t *) he->h_addr_list[0]; +int amqp_socket_open_noblock(amqp_socket_t *self, const char *host, int port, + struct timeval *timeout) { + assert(self); + assert(self->klass->open); + return self->klass->open(self, host, port, timeout); +} - sockfd = socket(PF_INET, SOCK_STREAM, 0); - if(((flags = fcntl(sockfd, F_GETFL, 0)) == -1) || - (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1)) { - result = -errno; - close(sockfd); - return -1; +int amqp_socket_close(amqp_socket_t *self, amqp_socket_close_enum force) { + assert(self); + assert(self->klass->close); + return self->klass->close(self, force); +} + +void amqp_socket_delete(amqp_socket_t *self) { + if (self) { + assert(self->klass->delete); + self->klass->delete (self); } +} - if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - result = -errno; - if(errno == EINPROGRESS) { - int aerrno, prv; - socklen_t aerrno_len = sizeof(aerrno); - struct pollfd pfd; +int amqp_socket_get_sockfd(amqp_socket_t *self) { + assert(self); + assert(self->klass->get_sockfd); + return self->klass->get_sockfd(self); +} + +int amqp_poll(int fd, int event, amqp_time_t deadline) { +#ifdef HAVE_POLL + struct pollfd pfd; + int res; + int timeout_ms; + + /* Function should only ever be called with one of these two */ + assert(event == AMQP_SF_POLLIN || event == AMQP_SF_POLLOUT); - pfd.fd = sockfd; +start_poll: + pfd.fd = fd; + switch (event) { + case AMQP_SF_POLLIN: + pfd.events = POLLIN; + break; + case AMQP_SF_POLLOUT: pfd.events = POLLOUT; - prv = poll(&pfd, 1, timeout?(timeout->tv_sec*1000 + timeout->tv_usec/1000):-1); - if(prv == 1) { - if(getsockopt(sockfd,SOL_SOCKET,SO_ERROR, &aerrno, &aerrno_len) == 0) { - if(aerrno == 0) goto good; - } - else - goto good; - result = -aerrno; - } - else result = -ETIMEDOUT; + break; + } + + timeout_ms = amqp_time_ms_until(deadline); + if (-1 > timeout_ms) { + return timeout_ms; + } + + res = poll(&pfd, 1, timeout_ms); + + if (0 < res) { + /* TODO: optimize this a bit by returning the AMQP_STATUS_SOCKET_ERROR or + * equivalent when pdf.revent is POLLHUP or POLLERR, so an extra syscall + * doesn't need to be made. */ + return AMQP_STATUS_OK; + } else if (0 == res) { + return AMQP_STATUS_TIMEOUT; + } else { + switch (amqp_os_socket_error()) { + case EINTR: + goto start_poll; + default: + return AMQP_STATUS_SOCKET_ERROR; } - close(sockfd); - return result; } -good: - if(((flags = fcntl(sockfd, F_GETFL, 0)) == -1) || - (fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK) == -1)) { - result = -errno; - close(sockfd); - return result; + return AMQP_STATUS_OK; +#elif defined(HAVE_SELECT) + fd_set fds; + fd_set exceptfds; + fd_set *exceptfdsp; + int res; + struct timeval tv; + struct timeval *tvp; + + assert((0 != (event & AMQP_SF_POLLIN)) || (0 != (event & AMQP_SF_POLLOUT))); +#ifndef _WIN32 + /* On Win32 connect() failure is indicated through the exceptfds, it does not + * make any sense to allow POLLERR on any other platform or condition */ + assert(0 == (event & AMQP_SF_POLLERR)); +#endif + +start_select: + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if (event & AMQP_SF_POLLERR) { + FD_ZERO(&exceptfds); + FD_SET(fd, &exceptfds); + exceptfdsp = &exceptfds; + } else { + exceptfdsp = NULL; } - if(timeout) { - setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(*timeout)); - setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, timeout, sizeof(*timeout)); + + res = amqp_time_tv_until(deadline, &tv, &tvp); + if (res != AMQP_STATUS_OK) { + return res; } + + if (event & AMQP_SF_POLLIN) { + res = select(fd + 1, &fds, NULL, exceptfdsp, tvp); + } else if (event & AMQP_SF_POLLOUT) { + res = select(fd + 1, NULL, &fds, exceptfdsp, tvp); + } + + if (0 < res) { + return AMQP_STATUS_OK; + } else if (0 == res) { + return AMQP_STATUS_TIMEOUT; + } else { + switch (amqp_os_socket_error()) { + case EINTR: + goto start_select; + default: + return AMQP_STATUS_SOCKET_ERROR; + } + } +#else +#error "poll() or select() is needed to compile rabbitmq-c" +#endif +} + +static ssize_t do_poll(amqp_connection_state_t state, ssize_t res, + amqp_time_t deadline) { + int fd = amqp_get_sockfd(state); + if (-1 == fd) { + return AMQP_STATUS_SOCKET_CLOSED; + } + switch (res) { + case AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD: + res = amqp_poll(fd, AMQP_SF_POLLIN, deadline); + break; + case AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE: + res = amqp_poll(fd, AMQP_SF_POLLOUT, deadline); + break; + } + return res; +} + +ssize_t amqp_try_send(amqp_connection_state_t state, const void *buf, + size_t len, amqp_time_t deadline, int flags) { + ssize_t res; + void *buf_left = (void *)buf; + /* Assume that len is not going to be larger than ssize_t can hold. */ + ssize_t len_left = (size_t)len; + +start_send: + res = amqp_socket_send(state->socket, buf_left, len_left, flags); + + if (res > 0) { + len_left -= res; + buf_left = (char *)buf_left + res; + if (0 == len_left) { + return (ssize_t)len; + } + goto start_send; + } + res = do_poll(state, res, deadline); + if (AMQP_STATUS_OK == res) { + goto start_send; + } + if (AMQP_STATUS_TIMEOUT == res) { + return (ssize_t)len - len_left; + } + return res; +} + +int amqp_open_socket(char const *hostname, int portnumber) { + return amqp_open_socket_inner(hostname, portnumber, amqp_time_infinite()); +} + +int amqp_open_socket_noblock(char const *hostname, int portnumber, + struct timeval *timeout) { + amqp_time_t deadline; + int res = amqp_time_from_now(&deadline, timeout); + if (AMQP_STATUS_OK != res) { + return res; + } + return amqp_open_socket_inner(hostname, portnumber, deadline); +} + +#ifdef _WIN32 +static int connect_socket(struct addrinfo *addr, amqp_time_t deadline) { + int one = 1; + SOCKET sockfd; + int last_error; + + /* + * This cast is to squash warnings on Win64, see: + * http://stackoverflow.com/questions/1953639/is-it-safe-to-cast-socket-to-int-under-win64 + */ + + sockfd = (int)socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if (INVALID_SOCKET == sockfd) { + return AMQP_STATUS_SOCKET_ERROR; + } + + /* Set the socket to be non-blocking */ + if (SOCKET_ERROR == ioctlsocket(sockfd, FIONBIO, &one)) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + /* Disable nagle */ + if (SOCKET_ERROR == setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, + (const char *)&one, sizeof(one))) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + /* Enable TCP keepalives */ + if (SOCKET_ERROR == setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, + (const char *)&one, sizeof(one))) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + if (SOCKET_ERROR != connect(sockfd, addr->ai_addr, (int)addr->ai_addrlen)) { + return (int)sockfd; + } + + if (WSAEWOULDBLOCK != WSAGetLastError()) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + last_error = + amqp_poll((int)sockfd, AMQP_SF_POLLOUT | AMQP_SF_POLLERR, deadline); + if (AMQP_STATUS_OK != last_error) { + goto err; + } + + { + int result; + int result_len = sizeof(result); + + if (SOCKET_ERROR == getsockopt(sockfd, SOL_SOCKET, SO_ERROR, + (char *)&result, &result_len) || + result != 0) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + } + + return (int)sockfd; + +err: + closesocket(sockfd); + return last_error; +} +#else +static int connect_socket(struct addrinfo *addr, amqp_time_t deadline) { + int one = 1; + int sockfd; + int flags; + int last_error; + + sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if (-1 == sockfd) { + return AMQP_STATUS_SOCKET_ERROR; + } + + /* Enable CLOEXEC on socket */ + flags = fcntl(sockfd, F_GETFD); + if (flags == -1 || fcntl(sockfd, F_SETFD, (long)(flags | FD_CLOEXEC)) == -1) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + /* Set the socket as non-blocking */ + flags = fcntl(sockfd, F_GETFL); + if (flags == -1 || fcntl(sockfd, F_SETFL, (long)(flags | O_NONBLOCK)) == -1) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + +#ifdef SO_NOSIGPIPE + /* Turn off SIGPIPE on platforms that support it, BSD, MacOSX */ + if (0 != setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one))) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } +#endif /* SO_NOSIGPIPE */ + + /* Disable nagle */ + if (0 != setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one))) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + /* Enable TCP keepalives */ + if (0 != setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one))) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + if (0 == connect(sockfd, addr->ai_addr, addr->ai_addrlen)) { + return sockfd; + } + + if (EINPROGRESS != errno) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + last_error = amqp_poll(sockfd, AMQP_SF_POLLOUT, deadline); + if (AMQP_STATUS_OK != last_error) { + goto err; + } + + { + int result; + socklen_t result_len = sizeof(result); + + if (-1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &result, &result_len) || + result != 0) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + } + return sockfd; + +err: + close(sockfd); + return last_error; } +#endif + +int amqp_open_socket_inner(char const *hostname, int portnumber, + amqp_time_t deadline) { + struct addrinfo hint; + struct addrinfo *address_list; + struct addrinfo *addr; + char portnumber_string[33]; + int sockfd = -1; + int last_error; + + last_error = amqp_os_socket_init(); + if (AMQP_STATUS_OK != last_error) { + return last_error; + } + + memset(&hint, 0, sizeof(hint)); + hint.ai_family = PF_UNSPEC; /* PF_INET or PF_INET6 */ + hint.ai_socktype = SOCK_STREAM; + hint.ai_protocol = IPPROTO_TCP; + + (void)sprintf(portnumber_string, "%d", portnumber); + + last_error = getaddrinfo(hostname, portnumber_string, &hint, &address_list); + if (0 != last_error) { + return AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED; + } + + for (addr = address_list; addr; addr = addr->ai_next) { + sockfd = connect_socket(addr, deadline); + + if (sockfd >= 0) { + last_error = AMQP_STATUS_OK; + break; + } else if (sockfd == AMQP_STATUS_TIMEOUT) { + last_error = sockfd; + break; + } + } -static char *header() { - static char header[8]; - header[0] = 'A'; - header[1] = 'M'; - header[2] = 'Q'; - header[3] = 'P'; - header[4] = 1; - header[5] = 1; - header[6] = AMQP_PROTOCOL_VERSION_MAJOR; - header[7] = AMQP_PROTOCOL_VERSION_MINOR; - return header; + freeaddrinfo(address_list); + if (last_error != AMQP_STATUS_OK || sockfd == -1) { + return last_error; + } + return sockfd; } -int amqp_send_header(amqp_connection_state_t state) { - return write(state->sockfd, header(), 8); +static int send_header_inner(amqp_connection_state_t state, + amqp_time_t deadline) { + ssize_t res; + static const uint8_t header[8] = {'A', + 'M', + 'Q', + 'P', + 0, + AMQP_PROTOCOL_VERSION_MAJOR, + AMQP_PROTOCOL_VERSION_MINOR, + AMQP_PROTOCOL_VERSION_REVISION}; + res = amqp_try_send(state, header, sizeof(header), deadline, AMQP_SF_NONE); + if (sizeof(header) == res) { + return AMQP_STATUS_OK; + } + return (int)res; } -int amqp_send_header_to(amqp_connection_state_t state, - amqp_output_fn_t fn, - void *context) -{ - return fn(context, header(), 8); +int amqp_send_header(amqp_connection_state_t state) { + return send_header_inner(state, amqp_time_infinite()); } static amqp_bytes_t sasl_method_name(amqp_sasl_method_enum method) { + amqp_bytes_t res; + switch (method) { - case AMQP_SASL_METHOD_PLAIN: return (amqp_bytes_t) {.len = 5, .bytes = "PLAIN"}; + case AMQP_SASL_METHOD_PLAIN: + res = amqp_cstring_bytes("PLAIN"); + break; + case AMQP_SASL_METHOD_EXTERNAL: + res = amqp_cstring_bytes("EXTERNAL"); + break; + default: - amqp_assert(0, "Invalid SASL method: %d", (int) method); + amqp_abort("Invalid SASL method: %d", (int)method); + } + + return res; +} + +static int bytes_equal(amqp_bytes_t l, amqp_bytes_t r) { + if (l.len == r.len) { + if (l.bytes && r.bytes) { + if (0 == memcmp(l.bytes, r.bytes, l.len)) { + return 1; + } + } } - abort(); /* unreachable */ + return 0; +} + +int sasl_mechanism_in_list(amqp_bytes_t mechanisms, + amqp_sasl_method_enum method) { + amqp_bytes_t mechanism; + amqp_bytes_t supported_mechanism; + uint8_t *start; + uint8_t *end; + uint8_t *current; + + assert(NULL != mechanisms.bytes); + + mechanism = sasl_method_name(method); + + start = (uint8_t *)mechanisms.bytes; + current = start; + end = start + mechanisms.len; + + for (; current != end; start = current + 1) { + /* HACK: SASL states that we should be parsing this string as a UTF-8 + * string, which we're plainly not doing here. At this point its not worth + * dragging an entire UTF-8 parser for this one case, and this should work + * most of the time */ + current = memchr(start, ' ', end - start); + if (NULL == current) { + current = end; + } + supported_mechanism.bytes = start; + supported_mechanism.len = current - start; + if (bytes_equal(mechanism, supported_mechanism)) { + return 1; + } + } + + return 0; } static amqp_bytes_t sasl_response(amqp_pool_t *pool, - amqp_sasl_method_enum method, - va_list args) -{ + amqp_sasl_method_enum method, va_list args) { amqp_bytes_t response; switch (method) { @@ -128,15 +610,38 @@ static amqp_bytes_t sasl_response(amqp_pool_t *pool, size_t username_len = strlen(username); char *password = va_arg(args, char *); size_t password_len = strlen(password); - amqp_pool_alloc_bytes(pool, strlen(username) + strlen(password) + 2, &response); - *BUF_AT(response, 0) = 0; - memcpy(((char *) response.bytes) + 1, username, username_len); - *BUF_AT(response, username_len + 1) = 0; - memcpy(((char *) response.bytes) + username_len + 2, password, password_len); + char *response_buf; + + amqp_pool_alloc_bytes(pool, strlen(username) + strlen(password) + 2, + &response); + if (response.bytes == NULL) + /* We never request a zero-length block, because of the +2 + above, so a NULL here really is ENOMEM. */ + { + return response; + } + + response_buf = response.bytes; + response_buf[0] = 0; + memcpy(response_buf + 1, username, username_len); + response_buf[username_len + 1] = 0; + memcpy(response_buf + username_len + 2, password, password_len); + break; + } + case AMQP_SASL_METHOD_EXTERNAL: { + char *identity = va_arg(args, char *); + size_t identity_len = strlen(identity); + + amqp_pool_alloc_bytes(pool, identity_len, &response); + if (response.bytes == NULL) { + return response; + } + + memcpy(response.bytes, identity, identity_len); break; } default: - amqp_assert(0, "Invalid SASL method: %d", (int) method); + amqp_abort("Invalid SASL method: %d", (int)method); } return response; @@ -154,116 +659,385 @@ amqp_boolean_t amqp_data_in_buffer(amqp_connection_state_t state) { return (state->sock_inbound_offset < state->sock_inbound_limit); } +static int consume_one_frame(amqp_connection_state_t state, + amqp_frame_t *decoded_frame) { + int res; + + amqp_bytes_t buffer; + buffer.len = state->sock_inbound_limit - state->sock_inbound_offset; + buffer.bytes = + ((char *)state->sock_inbound_buffer.bytes) + state->sock_inbound_offset; + + res = amqp_handle_input(state, buffer, decoded_frame); + if (res < 0) { + return res; + } + + state->sock_inbound_offset += res; + + return AMQP_STATUS_OK; +} + +static int recv_with_timeout(amqp_connection_state_t state, + amqp_time_t timeout) { + ssize_t res; + int fd; + +start_recv: + res = amqp_socket_recv(state->socket, state->sock_inbound_buffer.bytes, + state->sock_inbound_buffer.len, 0); + + if (res < 0) { + fd = amqp_get_sockfd(state); + if (-1 == fd) { + return AMQP_STATUS_CONNECTION_CLOSED; + } + switch (res) { + default: + return (int)res; + case AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD: + res = amqp_poll(fd, AMQP_SF_POLLIN, timeout); + break; + case AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE: + res = amqp_poll(fd, AMQP_SF_POLLOUT, timeout); + break; + } + if (AMQP_STATUS_OK == res) { + goto start_recv; + } + return (int)res; + } + + state->sock_inbound_limit = res; + state->sock_inbound_offset = 0; + + res = amqp_time_s_from_now(&state->next_recv_heartbeat, + amqp_heartbeat_recv(state)); + if (AMQP_STATUS_OK != res) { + return (int)res; + } + return AMQP_STATUS_OK; +} + +int amqp_try_recv(amqp_connection_state_t state) { + amqp_time_t timeout; + + while (amqp_data_in_buffer(state)) { + amqp_frame_t frame; + int res = consume_one_frame(state, &frame); + + if (AMQP_STATUS_OK != res) { + return res; + } + + if (frame.frame_type != 0) { + amqp_pool_t *channel_pool; + amqp_frame_t *frame_copy; + amqp_link_t *link; + + channel_pool = amqp_get_or_create_channel_pool(state, frame.channel); + if (NULL == channel_pool) { + return AMQP_STATUS_NO_MEMORY; + } + + frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t)); + link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t)); + + if (frame_copy == NULL || link == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + + *frame_copy = frame; + + link->next = NULL; + link->data = frame_copy; + + if (state->last_queued_frame == NULL) { + state->first_queued_frame = link; + } else { + state->last_queued_frame->next = link; + } + state->last_queued_frame = link; + } + } + timeout = amqp_time_immediate(); + + return recv_with_timeout(state, timeout); +} + static int wait_frame_inner(amqp_connection_state_t state, - amqp_frame_t *decoded_frame) -{ - while (1) { - int result; + amqp_frame_t *decoded_frame, + amqp_time_t timeout_deadline) { + amqp_time_t deadline; + int res; + for (;;) { while (amqp_data_in_buffer(state)) { - amqp_bytes_t buffer; - buffer.len = state->sock_inbound_limit - state->sock_inbound_offset; - buffer.bytes = ((char *) state->sock_inbound_buffer.bytes) + state->sock_inbound_offset; - AMQP_CHECK_RESULT((result = amqp_handle_input(state, buffer, decoded_frame))); - state->sock_inbound_offset += result; + res = consume_one_frame(state, decoded_frame); + + if (AMQP_STATUS_OK != res) { + return res; + } + + if (AMQP_FRAME_HEARTBEAT == decoded_frame->frame_type) { + amqp_maybe_release_buffers_on_channel(state, 0); + continue; + } if (decoded_frame->frame_type != 0) { - /* Complete frame was read. Return it. */ - return 1; + /* Complete frame was read. Return it. */ + return AMQP_STATUS_OK; + } + } + + beginrecv: + res = amqp_time_has_past(state->next_send_heartbeat); + if (AMQP_STATUS_TIMER_FAILURE == res) { + return res; + } else if (AMQP_STATUS_TIMEOUT == res) { + amqp_frame_t heartbeat; + heartbeat.channel = 0; + heartbeat.frame_type = AMQP_FRAME_HEARTBEAT; + + res = amqp_send_frame(state, &heartbeat); + if (AMQP_STATUS_OK != res) { + return res; + } + } + deadline = amqp_time_first(timeout_deadline, + amqp_time_first(state->next_recv_heartbeat, + state->next_send_heartbeat)); + + /* TODO this needs to wait for a _frame_ and not anything written from the + * socket */ + res = recv_with_timeout(state, deadline); + + if (AMQP_STATUS_TIMEOUT == res) { + if (amqp_time_equal(deadline, state->next_recv_heartbeat)) { + amqp_socket_close(state->socket, AMQP_SC_FORCE); + return AMQP_STATUS_HEARTBEAT_TIMEOUT; + } else if (amqp_time_equal(deadline, timeout_deadline)) { + return AMQP_STATUS_TIMEOUT; + } else if (amqp_time_equal(deadline, state->next_send_heartbeat)) { + /* send heartbeat happens before we do recv_with_timeout */ + goto beginrecv; + } else { + amqp_abort("Internal error: unable to determine timeout reason"); + } + } else if (AMQP_STATUS_OK != res) { + return res; + } + } +} + +static amqp_link_t *amqp_create_link_for_frame(amqp_connection_state_t state, + amqp_frame_t *frame) { + amqp_link_t *link; + amqp_frame_t *frame_copy; + + amqp_pool_t *channel_pool = + amqp_get_or_create_channel_pool(state, frame->channel); + + if (NULL == channel_pool) { + return NULL; + } + + link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t)); + frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t)); + + if (NULL == link || NULL == frame_copy) { + return NULL; + } + + *frame_copy = *frame; + link->data = frame_copy; + + return link; +} + +int amqp_queue_frame(amqp_connection_state_t state, amqp_frame_t *frame) { + amqp_link_t *link = amqp_create_link_for_frame(state, frame); + if (NULL == link) { + return AMQP_STATUS_NO_MEMORY; + } + + if (NULL == state->first_queued_frame) { + state->first_queued_frame = link; + } else { + state->last_queued_frame->next = link; + } + + link->next = NULL; + state->last_queued_frame = link; + + return AMQP_STATUS_OK; +} + +int amqp_put_back_frame(amqp_connection_state_t state, amqp_frame_t *frame) { + amqp_link_t *link = amqp_create_link_for_frame(state, frame); + if (NULL == link) { + return AMQP_STATUS_NO_MEMORY; + } + + if (NULL == state->first_queued_frame) { + state->first_queued_frame = link; + state->last_queued_frame = link; + link->next = NULL; + } else { + link->next = state->first_queued_frame; + state->first_queued_frame = link; + } + + return AMQP_STATUS_OK; +} + +int amqp_simple_wait_frame_on_channel(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_frame_t *decoded_frame) { + amqp_frame_t *frame_ptr; + amqp_link_t *cur; + int res; + + for (cur = state->first_queued_frame; NULL != cur; cur = cur->next) { + frame_ptr = cur->data; + + if (channel == frame_ptr->channel) { + state->first_queued_frame = cur->next; + if (NULL == state->first_queued_frame) { + state->last_queued_frame = NULL; } - /* Incomplete or ignored frame. Keep processing input. */ - assert(result != 0); - } + *decoded_frame = *frame_ptr; - result = read(state->sockfd, - state->sock_inbound_buffer.bytes, - state->sock_inbound_buffer.len); - if (result < 0) { - return -errno; + return AMQP_STATUS_OK; } - if (result == 0) { - /* EOF. */ - return 0; + } + + for (;;) { + res = wait_frame_inner(state, decoded_frame, amqp_time_infinite()); + + if (AMQP_STATUS_OK != res) { + return res; } - state->sock_inbound_limit = result; - state->sock_inbound_offset = 0; + if (channel == decoded_frame->channel) { + return AMQP_STATUS_OK; + } else { + res = amqp_queue_frame(state, decoded_frame); + if (res != AMQP_STATUS_OK) { + return res; + } + } } } int amqp_simple_wait_frame(amqp_connection_state_t state, - amqp_frame_t *decoded_frame) -{ + amqp_frame_t *decoded_frame) { + return amqp_simple_wait_frame_noblock(state, decoded_frame, NULL); +} + +int amqp_simple_wait_frame_noblock(amqp_connection_state_t state, + amqp_frame_t *decoded_frame, + struct timeval *timeout) { + amqp_time_t deadline; + + int res = amqp_time_from_now(&deadline, timeout); + if (AMQP_STATUS_OK != res) { + return res; + } + if (state->first_queued_frame != NULL) { - amqp_frame_t *f = (amqp_frame_t *) state->first_queued_frame->data; + amqp_frame_t *f = (amqp_frame_t *)state->first_queued_frame->data; state->first_queued_frame = state->first_queued_frame->next; if (state->first_queued_frame == NULL) { state->last_queued_frame = NULL; } *decoded_frame = *f; - return 1; + return AMQP_STATUS_OK; } else { - return wait_frame_inner(state, decoded_frame); + return wait_frame_inner(state, decoded_frame, deadline); } } -int amqp_simple_wait_method(amqp_connection_state_t state, - amqp_channel_t expected_channel, - amqp_method_number_t expected_method, - amqp_method_t *output) -{ +static int amqp_simple_wait_method_list(amqp_connection_state_t state, + amqp_channel_t expected_channel, + amqp_method_number_t *expected_methods, + amqp_time_t deadline, + amqp_method_t *output) { amqp_frame_t frame; + struct timeval tv; + struct timeval *tvp; + + int res = amqp_time_tv_until(deadline, &tv, &tvp); + if (res != AMQP_STATUS_OK) { + return res; + } + + res = amqp_simple_wait_frame_noblock(state, &frame, tvp); + if (AMQP_STATUS_OK != res) { + return res; + } - AMQP_CHECK_EOF_RESULT(amqp_simple_wait_frame(state, &frame)); - amqp_assert(frame.channel == expected_channel, - "Expected 0x%08X method frame on channel %d, got frame on channel %d", - expected_method, - expected_channel, - frame.channel); - amqp_assert(frame.frame_type == AMQP_FRAME_METHOD, - "Expected 0x%08X method frame on channel %d, got frame type %d", - expected_method, - expected_channel, - frame.frame_type); - amqp_assert(frame.payload.method.id == expected_method, - "Expected method ID 0x%08X on channel %d, got ID 0x%08X", - expected_method, - expected_channel, - frame.payload.method.id); + if (AMQP_FRAME_METHOD != frame.frame_type || + expected_channel != frame.channel || + !amqp_id_in_reply_list(frame.payload.method.id, expected_methods)) { + return AMQP_STATUS_WRONG_METHOD; + } *output = frame.payload.method; - return 1; + return AMQP_STATUS_OK; +} + +static int simple_wait_method_inner(amqp_connection_state_t state, + amqp_channel_t expected_channel, + amqp_method_number_t expected_method, + amqp_time_t deadline, + amqp_method_t *output) { + amqp_method_number_t expected_methods[] = {expected_method, 0}; + return amqp_simple_wait_method_list(state, expected_channel, expected_methods, + deadline, output); +} + +int amqp_simple_wait_method(amqp_connection_state_t state, + amqp_channel_t expected_channel, + amqp_method_number_t expected_method, + amqp_method_t *output) { + return simple_wait_method_inner(state, expected_channel, expected_method, + amqp_time_infinite(), output); } -int amqp_send_method(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_method_number_t id, - void *decoded) -{ +int amqp_send_method(amqp_connection_state_t state, amqp_channel_t channel, + amqp_method_number_t id, void *decoded) { + return amqp_send_method_inner(state, channel, id, decoded, AMQP_SF_NONE, + amqp_time_infinite()); +} + +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) { amqp_frame_t frame; frame.frame_type = AMQP_FRAME_METHOD; frame.channel = channel; frame.payload.method.id = id; frame.payload.method.decoded = decoded; - return amqp_send_frame(state, &frame); + return amqp_send_frame_inner(state, &frame, flags, deadline); } -static int amqp_id_in_reply_list( amqp_method_number_t expected, amqp_method_number_t *list ) -{ - while ( *list != 0 ) { - if ( *list == expected ) return 1; +static int amqp_id_in_reply_list(amqp_method_number_t expected, + amqp_method_number_t *list) { + while (*list != 0) { + if (*list == expected) { + return 1; + } list++; } return 0; } -amqp_rpc_reply_t amqp_simple_rpc(amqp_connection_state_t state, - amqp_channel_t channel, - amqp_method_number_t request_id, - amqp_method_number_t *expected_reply_ids, - void *decoded_request_method) -{ +static amqp_rpc_reply_t simple_rpc_inner( + amqp_connection_state_t state, amqp_channel_t channel, + amqp_method_number_t request_id, amqp_method_number_t *expected_reply_ids, + void *decoded_request_method, amqp_time_t deadline) { int status; amqp_rpc_reply_t result; @@ -271,20 +1045,19 @@ amqp_rpc_reply_t amqp_simple_rpc(amqp_connection_state_t state, status = amqp_send_method(state, channel, request_id, decoded_request_method); if (status < 0) { - result.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; - result.library_errno = -status; - return result; + return amqp_rpc_reply_error(status); } { amqp_frame_t frame; retry: - status = wait_frame_inner(state, &frame); - if (status <= 0) { - result.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; - result.library_errno = -status; - return result; + status = wait_frame_inner(state, &frame, deadline); + if (status < 0) { + if (status == AMQP_STATUS_TIMEOUT) { + amqp_socket_close(state->socket, AMQP_SC_FORCE); + } + return amqp_rpc_reply_error(status); } /* @@ -295,16 +1068,28 @@ amqp_rpc_reply_t amqp_simple_rpc(amqp_connection_state_t state, * - on the channel we want, and a channel.close frame, or * - on channel zero, and a connection.close frame. */ - if (!( (frame.frame_type == AMQP_FRAME_METHOD) && - ( ((frame.channel == channel) && - ((amqp_id_in_reply_list(frame.payload.method.id, expected_reply_ids)) || - (frame.payload.method.id == AMQP_CHANNEL_CLOSE_METHOD))) - || - ((frame.channel == 0) && - (frame.payload.method.id == AMQP_CONNECTION_CLOSE_METHOD)) ) )) - { - amqp_frame_t *frame_copy = amqp_pool_alloc(&state->decoding_pool, sizeof(amqp_frame_t)); - amqp_link_t *link = amqp_pool_alloc(&state->decoding_pool, sizeof(amqp_link_t)); + if (!((frame.frame_type == AMQP_FRAME_METHOD) && + (((frame.channel == channel) && + (amqp_id_in_reply_list(frame.payload.method.id, + expected_reply_ids) || + (frame.payload.method.id == AMQP_CHANNEL_CLOSE_METHOD))) || + ((frame.channel == 0) && + (frame.payload.method.id == AMQP_CONNECTION_CLOSE_METHOD))))) { + amqp_pool_t *channel_pool; + amqp_frame_t *frame_copy; + amqp_link_t *link; + + channel_pool = amqp_get_or_create_channel_pool(state, frame.channel); + if (NULL == channel_pool) { + return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY); + } + + frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t)); + link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t)); + + if (frame_copy == NULL || link == NULL) { + return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY); + } *frame_copy = frame; @@ -312,153 +1097,396 @@ amqp_rpc_reply_t amqp_simple_rpc(amqp_connection_state_t state, link->data = frame_copy; if (state->last_queued_frame == NULL) { - state->first_queued_frame = link; + state->first_queued_frame = link; } else { - state->last_queued_frame->next = link; + state->last_queued_frame->next = link; } state->last_queued_frame = link; goto retry; } - result.reply_type = (amqp_id_in_reply_list(frame.payload.method.id, expected_reply_ids)) - ? AMQP_RESPONSE_NORMAL - : AMQP_RESPONSE_SERVER_EXCEPTION; + result.reply_type = + (amqp_id_in_reply_list(frame.payload.method.id, expected_reply_ids)) + ? AMQP_RESPONSE_NORMAL + : AMQP_RESPONSE_SERVER_EXCEPTION; result.reply = frame.payload.method; return result; } } -static int amqp_login_inner(amqp_connection_state_t state, - int channel_max, - int frame_max, - int heartbeat, - amqp_sasl_method_enum sasl_method, - va_list vl) -{ - struct timeval hb; +amqp_rpc_reply_t amqp_simple_rpc(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_method_number_t request_id, + amqp_method_number_t *expected_reply_ids, + void *decoded_request_method) { + amqp_time_t deadline; + int res; + + res = amqp_time_from_now(&deadline, state->rpc_timeout); + if (res != AMQP_STATUS_OK) { + return amqp_rpc_reply_error(res); + } + + return simple_rpc_inner(state, channel, request_id, expected_reply_ids, + decoded_request_method, deadline); +} + +void *amqp_simple_rpc_decoded(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_method_number_t request_id, + amqp_method_number_t reply_id, + void *decoded_request_method) { + amqp_time_t deadline; + int res; + amqp_method_number_t replies[2]; + + res = amqp_time_from_now(&deadline, state->rpc_timeout); + if (res != AMQP_STATUS_OK) { + state->most_recent_api_result = amqp_rpc_reply_error(res); + return NULL; + } + + replies[0] = reply_id; + replies[1] = 0; + + state->most_recent_api_result = simple_rpc_inner( + state, channel, request_id, replies, decoded_request_method, deadline); + + if (state->most_recent_api_result.reply_type == AMQP_RESPONSE_NORMAL) { + return state->most_recent_api_result.reply.decoded; + } else { + return NULL; + } +} + +amqp_rpc_reply_t amqp_get_rpc_reply(amqp_connection_state_t state) { + return state->most_recent_api_result; +} + +/* + * Merge base and add tables. If the two tables contain an entry with the same + * key, the entry from the add table takes precedence. For entries that are both + * tables with the same key, the table is recursively merged. + */ +int amqp_merge_capabilities(const amqp_table_t *base, const amqp_table_t *add, + amqp_table_t *result, amqp_pool_t *pool) { + int i; + int res; + amqp_pool_t temp_pool; + amqp_table_t temp_result; + assert(base != NULL); + assert(result != NULL); + assert(pool != NULL); + + if (NULL == add) { + return amqp_table_clone(base, result, pool); + } + + init_amqp_pool(&temp_pool, 4096); + temp_result.num_entries = 0; + temp_result.entries = + amqp_pool_alloc(&temp_pool, sizeof(amqp_table_entry_t) * + (base->num_entries + add->num_entries)); + if (NULL == temp_result.entries) { + res = AMQP_STATUS_NO_MEMORY; + goto error_out; + } + for (i = 0; i < base->num_entries; ++i) { + temp_result.entries[temp_result.num_entries] = base->entries[i]; + temp_result.num_entries++; + } + for (i = 0; i < add->num_entries; ++i) { + amqp_table_entry_t *e = + amqp_table_get_entry_by_key(&temp_result, add->entries[i].key); + if (NULL != e) { + if (AMQP_FIELD_KIND_TABLE == add->entries[i].value.kind && + AMQP_FIELD_KIND_TABLE == e->value.kind) { + amqp_table_entry_t *be = + amqp_table_get_entry_by_key(base, add->entries[i].key); + + res = amqp_merge_capabilities(&be->value.value.table, + &add->entries[i].value.value.table, + &e->value.value.table, &temp_pool); + if (AMQP_STATUS_OK != res) { + goto error_out; + } + } else { + e->value = add->entries[i].value; + } + } else { + temp_result.entries[temp_result.num_entries] = add->entries[i]; + temp_result.num_entries++; + } + } + res = amqp_table_clone(&temp_result, result, pool); +error_out: + empty_amqp_pool(&temp_pool); + return res; +} + +static amqp_rpc_reply_t amqp_login_inner( + amqp_connection_state_t state, char const *vhost, int channel_max, + int frame_max, int heartbeat, const amqp_table_t *client_properties, + struct timeval *timeout, amqp_sasl_method_enum sasl_method, va_list vl) { + int res; amqp_method_t method; - uint32_t server_frame_max; + + uint16_t client_channel_max; + uint32_t client_frame_max; + uint16_t client_heartbeat; + uint16_t server_channel_max; + uint32_t server_frame_max; uint16_t server_heartbeat; - if(heartbeat != 0) { - hb.tv_sec = 2*heartbeat; hb.tv_usec = 0; - setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &hb, sizeof(hb)); - setsockopt(state->sockfd, SOL_SOCKET, SO_SNDTIMEO, &hb, sizeof(hb)); + amqp_rpc_reply_t result; + amqp_time_t deadline; + + if (channel_max < 0 || channel_max > UINT16_MAX) { + return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER); + } + client_channel_max = (uint16_t)channel_max; + + if (frame_max < 0) { + return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER); + } + client_frame_max = (uint32_t)frame_max; + + if (heartbeat < 0 || heartbeat > UINT16_MAX) { + return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER); } + client_heartbeat = (uint16_t)heartbeat; - amqp_send_header(state); + res = amqp_time_from_now(&deadline, timeout); + if (AMQP_STATUS_OK != res) { + goto error_res; + } + + res = send_header_inner(state, deadline); + if (AMQP_STATUS_OK != res) { + goto error_res; + } + + res = simple_wait_method_inner(state, 0, AMQP_CONNECTION_START_METHOD, + deadline, &method); + if (AMQP_STATUS_OK != res) { + goto error_res; + } - AMQP_CHECK_EOF_RESULT(amqp_simple_wait_method(state, 0, AMQP_CONNECTION_START_METHOD, &method)); { - amqp_connection_start_t *s = (amqp_connection_start_t *) method.decoded; + amqp_connection_start_t *s = (amqp_connection_start_t *)method.decoded; if ((s->version_major != AMQP_PROTOCOL_VERSION_MAJOR) || - (s->version_minor != AMQP_PROTOCOL_VERSION_MINOR)) { - return -EPROTOTYPE; + (s->version_minor != AMQP_PROTOCOL_VERSION_MINOR)) { + res = AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION; + goto error_res; + } + + res = amqp_table_clone(&s->server_properties, &state->server_properties, + &state->properties_pool); + + if (AMQP_STATUS_OK != res) { + goto error_res; } /* TODO: check that our chosen SASL mechanism is in the list of acceptable mechanisms. Or even let the application choose from the list! */ + if (!sasl_mechanism_in_list(s->mechanisms, sasl_method)) { + res = AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD; + goto error_res; + } } { - amqp_bytes_t response_bytes = sasl_response(&state->decoding_pool, sasl_method, vl); - amqp_connection_start_ok_t s = - (amqp_connection_start_ok_t) { - .client_properties = {.num_entries = 0, .entries = NULL}, - .mechanism = sasl_method_name(sasl_method), - .response = response_bytes, - .locale = {.len = 5, .bytes = "en_US"} - }; - AMQP_CHECK_RESULT(amqp_send_method(state, 0, AMQP_CONNECTION_START_OK_METHOD, &s)); + amqp_table_entry_t default_properties[6]; + amqp_table_t default_table; + amqp_table_entry_t client_capabilities[2]; + amqp_table_t client_capabilities_table; + amqp_connection_start_ok_t s; + amqp_pool_t *channel_pool; + amqp_bytes_t response_bytes; + + channel_pool = amqp_get_or_create_channel_pool(state, 0); + if (NULL == channel_pool) { + res = AMQP_STATUS_NO_MEMORY; + goto error_res; + } + + response_bytes = sasl_response(channel_pool, sasl_method, vl); + if (response_bytes.bytes == NULL) { + res = AMQP_STATUS_NO_MEMORY; + goto error_res; + } + + client_capabilities[0] = + amqp_table_construct_bool_entry("authentication_failure_close", 1); + client_capabilities[1] = + amqp_table_construct_bool_entry("exchange_exchange_bindings", 1); + + client_capabilities_table.entries = client_capabilities; + client_capabilities_table.num_entries = + sizeof(client_capabilities) / sizeof(amqp_table_entry_t); + + default_properties[0] = + amqp_table_construct_utf8_entry("product", "rabbitmq-c"); + default_properties[1] = + amqp_table_construct_utf8_entry("version", AMQP_VERSION_STRING); + default_properties[2] = + amqp_table_construct_utf8_entry("platform", "Linux"); + default_properties[3] = + amqp_table_construct_utf8_entry("copyright", AMQ_COPYRIGHT); + default_properties[4] = amqp_table_construct_utf8_entry( + "information", "See https://github.com/alanxz/rabbitmq-c"); + default_properties[5] = amqp_table_construct_table_entry( + "capabilities", &client_capabilities_table); + + default_table.entries = default_properties; + default_table.num_entries = + sizeof(default_properties) / sizeof(amqp_table_entry_t); + + res = amqp_merge_capabilities(&default_table, client_properties, + &state->client_properties, channel_pool); + if (AMQP_STATUS_OK != res) { + goto error_res; + } + + s.client_properties = state->client_properties; + s.mechanism = sasl_method_name(sasl_method); + s.response = response_bytes; + s.locale = amqp_cstring_bytes("en_US"); + + res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_START_OK_METHOD, &s, + AMQP_SF_NONE, deadline); + if (res < 0) { + goto error_res; + } } amqp_release_buffers(state); - AMQP_CHECK_EOF_RESULT(amqp_simple_wait_method(state, 0, AMQP_CONNECTION_TUNE_METHOD, &method)); { - amqp_connection_tune_t *s = (amqp_connection_tune_t *) method.decoded; + amqp_method_number_t expected[] = {AMQP_CONNECTION_TUNE_METHOD, + AMQP_CONNECTION_CLOSE_METHOD, 0}; + + res = amqp_simple_wait_method_list(state, 0, expected, deadline, &method); + if (AMQP_STATUS_OK != res) { + goto error_res; + } + } + + if (AMQP_CONNECTION_CLOSE_METHOD == method.id) { + result.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION; + result.reply = method; + result.library_error = 0; + goto out; + } + + { + amqp_connection_tune_t *s = (amqp_connection_tune_t *)method.decoded; server_channel_max = s->channel_max; server_frame_max = s->frame_max; server_heartbeat = s->heartbeat; } - if (server_channel_max != 0 && (server_channel_max < channel_max || channel_max == 0)) { - channel_max = server_channel_max; + if (server_channel_max != 0 && + (server_channel_max < client_channel_max || client_channel_max == 0)) { + client_channel_max = server_channel_max; + } else if (server_channel_max == 0 && client_channel_max == 0) { + client_channel_max = UINT16_MAX; } - if (server_frame_max != 0 && server_frame_max < frame_max) { - frame_max = server_frame_max; + if (server_frame_max != 0 && server_frame_max < client_frame_max) { + client_frame_max = server_frame_max; } - if (server_heartbeat != 0 && server_heartbeat < heartbeat) { - heartbeat = server_heartbeat; - } - if(heartbeat != 0) { - hb.tv_sec = 2*heartbeat; hb.tv_usec = 0; - setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &hb, sizeof(hb)); - setsockopt(state->sockfd, SOL_SOCKET, SO_SNDTIMEO, &hb, sizeof(hb)); + if (server_heartbeat != 0 && server_heartbeat < client_heartbeat) { + client_heartbeat = server_heartbeat; } - AMQP_CHECK_RESULT(amqp_tune_connection(state, channel_max, frame_max, heartbeat)); + res = amqp_tune_connection(state, client_channel_max, client_frame_max, + client_heartbeat); + if (res < 0) { + goto error_res; + } { - amqp_connection_tune_ok_t s = - (amqp_connection_tune_ok_t) { - .channel_max = channel_max, - .frame_max = frame_max, - .heartbeat = heartbeat - }; - AMQP_CHECK_RESULT(amqp_send_method(state, 0, AMQP_CONNECTION_TUNE_OK_METHOD, &s)); + amqp_connection_tune_ok_t s; + s.frame_max = client_frame_max; + s.channel_max = client_channel_max; + s.heartbeat = client_heartbeat; + + res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_TUNE_OK_METHOD, &s, + AMQP_SF_NONE, deadline); + if (res < 0) { + goto error_res; + } } amqp_release_buffers(state); - return 1; + { + amqp_method_number_t replies[] = {AMQP_CONNECTION_OPEN_OK_METHOD, 0}; + amqp_connection_open_t s; + s.virtual_host = amqp_cstring_bytes(vhost); + s.capabilities = amqp_empty_bytes; + s.insist = 1; + + result = simple_rpc_inner(state, 0, AMQP_CONNECTION_OPEN_METHOD, replies, + &s, deadline); + if (result.reply_type != AMQP_RESPONSE_NORMAL) { + goto out; + } + } + + result.reply_type = AMQP_RESPONSE_NORMAL; + result.reply.id = 0; + result.reply.decoded = NULL; + result.library_error = 0; + amqp_maybe_release_buffers(state); + +out: + return result; + +error_res: + amqp_socket_close(state->socket, AMQP_SC_FORCE); + result = amqp_rpc_reply_error(res); + + goto out; } -amqp_rpc_reply_t amqp_login(amqp_connection_state_t state, - char const *vhost, - int channel_max, - int frame_max, - int heartbeat, - amqp_sasl_method_enum sasl_method, - ...) -{ +amqp_rpc_reply_t amqp_login(amqp_connection_state_t state, char const *vhost, + int channel_max, int frame_max, int heartbeat, + int sasl_method, ...) { va_list vl; - amqp_rpc_reply_t result; + amqp_rpc_reply_t ret; va_start(vl, sasl_method); - amqp_login_inner(state, channel_max, frame_max, heartbeat, sasl_method, vl); + ret = amqp_login_inner(state, vhost, channel_max, frame_max, heartbeat, + &amqp_empty_table, state->handshake_timeout, + sasl_method, vl); - { - amqp_connection_open_t s = - (amqp_connection_open_t) { - .virtual_host = amqp_cstring_bytes(vhost), - .capabilities = {.len = 0, .bytes = NULL}, - .insist = 1 - }; - amqp_method_number_t replies[] = { AMQP_CONNECTION_OPEN_OK_METHOD, 0 }; - result = amqp_simple_rpc(state, - 0, - AMQP_CONNECTION_OPEN_METHOD, - (amqp_method_number_t *) &replies, - &s); - if (result.reply_type != AMQP_RESPONSE_NORMAL) { - return result; - } - } - amqp_maybe_release_buffers(state); + va_end(vl); + + return ret; +} + +amqp_rpc_reply_t amqp_login_with_properties( + amqp_connection_state_t state, char const *vhost, int channel_max, + int frame_max, int heartbeat, const amqp_table_t *client_properties, + int sasl_method, ...) { + va_list vl; + amqp_rpc_reply_t ret; + + va_start(vl, sasl_method); + + ret = amqp_login_inner(state, vhost, channel_max, frame_max, heartbeat, + client_properties, state->handshake_timeout, + sasl_method, vl); va_end(vl); - result.reply_type = AMQP_RESPONSE_NORMAL; - result.reply.id = 0; - result.reply.decoded = NULL; - result.library_errno = 0; - return result; + return ret; } diff --git a/src/librabbitmq/amqp_socket.h b/src/librabbitmq/amqp_socket.h new file mode 100644 index 0000000..3101cf6 --- /dev/null +++ b/src/librabbitmq/amqp_socket.h @@ -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 */ diff --git a/src/librabbitmq/amqp_ssl_socket.h b/src/librabbitmq/amqp_ssl_socket.h new file mode 100644 index 0000000..70d1a56 --- /dev/null +++ b/src/librabbitmq/amqp_ssl_socket.h @@ -0,0 +1,252 @@ +/** \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_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); + +/** + * Get the internal OpenSSL context. Caveat emptor. + * + * \param [in,out] self An SSL/TLS socket object. + * + * \return A pointer to the internal OpenSSL context. This should be cast to + * SSL_CTX*. + * + * \since v0.9.0 + */ +AMQP_PUBLIC_FUNCTION +void *AMQP_CALL amqp_ssl_socket_get_context(amqp_socket_t *self); + +/** + * 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 */ diff --git a/src/librabbitmq/amqp_table.c b/src/librabbitmq/amqp_table.c index 16d85ff..24b087c 100644 --- a/src/librabbitmq/amqp_table.c +++ b/src/librabbitmq/amqp_table.c @@ -1,170 +1,668 @@ -#include -#include -#include -#include -#include +/* + * ***** 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 ***** + */ -#include "amqp.h" -#include "amqp_private.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "amqp_private.h" +#include "amqp_table.h" #include +#include +#include +#include +#include +#define INITIAL_ARRAY_SIZE 16 #define INITIAL_TABLE_SIZE 16 -int amqp_decode_table(amqp_bytes_t encoded, - amqp_pool_t *pool, - amqp_table_t *output, - int *offsetptr) -{ - int offset = *offsetptr; - uint32_t tablesize = D_32(encoded, offset); +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; - amqp_table_entry_t *entries = malloc(INITIAL_TABLE_SIZE * sizeof(amqp_table_entry_t)); - int allocated_entries = INITIAL_TABLE_SIZE; - int limit; + 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 -ENOMEM; + 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++; } - offset += 4; - limit = offset + tablesize; + 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; + } - while (offset < limit) { - size_t keylen; - amqp_table_entry_t *entry; + memcpy(output->entries, entries, num_entries * sizeof(amqp_field_value_t)); + res = AMQP_STATUS_OK; - keylen = D_8(encoded, offset); - offset++; +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)); + newentries = + realloc(entries, allocated_entries * sizeof(amqp_table_entry_t)); + res = AMQP_STATUS_NO_MEMORY; if (newentries == NULL) { - free(entries); - return -ENOMEM; + goto out; } + entries = newentries; } - entry = &entries[num_entries]; - - entry->key.len = keylen; - entry->key.bytes = D_BYTES(encoded, offset, keylen); - offset += keylen; - - entry->kind = D_8(encoded, offset); - offset++; - - switch (entry->kind) { - case 'S': - entry->value.bytes.len = D_32(encoded, offset); - offset += 4; - entry->value.bytes.bytes = D_BYTES(encoded, offset, entry->value.bytes.len); - offset += entry->value.bytes.len; - break; - case 'I': - entry->value.i32 = (int32_t) D_32(encoded, offset); - offset += 4; - break; - case 'D': - entry->value.decimal.decimals = D_8(encoded, offset); - offset++; - entry->value.decimal.value = D_32(encoded, offset); - offset += 4; - break; - case 'T': - entry->value.u64 = D_64(encoded, offset); - offset += 8; - break; - case 'F': - AMQP_CHECK_RESULT(amqp_decode_table(encoded, pool, &(entry->value.table), &offset)); - break; - default: - return -EINVAL; + + 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)); + 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; - *offsetptr = offset; - return 0; +out: + free(entries); + return res; } -int amqp_encode_table(amqp_bytes_t encoded, - amqp_table_t *input, - int *offsetptr) -{ - int offset = *offsetptr; - int tablesize_offset = offset; - int i; +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; - offset += 4; /* skip space for the size of the table to be filled in later */ +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++) { - amqp_table_entry_t *entry = &(input->entries[i]); - - E_8(encoded, offset, entry->key.len); - offset++; - - E_BYTES(encoded, offset, entry->key.len, entry->key.bytes); - offset += entry->key.len; - - E_8(encoded, offset, entry->kind); - offset++; - - switch (entry->kind) { - case 'S': - E_32(encoded, offset, entry->value.bytes.len); - offset += 4; - E_BYTES(encoded, offset, entry->value.bytes.len, entry->value.bytes.bytes); - offset += entry->value.bytes.len; - break; - case 'I': - E_32(encoded, offset, (uint32_t) entry->value.i32); - offset += 4; - break; - case 'D': - E_8(encoded, offset, entry->value.decimal.decimals); - offset++; - E_32(encoded, offset, entry->value.decimal.value); - offset += 4; - break; - case 'T': - E_64(encoded, offset, entry->value.u64); - offset += 8; - break; - case 'F': - AMQP_CHECK_RESULT(amqp_encode_table(encoded, &(entry->value.table), &offset)); - break; - default: - return -EINVAL; + 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; } } - E_32(encoded, tablesize_offset, (offset - *offsetptr - 4)); - *offsetptr = offset; - return 0; + 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; + 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; - int minlen; + size_t minlen; minlen = p1->key.len; - if (p2->key.len < minlen) minlen = p2->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 p1->key.len - p2->key.len; + 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; } diff --git a/src/librabbitmq/amqp_table.h b/src/librabbitmq/amqp_table.h new file mode 100644 index 0000000..7b009a9 --- /dev/null +++ b/src/librabbitmq/amqp_table.h @@ -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 */ diff --git a/src/librabbitmq/amqp_tcp_socket.c b/src/librabbitmq/amqp_tcp_socket.c new file mode 100644 index 0000000..12e02cd --- /dev/null +++ b/src/librabbitmq/amqp_tcp_socket.c @@ -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 +#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__))) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#else +#include +#include +#include +#endif +#include +#include + +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; +} diff --git a/src/librabbitmq/amqp_tcp_socket.h b/src/librabbitmq/amqp_tcp_socket.h new file mode 100644 index 0000000..6389171 --- /dev/null +++ b/src/librabbitmq/amqp_tcp_socket.h @@ -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 */ diff --git a/src/librabbitmq/amqp_time.c b/src/librabbitmq/amqp_time.c new file mode 100644 index 0000000..7b0a42d --- /dev/null +++ b/src/librabbitmq/amqp_time.c @@ -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 +#include +#include + +#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 + +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 + +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 + +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; +} diff --git a/src/librabbitmq/amqp_time.h b/src/librabbitmq/amqp_time.h new file mode 100644 index 0000000..194bf67 --- /dev/null +++ b/src/librabbitmq/amqp_time.h @@ -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 + +#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 +#else +#include +#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 */ diff --git a/src/librabbitmq/amqp_url.c b/src/librabbitmq/amqp_url.c new file mode 100644 index 0000000..b5304e5 --- /dev/null +++ b/src/librabbitmq/amqp_url.c @@ -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 +#include +#include +#include +#include + +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; +} diff --git a/src/librabbitmq/unix/threads.h b/src/librabbitmq/unix/threads.h new file mode 100644 index 0000000..8a2623b --- /dev/null +++ b/src/librabbitmq/unix/threads.h @@ -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 + +#endif /* AMQP_THREADS_H */ diff --git a/src/librabbitmq/win32/msinttypes/stdint.h b/src/librabbitmq/win32/msinttypes/stdint.h new file mode 100644 index 0000000..a7437be --- /dev/null +++ b/src/librabbitmq/win32/msinttypes/stdint.h @@ -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 + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap 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 +#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 +#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_ ] diff --git a/src/librabbitmq/win32/threads.c b/src/librabbitmq/win32/threads.c new file mode 100644 index 0000000..cce3158 --- /dev/null +++ b/src/librabbitmq/win32/threads.c @@ -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 + +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; +} diff --git a/src/librabbitmq/win32/threads.h b/src/librabbitmq/win32/threads.h new file mode 100644 index 0000000..69371f3 --- /dev/null +++ b/src/librabbitmq/win32/threads.h @@ -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 + +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 */ diff --git a/src/pg_amqp.c b/src/pg_amqp.c index 1e091ac..1030a0a 100644 --- a/src/pg_amqp.c +++ b/src/pg_amqp.c @@ -52,6 +52,7 @@ #include "utils/builtins.h" #include "librabbitmq/amqp.h" #include "librabbitmq/amqp_framing.h" +#include "librabbitmq/amqp_tcp_socket.h" #define set_bytes_from_text(var,col) do { \ if(!PG_ARGISNULL(col)) { \ @@ -73,7 +74,8 @@ Datum pg_amqp_disconnect(PG_FUNCTION_ARGS); struct brokerstate { int broker_id; amqp_connection_state_t conn; - int sockfd; + amqp_socket_t *sock; + int sock_status; int uncommitted; int inerror; int idx; @@ -87,14 +89,13 @@ local_amqp_disconnect_bs(struct brokerstate *bs) { if(bs && bs->conn) { int errorstate = bs->inerror; amqp_connection_close(bs->conn, AMQP_REPLY_SUCCESS); - if(bs->sockfd >= 0) close(bs->sockfd); amqp_destroy_connection(bs->conn); memset(bs, 0, sizeof(*bs)); bs->inerror = errorstate; } } static void amqp_local_phase2(XactEvent event, void *arg) { - amqp_rpc_reply_t *reply; + amqp_rpc_reply_t reply; struct brokerstate *bs; switch(event) { case XACT_EVENT_COMMIT: @@ -102,10 +103,10 @@ static void amqp_local_phase2(XactEvent event, void *arg) { if(bs->inerror) local_amqp_disconnect_bs(bs); bs->inerror = 0; if(!bs->uncommitted) continue; - if(bs->conn) amqp_tx_commit(bs->conn, 2, AMQP_EMPTY_TABLE); - reply = amqp_get_rpc_reply(); - if(reply->reply_type != AMQP_RESPONSE_NORMAL) { - elog(WARNING, "amqp could not commit tx mode on broker %d, reply_type=%d, library_errno=%d", bs->broker_id, reply->reply_type, reply->library_errno); + if(bs->conn) amqp_tx_commit(bs->conn, 2); + reply = amqp_get_rpc_reply(bs->conn); + if(reply.reply_type != AMQP_RESPONSE_NORMAL) { + elog(WARNING, "amqp could not commit tx mode on broker %d, reply_type=%d, library_error=%d", bs->broker_id, reply.reply_type, reply.library_error); local_amqp_disconnect_bs(bs); } bs->uncommitted = 0; @@ -116,10 +117,10 @@ static void amqp_local_phase2(XactEvent event, void *arg) { if(bs->inerror) local_amqp_disconnect_bs(bs); bs->inerror = 0; if(!bs->uncommitted) continue; - if(bs->conn) amqp_tx_rollback(bs->conn, 2, AMQP_EMPTY_TABLE); - reply = amqp_get_rpc_reply(); - if(reply->reply_type != AMQP_RESPONSE_NORMAL) { - elog(WARNING, "amqp could not rollback tx mode on broker %d, reply_type=%d, library_errno=%d", bs->broker_id, reply->reply_type, reply->library_errno); + if(bs->conn) amqp_tx_rollback(bs->conn, 2); + reply = amqp_get_rpc_reply(bs->conn); + if(reply.reply_type != AMQP_RESPONSE_NORMAL) { + elog(WARNING, "amqp could not rollback tx mode on broker %d, reply_type=%d, library_error=%d", bs->broker_id, reply.reply_type, reply.library_error); local_amqp_disconnect_bs(bs); } bs->uncommitted = 0; @@ -128,7 +129,43 @@ static void amqp_local_phase2(XactEvent event, void *arg) { case XACT_EVENT_PREPARE: /* nothin' */ return; + case XACT_EVENT_PARALLEL_COMMIT: + for(bs = HEAD_BS; bs; bs = bs->next) { + if(bs->inerror) local_amqp_disconnect_bs(bs); + bs->inerror = 0; + if(!bs->uncommitted) continue; + if(bs->conn) amqp_tx_rollback(bs->conn, 2); + reply = amqp_get_rpc_reply(bs->conn); + if(reply.reply_type != AMQP_RESPONSE_NORMAL) { + elog(WARNING, "amqp could not rollback tx mode on broker %d, reply_type=%d, library_error=%d", bs->broker_id, reply.reply_type, reply.library_error); + local_amqp_disconnect_bs(bs); + } + bs->uncommitted = 0; + } + break; + case XACT_EVENT_PARALLEL_ABORT: + for(bs = HEAD_BS; bs; bs = bs->next) { + if(bs->inerror) local_amqp_disconnect_bs(bs); + bs->inerror = 0; + if(!bs->uncommitted) continue; + if(bs->conn) amqp_tx_rollback(bs->conn, 2); + reply = amqp_get_rpc_reply(bs->conn); + if(reply.reply_type != AMQP_RESPONSE_NORMAL) { + elog(WARNING, "amqp could not rollback tx mode on broker %d, reply_type=%d, library_error=%d", bs->broker_id, reply.reply_type, reply.library_error); + local_amqp_disconnect_bs(bs); + } + bs->uncommitted = 0; + } break; + case XACT_EVENT_PRE_COMMIT: + /* nothin' */ + return; + case XACT_EVENT_PARALLEL_PRE_COMMIT: + /* nothin' */ + return; + case XACT_EVENT_PRE_PREPARE: + /* nothin' */ + return; } } @@ -148,6 +185,7 @@ local_amqp_get_a_bs(broker_id) { HEAD_BS = bs; return bs; } + static struct brokerstate * local_amqp_get_bs(broker_id) { char sql[1024]; @@ -165,8 +203,7 @@ local_amqp_get_bs(broker_id) { retry: tries--; if(SPI_processed > 0) { - struct timeval hb = { .tv_sec = 2UL, .tv_usec = 0UL }; - amqp_rpc_reply_t *reply, s_reply; + amqp_rpc_reply_t reply, s_reply; char *host, *vhost, *user, *pass; Datum port_datum; bool is_null; @@ -186,13 +223,17 @@ local_amqp_get_bs(broker_id) { bs->conn = amqp_new_connection(); if(!bs->conn) { SPI_finish(); return NULL; } - bs->sockfd = amqp_open_socket(host, port, &hb); - if(bs->sockfd < 0) { - elog(WARNING, "amqp[%s] login socket/connect failed: %s", - host_copy, strerror(-bs->sockfd)); + bs->sock = amqp_tcp_socket_new(bs->conn); + if (!bs->sock) { + elog(WARNING, "amqp[%s] creating tcp socket failed.", host_copy); + goto busted; + } + bs->sock_status = amqp_socket_open(bs->sock, host, port); + if (!bs->sock_status) { + elog(WARNING, "amqp[%s] opening tcp socket failed.", host_copy); goto busted; } - amqp_set_sockfd(bs->conn, bs->sockfd); + s_reply = amqp_login(bs->conn, vhost, 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, user, pass); @@ -201,20 +242,20 @@ local_amqp_get_bs(broker_id) { goto busted; } amqp_channel_open(bs->conn, 1); - reply = amqp_get_rpc_reply(); - if(reply->reply_type != AMQP_RESPONSE_NORMAL) { + reply = amqp_get_rpc_reply(bs->conn); + if(reply.reply_type != AMQP_RESPONSE_NORMAL) { elog(WARNING, "amqp[%s] channel open failed on broker %d", host_copy, broker_id); goto busted; } amqp_channel_open(bs->conn, 2); - reply = amqp_get_rpc_reply(); - if(reply->reply_type != AMQP_RESPONSE_NORMAL) { + reply = amqp_get_rpc_reply(bs->conn); + if(reply.reply_type != AMQP_RESPONSE_NORMAL) { elog(WARNING, "amqp[%s] channel open failed on broker %d", host_copy, broker_id); goto busted; } - amqp_tx_select(bs->conn, 2, AMQP_EMPTY_TABLE); - reply = amqp_get_rpc_reply(); - if(reply->reply_type != AMQP_RESPONSE_NORMAL) { + amqp_tx_select(bs->conn, 2); + reply = amqp_get_rpc_reply(bs->conn); + if(reply.reply_type != AMQP_RESPONSE_NORMAL) { elog(WARNING, "amqp[%s] could not start tx mode on broker %d", host_copy, broker_id); goto busted; } @@ -250,23 +291,25 @@ pg_amqp_exchange_declare(PG_FUNCTION_ARGS) { broker_id = PG_GETARG_INT32(0); bs = local_amqp_get_bs(broker_id); if(bs && bs->conn) { - amqp_rpc_reply_t *reply; - amqp_bytes_t exchange_b; - amqp_bytes_t exchange_type_b; + amqp_rpc_reply_t reply; + amqp_bytes_t exchange_b = { .len = 0 }; + amqp_bytes_t exchange_type_b = { .len = 0 }; amqp_boolean_t passive = 0; amqp_boolean_t durable = 0; amqp_boolean_t auto_delete = 0; + amqp_boolean_t internal = 0; set_bytes_from_text(exchange_b,1); set_bytes_from_text(exchange_type_b,2); passive = PG_GETARG_BOOL(3); durable = PG_GETARG_BOOL(4); auto_delete = PG_GETARG_BOOL(5); + internal = PG_GETARG_BOOL(6); amqp_exchange_declare(bs->conn, 1, exchange_b, exchange_type_b, - passive, durable, auto_delete, AMQP_EMPTY_TABLE); - reply = amqp_get_rpc_reply(); - if(reply->reply_type == AMQP_RESPONSE_NORMAL) + passive, durable, auto_delete, internal, AMQP_EMPTY_TABLE); + reply = amqp_get_rpc_reply(bs->conn); + if(reply.reply_type == AMQP_RESPONSE_NORMAL) PG_RETURN_BOOL(0 == 0); bs->inerror = 1; } @@ -279,50 +322,50 @@ pg_amqp_publish_opt(PG_FUNCTION_ARGS, int channel) { if(!PG_ARGISNULL(0)) { int broker_id; amqp_basic_properties_t properties; - + int once_more = 1; broker_id = PG_GETARG_INT32(0); redo: bs = local_amqp_get_bs(broker_id); if(bs && bs->conn && (channel == 1 || !bs->inerror)) { int rv; - amqp_rpc_reply_t *reply; + amqp_rpc_reply_t reply; amqp_boolean_t mandatory = 0; amqp_boolean_t immediate = 0; amqp_bytes_t exchange_b = amqp_cstring_bytes("amq.direct"); amqp_bytes_t routing_key_b = amqp_cstring_bytes(""); - amqp_bytes_t body_b = amqp_cstring_bytes(""); + amqp_bytes_t body_b = amqp_cstring_bytes(""); properties._flags = 0; - + /* Sets delivery_mode */ if (!PG_ARGISNULL(4)) { - if (PG_GETARG_INT32(4) == 1 || PG_GETARG_INT32(4) == 2) { - properties._flags |= AMQP_BASIC_DELIVERY_MODE_FLAG; + if (PG_GETARG_INT32(4) == 1 || PG_GETARG_INT32(4) == 2) { + properties._flags |= AMQP_BASIC_DELIVERY_MODE_FLAG; properties.delivery_mode = PG_GETARG_INT32(4); - } else { - elog(WARNING, "Ignored delivery_mode %d, value should be 1 or 2", + } else { + elog(WARNING, "Ignored delivery_mode %d, value should be 1 or 2", PG_GETARG_INT32(4)); - } + } } /* Sets content_type */ if (!PG_ARGISNULL(5)) { - properties._flags |= AMQP_BASIC_CONTENT_TYPE_FLAG; - set_bytes_from_text(properties.content_type, 5); + properties._flags |= AMQP_BASIC_CONTENT_TYPE_FLAG; + set_bytes_from_text(properties.content_type, 5); } /* Sets reply_to */ if (!PG_ARGISNULL(6)) { - properties._flags |= AMQP_BASIC_REPLY_TO_FLAG; - set_bytes_from_text(properties.reply_to, 6); + properties._flags |= AMQP_BASIC_REPLY_TO_FLAG; + set_bytes_from_text(properties.reply_to, 6); } /* Sets correlation_id */ if (!PG_ARGISNULL(7)) { - properties._flags |= AMQP_BASIC_CORRELATION_ID_FLAG; - set_bytes_from_text(properties.correlation_id, 7); + properties._flags |= AMQP_BASIC_CORRELATION_ID_FLAG; + set_bytes_from_text(properties.correlation_id, 7); } - + set_bytes_from_text(exchange_b,1); set_bytes_from_text(routing_key_b,2); set_bytes_from_text(body_b,3); @@ -335,8 +378,8 @@ pg_amqp_publish_opt(PG_FUNCTION_ARGS, int channel) { mandatory, immediate, &properties, body_b); } - reply = amqp_get_rpc_reply(); - if(rv || reply->reply_type != AMQP_RESPONSE_NORMAL) { + reply = amqp_get_rpc_reply(bs->conn); + if(rv || reply.reply_type != AMQP_RESPONSE_NORMAL) { if(once_more && (channel == 1 || bs->uncommitted == 0)) { once_more = 0; local_amqp_disconnect_bs(bs); @@ -375,4 +418,3 @@ pg_amqp_disconnect(PG_FUNCTION_ARGS) { } PG_RETURN_VOID(); } -