/*++ /* NAME /* anvil_clnt 3 /* SUMMARY /* connection count and rate management client interface /* SYNOPSIS /* #include /* /* 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 /* Utility library. */ #include #include #include #include /* Global library. */ #include #include #include /* 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 #include #include #include #include #include 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