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