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