123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- // Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
- // SPDX-License-Identifier: mit
- #include "common.h"
- #ifdef WITH_SSL
- #include <rabbitmq-c/ssl_socket.h>
- #endif
- #include <errno.h>
- #include <fcntl.h>
- #include <rabbitmq-c/tcp_socket.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #ifdef WINDOWS
- #include "compat.h"
- #endif
- void die(const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
- exit(1);
- }
- void die_errno(int err, const char *fmt, ...) {
- va_list ap;
- if (err == 0) {
- return;
- }
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, ": %s\n", strerror(err));
- exit(1);
- }
- void die_amqp_error(int err, const char *fmt, ...) {
- va_list ap;
- if (err >= 0) {
- return;
- }
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, ": %s\n", amqp_error_string2(err));
- exit(1);
- }
- const char *amqp_server_exception_string(amqp_rpc_reply_t r) {
- int res;
- static char s[512];
- switch (r.reply.id) {
- case AMQP_CONNECTION_CLOSE_METHOD: {
- amqp_connection_close_t *m = (amqp_connection_close_t *)r.reply.decoded;
- res = snprintf(s, sizeof(s), "server connection error %d, message: %.*s",
- m->reply_code, (int)m->reply_text.len,
- (char *)m->reply_text.bytes);
- break;
- }
- case AMQP_CHANNEL_CLOSE_METHOD: {
- amqp_channel_close_t *m = (amqp_channel_close_t *)r.reply.decoded;
- res = snprintf(s, sizeof(s), "server channel error %d, message: %.*s",
- m->reply_code, (int)m->reply_text.len,
- (char *)m->reply_text.bytes);
- break;
- }
- default:
- res = snprintf(s, sizeof(s), "unknown server error, method id 0x%08X",
- r.reply.id);
- break;
- }
- return res >= 0 ? s : NULL;
- }
- const char *amqp_rpc_reply_string(amqp_rpc_reply_t r) {
- switch (r.reply_type) {
- case AMQP_RESPONSE_NORMAL:
- return "normal response";
- case AMQP_RESPONSE_NONE:
- return "missing RPC reply type";
- case AMQP_RESPONSE_LIBRARY_EXCEPTION:
- return amqp_error_string2(r.library_error);
- case AMQP_RESPONSE_SERVER_EXCEPTION:
- return amqp_server_exception_string(r);
- default:
- abort();
- }
- }
- void die_rpc(amqp_rpc_reply_t r, const char *fmt, ...) {
- va_list ap;
- if (r.reply_type == AMQP_RESPONSE_NORMAL) {
- return;
- }
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, ": %s\n", amqp_rpc_reply_string(r));
- exit(1);
- }
- static char *amqp_url;
- static char *amqp_server;
- static int amqp_port = -1;
- static char *amqp_vhost;
- static char *amqp_username;
- static char *amqp_password;
- static int amqp_heartbeat = 0;
- #ifdef WITH_SSL
- static int amqp_ssl = 0;
- static char *amqp_cacert = "/etc/ssl/certs/cacert.pem";
- static char *amqp_key = NULL;
- static char *amqp_cert = NULL;
- #endif /* WITH_SSL */
- const char *connect_options_title = "Connection options";
- struct poptOption connect_options[] = {
- {"url", 'u', POPT_ARG_STRING, &amqp_url, 0, "the AMQP URL to connect to",
- "amqp://..."},
- {"server", 's', POPT_ARG_STRING, &amqp_server, 0,
- "the AMQP server to connect to", "hostname"},
- {"port", 0, POPT_ARG_INT, &amqp_port, 0, "the port to connect on", "port"},
- {"vhost", 0, POPT_ARG_STRING, &amqp_vhost, 0,
- "the vhost to use when connecting", "vhost"},
- {"username", 0, POPT_ARG_STRING, &amqp_username, 0,
- "the username to login with", "username"},
- {"password", 0, POPT_ARG_STRING, &amqp_password, 0,
- "the password to login with", "password"},
- {"heartbeat", 0, POPT_ARG_INT, &amqp_heartbeat, 0,
- "heartbeat interval, set to 0 to disable", "heartbeat"},
- #ifdef WITH_SSL
- {"ssl", 0, POPT_ARG_NONE, &amqp_ssl, 0, "connect over SSL/TLS", NULL},
- {"cacert", 0, POPT_ARG_STRING, &amqp_cacert, 0,
- "path to the CA certificate file", "cacert.pem"},
- {"key", 0, POPT_ARG_STRING, &amqp_key, 0,
- "path to the client private key file", "key.pem"},
- {"cert", 0, POPT_ARG_STRING, &amqp_cert, 0,
- "path to the client certificate file", "cert.pem"},
- #endif /* WITH_SSL */
- {NULL, '\0', 0, NULL, 0, NULL, NULL}};
- static void init_connection_info(struct amqp_connection_info *ci) {
- ci->user = NULL;
- ci->password = NULL;
- ci->host = NULL;
- ci->port = -1;
- ci->vhost = NULL;
- ci->user = NULL;
- amqp_default_connection_info(ci);
- if (amqp_url)
- die_amqp_error(amqp_parse_url(strdup(amqp_url), ci), "Parsing URL '%s'",
- amqp_url);
- if (amqp_server) {
- char *colon;
- if (amqp_url) {
- die("--server and --url options cannot be used at the same time");
- }
- /* parse the server string into a hostname and a port */
- colon = strchr(amqp_server, ':');
- if (colon) {
- char *port_end;
- size_t host_len;
- /* Deprecate specifying the port number with the
- --server option, because it is not ipv6 friendly.
- --url now allows connection options to be
- specified concisely. */
- fprintf(stderr,
- "Specifying the port number with --server is deprecated\n");
- host_len = colon - amqp_server;
- ci->host = malloc(host_len + 1);
- memcpy(ci->host, amqp_server, host_len);
- ci->host[host_len] = 0;
- if (amqp_port >= 0) {
- die("both --server and --port options specify server port");
- }
- ci->port = strtol(colon + 1, &port_end, 10);
- if (ci->port < 0 || ci->port > 65535 || port_end == colon + 1 ||
- *port_end != 0)
- die("bad server port number in '%s'", amqp_server);
- } else {
- ci->host = amqp_server;
- ci->port = 5672;
- #if WITH_SSL
- if (amqp_ssl) {
- ci->port = 5671;
- }
- #endif
- }
- }
- #if WITH_SSL
- if (amqp_ssl && !ci->ssl) {
- if (amqp_url) {
- die("the --ssl option specifies an SSL connection"
- " but the --url option does not");
- } else {
- ci->ssl = 1;
- }
- }
- #endif
- if (amqp_port >= 0) {
- if (amqp_url) {
- die("--port and --url options cannot be used at the same time");
- }
- ci->port = amqp_port;
- }
- if (amqp_username) {
- if (amqp_url) {
- die("--username and --url options cannot be used at the same time");
- }
- ci->user = amqp_username;
- }
- if (amqp_password) {
- if (amqp_url) {
- die("--password and --url options cannot be used at the same time");
- }
- ci->password = amqp_password;
- }
- if (amqp_vhost) {
- if (amqp_url) {
- die("--vhost and --url options cannot be used at the same time");
- }
- ci->vhost = amqp_vhost;
- }
- if (amqp_heartbeat < 0) {
- die("--heartbeat must be a positive value");
- }
- }
- amqp_connection_state_t make_connection(void) {
- int status;
- amqp_socket_t *socket = NULL;
- struct amqp_connection_info ci;
- amqp_connection_state_t conn;
- init_connection_info(&ci);
- conn = amqp_new_connection();
- if (ci.ssl) {
- #ifdef WITH_SSL
- socket = amqp_ssl_socket_new(conn);
- if (!socket) {
- die("creating SSL/TLS socket");
- }
- if (amqp_cacert) {
- amqp_ssl_socket_set_cacert(socket, amqp_cacert);
- }
- if (amqp_key) {
- amqp_ssl_socket_set_key(socket, amqp_cert, amqp_key);
- }
- #else
- die("librabbitmq was not built with SSL/TLS support");
- #endif
- } else {
- socket = amqp_tcp_socket_new(conn);
- if (!socket) {
- die("creating TCP socket (out of memory)");
- }
- }
- status = amqp_socket_open(socket, ci.host, ci.port);
- if (status) {
- die_amqp_error(status, "opening socket to %s:%d", ci.host, ci.port);
- }
- die_rpc(amqp_login(conn, ci.vhost, 0, 131072, amqp_heartbeat,
- AMQP_SASL_METHOD_PLAIN, ci.user, ci.password),
- "logging in to AMQP server");
- if (!amqp_channel_open(conn, 1)) {
- die_rpc(amqp_get_rpc_reply(conn), "opening channel");
- }
- return conn;
- }
- void close_connection(amqp_connection_state_t conn) {
- int res;
- die_rpc(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), "closing channel");
- die_rpc(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
- "closing connection");
- res = amqp_destroy_connection(conn);
- die_amqp_error(res, "closing connection");
- }
- amqp_bytes_t read_all(int fd) {
- size_t space = 4096;
- amqp_bytes_t bytes;
- bytes.bytes = malloc(space);
- bytes.len = 0;
- for (;;) {
- ssize_t res = read(fd, (char *)bytes.bytes + bytes.len, space - bytes.len);
- if (res == 0) {
- break;
- }
- if (res < 0) {
- if (errno == EINTR) {
- continue;
- }
- die_errno(errno, "reading");
- }
- bytes.len += res;
- if (bytes.len == space) {
- space *= 2;
- bytes.bytes = realloc(bytes.bytes, space);
- }
- }
- return bytes;
- }
- void write_all(int fd, amqp_bytes_t data) {
- while (data.len > 0) {
- ssize_t res = write(fd, data.bytes, data.len);
- if (res < 0) {
- die_errno(errno, "write");
- }
- data.len -= res;
- data.bytes = (char *)data.bytes + res;
- }
- }
- void copy_body(amqp_connection_state_t conn, int fd) {
- size_t body_remaining;
- amqp_frame_t frame;
- int res = amqp_simple_wait_frame(conn, &frame);
- die_amqp_error(res, "waiting for header frame");
- if (frame.frame_type != AMQP_FRAME_HEADER) {
- die("expected header, got frame type 0x%X", frame.frame_type);
- }
- body_remaining = frame.payload.properties.body_size;
- while (body_remaining) {
- res = amqp_simple_wait_frame(conn, &frame);
- die_amqp_error(res, "waiting for body frame");
- if (frame.frame_type != AMQP_FRAME_BODY) {
- die("expected body, got frame type 0x%X", frame.frame_type);
- }
- write_all(fd, frame.payload.body_fragment);
- body_remaining -= frame.payload.body_fragment.len;
- }
- }
- poptContext process_options(int argc, const char **argv,
- struct poptOption *options, const char *help) {
- int c;
- poptContext opts = poptGetContext(NULL, argc, argv, options, 0);
- poptSetOtherOptionHelp(opts, help);
- while ((c = poptGetNextOpt(opts)) >= 0) {
- /* no options require explicit handling */
- }
- if (c < -1) {
- fprintf(stderr, "%s: %s\n", poptBadOption(opts, POPT_BADOPTION_NOALIAS),
- poptStrerror(c));
- poptPrintUsage(opts, stderr, 0);
- exit(1);
- }
- return opts;
- }
- void process_all_options(int argc, const char **argv,
- struct poptOption *options) {
- poptContext opts = process_options(argc, argv, options, "[OPTIONS]...");
- const char *opt = poptPeekArg(opts);
- if (opt) {
- fprintf(stderr, "unexpected operand: %s\n", opt);
- poptPrintUsage(opts, stderr, 0);
- exit(1);
- }
- poptFreeContext(opts);
- }
- amqp_bytes_t cstring_bytes(const char *str) {
- return str ? amqp_cstring_bytes(str) : amqp_empty_bytes;
- }
|