1/*++
2/* NAME
3/*	anvil_clnt 3
4/* SUMMARY
5/*	connection count and rate management client interface
6/* SYNOPSIS
7/*	#include <anvil_clnt.h>
8/*
9/*	ANVIL_CLNT *anvil_clnt_create(void)
10/*
11/*	void	anvil_clnt_free(anvil_clnt)
12/*	ANVIL_CLNT *anvil_clnt;
13/*
14/*	int	anvil_clnt_connect(anvil_clnt, service, addr,
15/*					count, rate)
16/*	ANVIL_CLNT *anvil_clnt;
17/*	const char *service;
18/*	const char *addr;
19/*	int	*count;
20/*	int	*rate;
21/*
22/*	int	anvil_clnt_mail(anvil_clnt, service, addr, msgs)
23/*	ANVIL_CLNT *anvil_clnt;
24/*	const char *service;
25/*	const char *addr;
26/*	int	*msgs;
27/*
28/*	int	anvil_clnt_rcpt(anvil_clnt, service, addr, rcpts)
29/*	ANVIL_CLNT *anvil_clnt;
30/*	const char *service;
31/*	const char *addr;
32/*	int	*rcpts;
33/*
34/*	int	anvil_clnt_newtls(anvil_clnt, service, addr, newtls)
35/*	ANVIL_CLNT *anvil_clnt;
36/*	const char *service;
37/*	const char *addr;
38/*	int	*newtls;
39/*
40/*	int	anvil_clnt_newtls_stat(anvil_clnt, service, addr, newtls)
41/*	ANVIL_CLNT *anvil_clnt;
42/*	const char *service;
43/*	const char *addr;
44/*	int	*newtls;
45/*
46/*	int	anvil_clnt_disconnect(anvil_clnt, service, addr)
47/*	ANVIL_CLNT *anvil_clnt;
48/*	const char *service;
49/*	const char *addr;
50/*
51/*	int	anvil_clnt_lookup(anvil_clnt, service, addr,
52/*					count, rate, msgs, rcpts)
53/*	ANVIL_CLNT *anvil_clnt;
54/*	const char *service;
55/*	const char *addr;
56/*	int	*count;
57/*	int	*rate;
58/*	int	*msgs;
59/*	int	*rcpts;
60/* DESCRIPTION
61/*	anvil_clnt_create() instantiates a local anvil service
62/*	client endpoint.
63/*
64/*	anvil_clnt_connect() informs the anvil server that a
65/*	remote client has connected, and returns the current
66/*	connection count and connection rate for that remote client.
67/*
68/*	anvil_clnt_mail() registers a MAIL FROM event and
69/*	returns the current MAIL FROM rate for the specified remote
70/*	client.
71/*
72/*	anvil_clnt_rcpt() registers a RCPT TO event and
73/*	returns the current RCPT TO rate for the specified remote
74/*	client.
75/*
76/*	anvil_clnt_newtls() registers a remote client request
77/*	to negotiate a new (uncached) TLS session and returns the
78/*	current newtls request rate for the specified remote client.
79/*
80/*	anvil_clnt_newtls_stat() returns the current newtls request
81/*	rate for the specified remote client.
82/*
83/*	anvil_clnt_disconnect() informs the anvil server that a remote
84/*	client has disconnected.
85/*
86/*	anvil_clnt_lookup() returns the current count and rate
87/*	information for the specified client.
88/*
89/*	anvil_clnt_free() destroys a local anvil service client
90/*	endpoint.
91/*
92/*	Arguments:
93/* .IP anvil_clnt
94/*	Client rate control service handle.
95/* .IP service
96/*	The service that the remote client is connected to.
97/* .IP addr
98/*	Null terminated string that identifies the remote client.
99/* .IP count
100/*	Pointer to storage for the current number of connections from
101/*	this remote client.
102/* .IP rate
103/*	Pointer to storage for the current connection rate for this
104/*	remote client.
105/* .IP msgs
106/*	Pointer to storage for the current message rate for this
107/*	remote client.
108/* .IP rcpts
109/*	Pointer to storage for the current recipient rate for this
110/*	remote client.
111/* .IP newtls
112/*	Pointer to storage for the current "new TLS session" rate
113/*	for this remote client.
114/* DIAGNOSTICS
115/*	The update and status query routines return
116/*	ANVIL_STAT_OK in case of success, ANVIL_STAT_FAIL otherwise
117/*	(either the communication with the server is broken or the
118/*	server experienced a problem).
119/* SEE ALSO
120/*	anvil(8), connection/rate limiting
121/* LICENSE
122/* .ad
123/* .fi
124/*	The Secure Mailer license must be distributed with this software.
125/* AUTHOR(S)
126/*	Wietse Venema
127/*	IBM T.J. Watson Research
128/*	P.O. Box 704
129/*	Yorktown Heights, NY 10598, USA
130/*--*/
131
132/* System library. */
133
134#include <sys_defs.h>
135
136/* Utility library. */
137
138#include <mymalloc.h>
139#include <msg.h>
140#include <attr_clnt.h>
141#include <stringops.h>
142
143/* Global library. */
144
145#include <mail_proto.h>
146#include <mail_params.h>
147#include <anvil_clnt.h>
148
149/* Application specific. */
150
151#define ANVIL_IDENT(service, addr) \
152    printable(concatenate(service, ":", addr, (char *) 0), '?')
153
154/* anvil_clnt_create - instantiate connection rate service client */
155
156ANVIL_CLNT *anvil_clnt_create(void)
157{
158    ATTR_CLNT *anvil_clnt;
159
160    /*
161     * Use whatever IPC is preferred for internal use: UNIX-domain sockets or
162     * Solaris streams.
163     */
164#ifndef VAR_ANVIL_SERVICE
165    anvil_clnt = attr_clnt_create("local:" ANVIL_CLASS "/" ANVIL_SERVICE,
166				  var_ipc_timeout, 0, 0);
167#else
168    anvil_clnt = attr_clnt_create(var_anvil_service, var_ipc_timeout, 0, 0);
169#endif
170    return ((ANVIL_CLNT *) anvil_clnt);
171}
172
173/* anvil_clnt_free - destroy connection rate service client */
174
175void    anvil_clnt_free(ANVIL_CLNT *anvil_clnt)
176{
177    attr_clnt_free((ATTR_CLNT *) anvil_clnt);
178}
179
180/* anvil_clnt_lookup - status query */
181
182int     anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service,
183			          const char *addr, int *count, int *rate,
184			          int *msgs, int *rcpts, int *newtls)
185{
186    char   *ident = ANVIL_IDENT(service, addr);
187    int     status;
188
189    if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
190			  ATTR_FLAG_NONE,	/* Query attributes. */
191			  ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_LOOKUP,
192			  ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
193			  ATTR_TYPE_END,
194			  ATTR_FLAG_MISSING,	/* Reply attributes. */
195			  ATTR_TYPE_INT, ANVIL_ATTR_STATUS, &status,
196			  ATTR_TYPE_INT, ANVIL_ATTR_COUNT, count,
197			  ATTR_TYPE_INT, ANVIL_ATTR_RATE, rate,
198			  ATTR_TYPE_INT, ANVIL_ATTR_MAIL, msgs,
199			  ATTR_TYPE_INT, ANVIL_ATTR_RCPT, rcpts,
200			  ATTR_TYPE_INT, ANVIL_ATTR_NTLS, newtls,
201			  ATTR_TYPE_END) != 6)
202	status = ANVIL_STAT_FAIL;
203    else if (status != ANVIL_STAT_OK)
204	status = ANVIL_STAT_FAIL;
205    myfree(ident);
206    return (status);
207}
208
209/* anvil_clnt_connect - heads-up and status query */
210
211int     anvil_clnt_connect(ANVIL_CLNT *anvil_clnt, const char *service,
212			           const char *addr, int *count, int *rate)
213{
214    char   *ident = ANVIL_IDENT(service, addr);
215    int     status;
216
217    if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
218			  ATTR_FLAG_NONE,	/* Query attributes. */
219			  ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_CONN,
220			  ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
221			  ATTR_TYPE_END,
222			  ATTR_FLAG_MISSING,	/* Reply attributes. */
223			  ATTR_TYPE_INT, ANVIL_ATTR_STATUS, &status,
224			  ATTR_TYPE_INT, ANVIL_ATTR_COUNT, count,
225			  ATTR_TYPE_INT, ANVIL_ATTR_RATE, rate,
226			  ATTR_TYPE_END) != 3)
227	status = ANVIL_STAT_FAIL;
228    else if (status != ANVIL_STAT_OK)
229	status = ANVIL_STAT_FAIL;
230    myfree(ident);
231    return (status);
232}
233
234/* anvil_clnt_mail - heads-up and status query */
235
236int     anvil_clnt_mail(ANVIL_CLNT *anvil_clnt, const char *service,
237			        const char *addr, int *msgs)
238{
239    char   *ident = ANVIL_IDENT(service, addr);
240    int     status;
241
242    if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
243			  ATTR_FLAG_NONE,	/* Query attributes. */
244			  ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_MAIL,
245			  ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
246			  ATTR_TYPE_END,
247			  ATTR_FLAG_MISSING,	/* Reply attributes. */
248			  ATTR_TYPE_INT, ANVIL_ATTR_STATUS, &status,
249			  ATTR_TYPE_INT, ANVIL_ATTR_RATE, msgs,
250			  ATTR_TYPE_END) != 2)
251	status = ANVIL_STAT_FAIL;
252    else if (status != ANVIL_STAT_OK)
253	status = ANVIL_STAT_FAIL;
254    myfree(ident);
255    return (status);
256}
257
258/* anvil_clnt_rcpt - heads-up and status query */
259
260int     anvil_clnt_rcpt(ANVIL_CLNT *anvil_clnt, const char *service,
261			        const char *addr, int *rcpts)
262{
263    char   *ident = ANVIL_IDENT(service, addr);
264    int     status;
265
266    if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
267			  ATTR_FLAG_NONE,	/* Query attributes. */
268			  ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_RCPT,
269			  ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
270			  ATTR_TYPE_END,
271			  ATTR_FLAG_MISSING,	/* Reply attributes. */
272			  ATTR_TYPE_INT, ANVIL_ATTR_STATUS, &status,
273			  ATTR_TYPE_INT, ANVIL_ATTR_RATE, rcpts,
274			  ATTR_TYPE_END) != 2)
275	status = ANVIL_STAT_FAIL;
276    else if (status != ANVIL_STAT_OK)
277	status = ANVIL_STAT_FAIL;
278    myfree(ident);
279    return (status);
280}
281
282/* anvil_clnt_newtls - heads-up and status query */
283
284int     anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service,
285			          const char *addr, int *newtls)
286{
287    char   *ident = ANVIL_IDENT(service, addr);
288    int     status;
289
290    if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
291			  ATTR_FLAG_NONE,	/* Query attributes. */
292			  ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_NTLS,
293			  ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
294			  ATTR_TYPE_END,
295			  ATTR_FLAG_MISSING,	/* Reply attributes. */
296			  ATTR_TYPE_INT, ANVIL_ATTR_STATUS, &status,
297			  ATTR_TYPE_INT, ANVIL_ATTR_RATE, newtls,
298			  ATTR_TYPE_END) != 2)
299	status = ANVIL_STAT_FAIL;
300    else if (status != ANVIL_STAT_OK)
301	status = ANVIL_STAT_FAIL;
302    myfree(ident);
303    return (status);
304}
305
306/* anvil_clnt_newtls_stat - status query */
307
308int     anvil_clnt_newtls_stat(ANVIL_CLNT *anvil_clnt, const char *service,
309			               const char *addr, int *newtls)
310{
311    char   *ident = ANVIL_IDENT(service, addr);
312    int     status;
313
314    if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
315			  ATTR_FLAG_NONE,	/* Query attributes. */
316			  ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_NTLS_STAT,
317			  ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
318			  ATTR_TYPE_END,
319			  ATTR_FLAG_MISSING,	/* Reply attributes. */
320			  ATTR_TYPE_INT, ANVIL_ATTR_STATUS, &status,
321			  ATTR_TYPE_INT, ANVIL_ATTR_RATE, newtls,
322			  ATTR_TYPE_END) != 2)
323	status = ANVIL_STAT_FAIL;
324    else if (status != ANVIL_STAT_OK)
325	status = ANVIL_STAT_FAIL;
326    myfree(ident);
327    return (status);
328}
329
330/* anvil_clnt_disconnect - heads-up only */
331
332int     anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service,
333			              const char *addr)
334{
335    char   *ident = ANVIL_IDENT(service, addr);
336    int     status;
337
338    if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
339			  ATTR_FLAG_NONE,	/* Query attributes. */
340			  ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_DISC,
341			  ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
342			  ATTR_TYPE_END,
343			  ATTR_FLAG_MISSING,	/* Reply attributes. */
344			  ATTR_TYPE_INT, ANVIL_ATTR_STATUS, &status,
345			  ATTR_TYPE_END) != 1)
346	status = ANVIL_STAT_FAIL;
347    else if (status != ANVIL_STAT_OK)
348	status = ANVIL_STAT_FAIL;
349    myfree(ident);
350    return (status);
351}
352
353#ifdef TEST
354
355 /*
356  * Stand-alone client for testing.
357  */
358#include <unistd.h>
359#include <string.h>
360#include <msg_vstream.h>
361#include <mail_conf.h>
362#include <mail_params.h>
363#include <vstring_vstream.h>
364
365static void usage(void)
366{
367    vstream_printf("usage: "
368		   ANVIL_REQ_CONN " service addr | "
369		   ANVIL_REQ_DISC " service addr | "
370		   ANVIL_REQ_MAIL " service addr | "
371		   ANVIL_REQ_RCPT " service addr | "
372		   ANVIL_REQ_NTLS " service addr | "
373		   ANVIL_REQ_NTLS_STAT " service addr | "
374		   ANVIL_REQ_LOOKUP " service addr\n");
375}
376
377int     main(int unused_argc, char **argv)
378{
379    VSTRING *inbuf = vstring_alloc(1);
380    char   *bufp;
381    char   *cmd;
382    ssize_t cmd_len;
383    char   *service;
384    char   *addr;
385    int     count;
386    int     rate;
387    int     msgs;
388    int     rcpts;
389    int     newtls;
390    ANVIL_CLNT *anvil;
391
392    msg_vstream_init(argv[0], VSTREAM_ERR);
393
394    mail_conf_read();
395    msg_info("using config files in %s", var_config_dir);
396    if (chdir(var_queue_dir) < 0)
397	msg_fatal("chdir %s: %m", var_queue_dir);
398
399    msg_verbose++;
400
401    anvil = anvil_clnt_create();
402
403    while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) {
404	bufp = vstring_str(inbuf);
405	if ((cmd = mystrtok(&bufp, " ")) == 0 || *bufp == 0
406	    || (service = mystrtok(&bufp, " ")) == 0 || *service == 0
407	    || (addr = mystrtok(&bufp, " ")) == 0 || *addr == 0
408	    || mystrtok(&bufp, " ") != 0) {
409	    vstream_printf("bad command syntax\n");
410	    usage();
411	    vstream_fflush(VSTREAM_OUT);
412	    continue;
413	}
414	cmd_len = strlen(cmd);
415	if (strncmp(cmd, ANVIL_REQ_CONN, cmd_len) == 0) {
416	    if (anvil_clnt_connect(anvil, service, addr, &count, &rate) != ANVIL_STAT_OK)
417		msg_warn("error!");
418	    else
419		vstream_printf("count=%d, rate=%d\n", count, rate);
420	} else if (strncmp(cmd, ANVIL_REQ_MAIL, cmd_len) == 0) {
421	    if (anvil_clnt_mail(anvil, service, addr, &msgs) != ANVIL_STAT_OK)
422		msg_warn("error!");
423	    else
424		vstream_printf("rate=%d\n", msgs);
425	} else if (strncmp(cmd, ANVIL_REQ_RCPT, cmd_len) == 0) {
426	    if (anvil_clnt_rcpt(anvil, service, addr, &rcpts) != ANVIL_STAT_OK)
427		msg_warn("error!");
428	    else
429		vstream_printf("rate=%d\n", rcpts);
430	} else if (strncmp(cmd, ANVIL_REQ_NTLS, cmd_len) == 0) {
431	    if (anvil_clnt_newtls(anvil, service, addr, &newtls) != ANVIL_STAT_OK)
432		msg_warn("error!");
433	    else
434		vstream_printf("rate=%d\n", newtls);
435	} else if (strncmp(cmd, ANVIL_REQ_NTLS_STAT, cmd_len) == 0) {
436	    if (anvil_clnt_newtls_stat(anvil, service, addr, &newtls) != ANVIL_STAT_OK)
437		msg_warn("error!");
438	    else
439		vstream_printf("rate=%d\n", newtls);
440	} else if (strncmp(cmd, ANVIL_REQ_DISC, cmd_len) == 0) {
441	    if (anvil_clnt_disconnect(anvil, service, addr) != ANVIL_STAT_OK)
442		msg_warn("error!");
443	    else
444		vstream_printf("OK\n");
445	} else if (strncmp(cmd, ANVIL_REQ_LOOKUP, cmd_len) == 0) {
446	    if (anvil_clnt_lookup(anvil, service, addr, &count, &rate,
447				  &msgs, &rcpts, &newtls) != ANVIL_STAT_OK)
448		msg_warn("error!");
449	    else
450		vstream_printf("count=%d, rate=%d msgs=%d rcpts=%d newtls=%d\n",
451			       count, rate, msgs, rcpts, newtls);
452	} else {
453	    vstream_printf("bad command: \"%s\"\n", cmd);
454	    usage();
455	}
456	vstream_fflush(VSTREAM_OUT);
457    }
458    vstring_free(inbuf);
459    anvil_clnt_free(anvil);
460    return (0);
461}
462
463#endif
464