summaryrefslogtreecommitdiffstats
path: root/src/global/anvil_clnt.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:18:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:18:56 +0000
commitb7c15c31519dc44c1f691e0466badd556ffe9423 (patch)
treef944572f288bab482a615e09af627d9a2b6727d8 /src/global/anvil_clnt.c
parentInitial commit. (diff)
downloadpostfix-b7c15c31519dc44c1f691e0466badd556ffe9423.tar.xz
postfix-b7c15c31519dc44c1f691e0466badd556ffe9423.zip
Adding upstream version 3.7.10.upstream/3.7.10
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/global/anvil_clnt.c')
-rw-r--r--src/global/anvil_clnt.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/src/global/anvil_clnt.c b/src/global/anvil_clnt.c
new file mode 100644
index 0000000..fff9ec7
--- /dev/null
+++ b/src/global/anvil_clnt.c
@@ -0,0 +1,527 @@
+/*++
+/* NAME
+/* anvil_clnt 3
+/* SUMMARY
+/* connection count and rate management client interface
+/* SYNOPSIS
+/* #include <anvil_clnt.h>
+/*
+/* ANVIL_CLNT *anvil_clnt_create(void)
+/*
+/* void anvil_clnt_free(anvil_clnt)
+/* ANVIL_CLNT *anvil_clnt;
+/*
+/* int anvil_clnt_connect(anvil_clnt, service, addr,
+/* count, rate)
+/* ANVIL_CLNT *anvil_clnt;
+/* const char *service;
+/* const char *addr;
+/* int *count;
+/* int *rate;
+/*
+/* int anvil_clnt_mail(anvil_clnt, service, addr, msgs)
+/* ANVIL_CLNT *anvil_clnt;
+/* const char *service;
+/* const char *addr;
+/* int *msgs;
+/*
+/* int anvil_clnt_rcpt(anvil_clnt, service, addr, rcpts)
+/* ANVIL_CLNT *anvil_clnt;
+/* const char *service;
+/* const char *addr;
+/* int *rcpts;
+/*
+/* int anvil_clnt_newtls(anvil_clnt, service, addr, newtls)
+/* ANVIL_CLNT *anvil_clnt;
+/* const char *service;
+/* const char *addr;
+/* int *newtls;
+/*
+/* int anvil_clnt_newtls_stat(anvil_clnt, service, addr, newtls)
+/* ANVIL_CLNT *anvil_clnt;
+/* const char *service;
+/* const char *addr;
+/* int *newtls;
+/*
+/* int anvil_clnt_auth(anvil_clnt, service, addr, auths)
+/* ANVIL_CLNT *anvil_clnt;
+/* const char *service;
+/* const char *addr;
+/* int *auths;
+/*
+/* int anvil_clnt_disconnect(anvil_clnt, service, addr)
+/* ANVIL_CLNT *anvil_clnt;
+/* const char *service;
+/* const char *addr;
+/*
+/* int anvil_clnt_lookup(anvil_clnt, service, addr, count,
+/* rate, msgs, rcpts, ntls, auths)
+/* ANVIL_CLNT *anvil_clnt;
+/* const char *service;
+/* const char *addr;
+/* int *count;
+/* int *rate;
+/* int *msgs;
+/* int *rcpts;
+/* int *ntls;
+/* int *auths;
+/* DESCRIPTION
+/* anvil_clnt_create() instantiates a local anvil service
+/* client endpoint.
+/*
+/* anvil_clnt_connect() informs the anvil server that a
+/* remote client has connected, and returns the current
+/* connection count and connection rate for that remote client.
+/*
+/* anvil_clnt_mail() registers a MAIL FROM event and
+/* returns the current MAIL FROM rate for the specified remote
+/* client.
+/*
+/* anvil_clnt_rcpt() registers a RCPT TO event and
+/* returns the current RCPT TO rate for the specified remote
+/* client.
+/*
+/* anvil_clnt_newtls() registers a remote client request
+/* to negotiate a new (uncached) TLS session and returns the
+/* current newtls request rate for the specified remote client.
+/*
+/* anvil_clnt_newtls_stat() returns the current newtls request
+/* rate for the specified remote client.
+/*
+/* anvil_clnt_auth() registers an AUTH event and returns the
+/* current AUTH event rate for the specified remote client.
+/*
+/* anvil_clnt_disconnect() informs the anvil server that a remote
+/* client has disconnected.
+/*
+/* anvil_clnt_lookup() returns the current count and rate
+/* information for the specified client.
+/*
+/* anvil_clnt_free() destroys a local anvil service client
+/* endpoint.
+/*
+/* Arguments:
+/* .IP anvil_clnt
+/* Client rate control service handle.
+/* .IP service
+/* The service that the remote client is connected to.
+/* .IP addr
+/* Null terminated string that identifies the remote client.
+/* .IP count
+/* Pointer to storage for the current number of connections from
+/* this remote client.
+/* .IP rate
+/* Pointer to storage for the current connection rate for this
+/* remote client.
+/* .IP msgs
+/* Pointer to storage for the current message rate for this
+/* remote client.
+/* .IP rcpts
+/* Pointer to storage for the current recipient rate for this
+/* remote client.
+/* .IP newtls
+/* Pointer to storage for the current "new TLS session" rate
+/* for this remote client.
+/* .IP auths
+/* Pointer to storage for the current AUTH event rate for this
+/* remote client.
+/* DIAGNOSTICS
+/* The update and status query routines return
+/* ANVIL_STAT_OK in case of success, ANVIL_STAT_FAIL otherwise
+/* (either the communication with the server is broken or the
+/* server experienced a problem).
+/* SEE ALSO
+/* anvil(8), connection/rate limiting
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <msg.h>
+#include <attr_clnt.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <mail_params.h>
+#include <anvil_clnt.h>
+
+/* Application specific. */
+
+#define ANVIL_IDENT(service, addr) \
+ printable(concatenate(service, ":", addr, (char *) 0), '?')
+
+/* anvil_clnt_handshake - receive server protocol announcement */
+
+static int anvil_clnt_handshake(VSTREAM *stream)
+{
+ return (attr_scan_plain(stream, ATTR_FLAG_STRICT,
+ RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_ANVIL),
+ ATTR_TYPE_END));
+}
+
+/* anvil_clnt_create - instantiate connection rate service client */
+
+ANVIL_CLNT *anvil_clnt_create(void)
+{
+ ATTR_CLNT *anvil_clnt;
+
+ /*
+ * Use whatever IPC is preferred for internal use: UNIX-domain sockets or
+ * Solaris streams.
+ */
+#ifndef VAR_ANVIL_SERVICE
+ anvil_clnt = attr_clnt_create("local:" ANVIL_CLASS "/" ANVIL_SERVICE,
+ var_ipc_timeout, 0, 0);
+#else
+ anvil_clnt = attr_clnt_create(var_anvil_service, var_ipc_timeout, 0, 0);
+#endif
+ attr_clnt_control(anvil_clnt,
+ ATTR_CLNT_CTL_HANDSHAKE, anvil_clnt_handshake,
+ ATTR_CLNT_CTL_END);
+ return ((ANVIL_CLNT *) anvil_clnt);
+}
+
+/* anvil_clnt_free - destroy connection rate service client */
+
+void anvil_clnt_free(ANVIL_CLNT *anvil_clnt)
+{
+ attr_clnt_free((ATTR_CLNT *) anvil_clnt);
+}
+
+/* anvil_clnt_lookup - status query */
+
+int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr, int *count, int *rate,
+ int *msgs, int *rcpts, int *newtls, int *auths)
+{
+ char *ident = ANVIL_IDENT(service, addr);
+ int status;
+
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
+ ATTR_FLAG_NONE, /* Query attributes. */
+ SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_LOOKUP),
+ SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
+ ATTR_TYPE_END,
+ ATTR_FLAG_MISSING, /* Reply attributes. */
+ RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
+ RECV_ATTR_INT(ANVIL_ATTR_COUNT, count),
+ RECV_ATTR_INT(ANVIL_ATTR_RATE, rate),
+ RECV_ATTR_INT(ANVIL_ATTR_MAIL, msgs),
+ RECV_ATTR_INT(ANVIL_ATTR_RCPT, rcpts),
+ RECV_ATTR_INT(ANVIL_ATTR_NTLS, newtls),
+ RECV_ATTR_INT(ANVIL_ATTR_AUTH, auths),
+ ATTR_TYPE_END) != 7)
+ status = ANVIL_STAT_FAIL;
+ else if (status != ANVIL_STAT_OK)
+ status = ANVIL_STAT_FAIL;
+ myfree(ident);
+ return (status);
+}
+
+/* anvil_clnt_connect - heads-up and status query */
+
+int anvil_clnt_connect(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr, int *count, int *rate)
+{
+ char *ident = ANVIL_IDENT(service, addr);
+ int status;
+
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
+ ATTR_FLAG_NONE, /* Query attributes. */
+ SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_CONN),
+ SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
+ ATTR_TYPE_END,
+ ATTR_FLAG_MISSING, /* Reply attributes. */
+ RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
+ RECV_ATTR_INT(ANVIL_ATTR_COUNT, count),
+ RECV_ATTR_INT(ANVIL_ATTR_RATE, rate),
+ ATTR_TYPE_END) != 3)
+ status = ANVIL_STAT_FAIL;
+ else if (status != ANVIL_STAT_OK)
+ status = ANVIL_STAT_FAIL;
+ myfree(ident);
+ return (status);
+}
+
+/* anvil_clnt_mail - heads-up and status query */
+
+int anvil_clnt_mail(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr, int *msgs)
+{
+ char *ident = ANVIL_IDENT(service, addr);
+ int status;
+
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
+ ATTR_FLAG_NONE, /* Query attributes. */
+ SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_MAIL),
+ SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
+ ATTR_TYPE_END,
+ ATTR_FLAG_MISSING, /* Reply attributes. */
+ RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
+ RECV_ATTR_INT(ANVIL_ATTR_RATE, msgs),
+ ATTR_TYPE_END) != 2)
+ status = ANVIL_STAT_FAIL;
+ else if (status != ANVIL_STAT_OK)
+ status = ANVIL_STAT_FAIL;
+ myfree(ident);
+ return (status);
+}
+
+/* anvil_clnt_rcpt - heads-up and status query */
+
+int anvil_clnt_rcpt(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr, int *rcpts)
+{
+ char *ident = ANVIL_IDENT(service, addr);
+ int status;
+
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
+ ATTR_FLAG_NONE, /* Query attributes. */
+ SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_RCPT),
+ SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
+ ATTR_TYPE_END,
+ ATTR_FLAG_MISSING, /* Reply attributes. */
+ RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
+ RECV_ATTR_INT(ANVIL_ATTR_RATE, rcpts),
+ ATTR_TYPE_END) != 2)
+ status = ANVIL_STAT_FAIL;
+ else if (status != ANVIL_STAT_OK)
+ status = ANVIL_STAT_FAIL;
+ myfree(ident);
+ return (status);
+}
+
+/* anvil_clnt_newtls - heads-up and status query */
+
+int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr, int *newtls)
+{
+ char *ident = ANVIL_IDENT(service, addr);
+ int status;
+
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
+ ATTR_FLAG_NONE, /* Query attributes. */
+ SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_NTLS),
+ SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
+ ATTR_TYPE_END,
+ ATTR_FLAG_MISSING, /* Reply attributes. */
+ RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
+ RECV_ATTR_INT(ANVIL_ATTR_RATE, newtls),
+ ATTR_TYPE_END) != 2)
+ status = ANVIL_STAT_FAIL;
+ else if (status != ANVIL_STAT_OK)
+ status = ANVIL_STAT_FAIL;
+ myfree(ident);
+ return (status);
+}
+
+/* anvil_clnt_newtls_stat - status query */
+
+int anvil_clnt_newtls_stat(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr, int *newtls)
+{
+ char *ident = ANVIL_IDENT(service, addr);
+ int status;
+
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
+ ATTR_FLAG_NONE, /* Query attributes. */
+ SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_NTLS_STAT),
+ SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
+ ATTR_TYPE_END,
+ ATTR_FLAG_MISSING, /* Reply attributes. */
+ RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
+ RECV_ATTR_INT(ANVIL_ATTR_RATE, newtls),
+ ATTR_TYPE_END) != 2)
+ status = ANVIL_STAT_FAIL;
+ else if (status != ANVIL_STAT_OK)
+ status = ANVIL_STAT_FAIL;
+ myfree(ident);
+ return (status);
+}
+
+/* anvil_clnt_auth - heads-up and status query */
+
+int anvil_clnt_auth(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr, int *auths)
+{
+ char *ident = ANVIL_IDENT(service, addr);
+ int status;
+
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
+ ATTR_FLAG_NONE, /* Query attributes. */
+ SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_AUTH),
+ SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
+ ATTR_TYPE_END,
+ ATTR_FLAG_MISSING, /* Reply attributes. */
+ RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
+ RECV_ATTR_INT(ANVIL_ATTR_RATE, auths),
+ ATTR_TYPE_END) != 2)
+ status = ANVIL_STAT_FAIL;
+ else if (status != ANVIL_STAT_OK)
+ status = ANVIL_STAT_FAIL;
+ myfree(ident);
+ return (status);
+}
+
+/* anvil_clnt_disconnect - heads-up only */
+
+int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr)
+{
+ char *ident = ANVIL_IDENT(service, addr);
+ int status;
+
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
+ ATTR_FLAG_NONE, /* Query attributes. */
+ SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_DISC),
+ SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
+ ATTR_TYPE_END,
+ ATTR_FLAG_MISSING, /* Reply attributes. */
+ RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
+ ATTR_TYPE_END) != 1)
+ status = ANVIL_STAT_FAIL;
+ else if (status != ANVIL_STAT_OK)
+ status = ANVIL_STAT_FAIL;
+ myfree(ident);
+ return (status);
+}
+
+#ifdef TEST
+
+ /*
+ * Stand-alone client for testing.
+ */
+#include <unistd.h>
+#include <string.h>
+#include <msg_vstream.h>
+#include <mail_conf.h>
+#include <mail_params.h>
+#include <vstring_vstream.h>
+
+static void usage(void)
+{
+ vstream_printf("usage: "
+ ANVIL_REQ_CONN " service addr | "
+ ANVIL_REQ_DISC " service addr | "
+ ANVIL_REQ_MAIL " service addr | "
+ ANVIL_REQ_RCPT " service addr | "
+ ANVIL_REQ_NTLS " service addr | "
+ ANVIL_REQ_NTLS_STAT " service addr | "
+ ANVIL_REQ_AUTH " service addr | "
+ ANVIL_REQ_LOOKUP " service addr\n");
+}
+
+int main(int unused_argc, char **argv)
+{
+ VSTRING *inbuf = vstring_alloc(1);
+ char *bufp;
+ char *cmd;
+ ssize_t cmd_len;
+ char *service;
+ char *addr;
+ int count;
+ int rate;
+ int msgs;
+ int rcpts;
+ int newtls;
+ int auths;
+ ANVIL_CLNT *anvil;
+
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+
+ mail_conf_read();
+ msg_info("using config files in %s", var_config_dir);
+ if (chdir(var_queue_dir) < 0)
+ msg_fatal("chdir %s: %m", var_queue_dir);
+
+ msg_verbose++;
+
+ anvil = anvil_clnt_create();
+
+ while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) {
+ bufp = vstring_str(inbuf);
+ if ((cmd = mystrtok(&bufp, " ")) == 0 || *bufp == 0
+ || (service = mystrtok(&bufp, " ")) == 0 || *service == 0
+ || (addr = mystrtok(&bufp, " ")) == 0 || *addr == 0
+ || mystrtok(&bufp, " ") != 0) {
+ vstream_printf("bad command syntax\n");
+ usage();
+ vstream_fflush(VSTREAM_OUT);
+ continue;
+ }
+ cmd_len = strlen(cmd);
+ if (strncmp(cmd, ANVIL_REQ_CONN, cmd_len) == 0) {
+ if (anvil_clnt_connect(anvil, service, addr, &count, &rate) != ANVIL_STAT_OK)
+ msg_warn("error!");
+ else
+ vstream_printf("count=%d, rate=%d\n", count, rate);
+ } else if (strncmp(cmd, ANVIL_REQ_MAIL, cmd_len) == 0) {
+ if (anvil_clnt_mail(anvil, service, addr, &msgs) != ANVIL_STAT_OK)
+ msg_warn("error!");
+ else
+ vstream_printf("rate=%d\n", msgs);
+ } else if (strncmp(cmd, ANVIL_REQ_RCPT, cmd_len) == 0) {
+ if (anvil_clnt_rcpt(anvil, service, addr, &rcpts) != ANVIL_STAT_OK)
+ msg_warn("error!");
+ else
+ vstream_printf("rate=%d\n", rcpts);
+ } else if (strncmp(cmd, ANVIL_REQ_NTLS, cmd_len) == 0) {
+ if (anvil_clnt_newtls(anvil, service, addr, &newtls) != ANVIL_STAT_OK)
+ msg_warn("error!");
+ else
+ vstream_printf("rate=%d\n", newtls);
+ } else if (strncmp(cmd, ANVIL_REQ_AUTH, cmd_len) == 0) {
+ if (anvil_clnt_auth(anvil, service, addr, &auths) != ANVIL_STAT_OK)
+ msg_warn("error!");
+ else
+ vstream_printf("rate=%d\n", auths);
+ } else if (strncmp(cmd, ANVIL_REQ_NTLS_STAT, cmd_len) == 0) {
+ if (anvil_clnt_newtls_stat(anvil, service, addr, &newtls) != ANVIL_STAT_OK)
+ msg_warn("error!");
+ else
+ vstream_printf("rate=%d\n", newtls);
+ } else if (strncmp(cmd, ANVIL_REQ_DISC, cmd_len) == 0) {
+ if (anvil_clnt_disconnect(anvil, service, addr) != ANVIL_STAT_OK)
+ msg_warn("error!");
+ else
+ vstream_printf("OK\n");
+ } else if (strncmp(cmd, ANVIL_REQ_LOOKUP, cmd_len) == 0) {
+ if (anvil_clnt_lookup(anvil, service, addr, &count, &rate, &msgs,
+ &rcpts, &newtls, &auths) != ANVIL_STAT_OK)
+ msg_warn("error!");
+ else
+ vstream_printf("count=%d, rate=%d msgs=%d rcpts=%d newtls=%d "
+ "auths=%d\n", count, rate, msgs, rcpts, newtls,
+ auths);
+ } else {
+ vstream_printf("bad command: \"%s\"\n", cmd);
+ usage();
+ }
+ vstream_fflush(VSTREAM_OUT);
+ }
+ vstring_free(inbuf);
+ anvil_clnt_free(anvil);
+ return (0);
+}
+
+#endif