1 /* $NetBSD: anvil_clnt.c,v 1.4 2022/10/08 16:12:45 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_handshake - receive server protocol announcement */
176
anvil_clnt_handshake(VSTREAM * stream)177 static int anvil_clnt_handshake(VSTREAM *stream)
178 {
179 return (attr_scan_plain(stream, ATTR_FLAG_STRICT,
180 RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_ANVIL),
181 ATTR_TYPE_END));
182 }
183
184 /* anvil_clnt_create - instantiate connection rate service client */
185
anvil_clnt_create(void)186 ANVIL_CLNT *anvil_clnt_create(void)
187 {
188 ATTR_CLNT *anvil_clnt;
189
190 /*
191 * Use whatever IPC is preferred for internal use: UNIX-domain sockets or
192 * Solaris streams.
193 */
194 #ifndef VAR_ANVIL_SERVICE
195 anvil_clnt = attr_clnt_create("local:" ANVIL_CLASS "/" ANVIL_SERVICE,
196 var_ipc_timeout, 0, 0);
197 #else
198 anvil_clnt = attr_clnt_create(var_anvil_service, var_ipc_timeout, 0, 0);
199 #endif
200 attr_clnt_control(anvil_clnt,
201 ATTR_CLNT_CTL_HANDSHAKE, anvil_clnt_handshake,
202 ATTR_CLNT_CTL_END);
203 return ((ANVIL_CLNT *) anvil_clnt);
204 }
205
206 /* anvil_clnt_free - destroy connection rate service client */
207
anvil_clnt_free(ANVIL_CLNT * anvil_clnt)208 void anvil_clnt_free(ANVIL_CLNT *anvil_clnt)
209 {
210 attr_clnt_free((ATTR_CLNT *) anvil_clnt);
211 }
212
213 /* anvil_clnt_lookup - status query */
214
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)215 int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service,
216 const char *addr, int *count, int *rate,
217 int *msgs, int *rcpts, int *newtls, int *auths)
218 {
219 char *ident = ANVIL_IDENT(service, addr);
220 int status;
221
222 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
223 ATTR_FLAG_NONE, /* Query attributes. */
224 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_LOOKUP),
225 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
226 ATTR_TYPE_END,
227 ATTR_FLAG_MISSING, /* Reply attributes. */
228 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
229 RECV_ATTR_INT(ANVIL_ATTR_COUNT, count),
230 RECV_ATTR_INT(ANVIL_ATTR_RATE, rate),
231 RECV_ATTR_INT(ANVIL_ATTR_MAIL, msgs),
232 RECV_ATTR_INT(ANVIL_ATTR_RCPT, rcpts),
233 RECV_ATTR_INT(ANVIL_ATTR_NTLS, newtls),
234 RECV_ATTR_INT(ANVIL_ATTR_AUTH, auths),
235 ATTR_TYPE_END) != 7)
236 status = ANVIL_STAT_FAIL;
237 else if (status != ANVIL_STAT_OK)
238 status = ANVIL_STAT_FAIL;
239 myfree(ident);
240 return (status);
241 }
242
243 /* anvil_clnt_connect - heads-up and status query */
244
anvil_clnt_connect(ANVIL_CLNT * anvil_clnt,const char * service,const char * addr,int * count,int * rate)245 int anvil_clnt_connect(ANVIL_CLNT *anvil_clnt, const char *service,
246 const char *addr, int *count, int *rate)
247 {
248 char *ident = ANVIL_IDENT(service, addr);
249 int status;
250
251 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
252 ATTR_FLAG_NONE, /* Query attributes. */
253 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_CONN),
254 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
255 ATTR_TYPE_END,
256 ATTR_FLAG_MISSING, /* Reply attributes. */
257 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
258 RECV_ATTR_INT(ANVIL_ATTR_COUNT, count),
259 RECV_ATTR_INT(ANVIL_ATTR_RATE, rate),
260 ATTR_TYPE_END) != 3)
261 status = ANVIL_STAT_FAIL;
262 else if (status != ANVIL_STAT_OK)
263 status = ANVIL_STAT_FAIL;
264 myfree(ident);
265 return (status);
266 }
267
268 /* anvil_clnt_mail - heads-up and status query */
269
anvil_clnt_mail(ANVIL_CLNT * anvil_clnt,const char * service,const char * addr,int * msgs)270 int anvil_clnt_mail(ANVIL_CLNT *anvil_clnt, const char *service,
271 const char *addr, int *msgs)
272 {
273 char *ident = ANVIL_IDENT(service, addr);
274 int status;
275
276 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
277 ATTR_FLAG_NONE, /* Query attributes. */
278 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_MAIL),
279 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
280 ATTR_TYPE_END,
281 ATTR_FLAG_MISSING, /* Reply attributes. */
282 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
283 RECV_ATTR_INT(ANVIL_ATTR_RATE, msgs),
284 ATTR_TYPE_END) != 2)
285 status = ANVIL_STAT_FAIL;
286 else if (status != ANVIL_STAT_OK)
287 status = ANVIL_STAT_FAIL;
288 myfree(ident);
289 return (status);
290 }
291
292 /* anvil_clnt_rcpt - heads-up and status query */
293
anvil_clnt_rcpt(ANVIL_CLNT * anvil_clnt,const char * service,const char * addr,int * rcpts)294 int anvil_clnt_rcpt(ANVIL_CLNT *anvil_clnt, const char *service,
295 const char *addr, int *rcpts)
296 {
297 char *ident = ANVIL_IDENT(service, addr);
298 int status;
299
300 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
301 ATTR_FLAG_NONE, /* Query attributes. */
302 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_RCPT),
303 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
304 ATTR_TYPE_END,
305 ATTR_FLAG_MISSING, /* Reply attributes. */
306 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
307 RECV_ATTR_INT(ANVIL_ATTR_RATE, rcpts),
308 ATTR_TYPE_END) != 2)
309 status = ANVIL_STAT_FAIL;
310 else if (status != ANVIL_STAT_OK)
311 status = ANVIL_STAT_FAIL;
312 myfree(ident);
313 return (status);
314 }
315
316 /* anvil_clnt_newtls - heads-up and status query */
317
anvil_clnt_newtls(ANVIL_CLNT * anvil_clnt,const char * service,const char * addr,int * newtls)318 int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service,
319 const char *addr, int *newtls)
320 {
321 char *ident = ANVIL_IDENT(service, addr);
322 int status;
323
324 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
325 ATTR_FLAG_NONE, /* Query attributes. */
326 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_NTLS),
327 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
328 ATTR_TYPE_END,
329 ATTR_FLAG_MISSING, /* Reply attributes. */
330 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
331 RECV_ATTR_INT(ANVIL_ATTR_RATE, newtls),
332 ATTR_TYPE_END) != 2)
333 status = ANVIL_STAT_FAIL;
334 else if (status != ANVIL_STAT_OK)
335 status = ANVIL_STAT_FAIL;
336 myfree(ident);
337 return (status);
338 }
339
340 /* anvil_clnt_newtls_stat - status query */
341
anvil_clnt_newtls_stat(ANVIL_CLNT * anvil_clnt,const char * service,const char * addr,int * newtls)342 int anvil_clnt_newtls_stat(ANVIL_CLNT *anvil_clnt, const char *service,
343 const char *addr, int *newtls)
344 {
345 char *ident = ANVIL_IDENT(service, addr);
346 int status;
347
348 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
349 ATTR_FLAG_NONE, /* Query attributes. */
350 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_NTLS_STAT),
351 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
352 ATTR_TYPE_END,
353 ATTR_FLAG_MISSING, /* Reply attributes. */
354 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
355 RECV_ATTR_INT(ANVIL_ATTR_RATE, newtls),
356 ATTR_TYPE_END) != 2)
357 status = ANVIL_STAT_FAIL;
358 else if (status != ANVIL_STAT_OK)
359 status = ANVIL_STAT_FAIL;
360 myfree(ident);
361 return (status);
362 }
363
364 /* anvil_clnt_auth - heads-up and status query */
365
anvil_clnt_auth(ANVIL_CLNT * anvil_clnt,const char * service,const char * addr,int * auths)366 int anvil_clnt_auth(ANVIL_CLNT *anvil_clnt, const char *service,
367 const char *addr, int *auths)
368 {
369 char *ident = ANVIL_IDENT(service, addr);
370 int status;
371
372 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
373 ATTR_FLAG_NONE, /* Query attributes. */
374 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_AUTH),
375 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
376 ATTR_TYPE_END,
377 ATTR_FLAG_MISSING, /* Reply attributes. */
378 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
379 RECV_ATTR_INT(ANVIL_ATTR_RATE, auths),
380 ATTR_TYPE_END) != 2)
381 status = ANVIL_STAT_FAIL;
382 else if (status != ANVIL_STAT_OK)
383 status = ANVIL_STAT_FAIL;
384 myfree(ident);
385 return (status);
386 }
387
388 /* anvil_clnt_disconnect - heads-up only */
389
anvil_clnt_disconnect(ANVIL_CLNT * anvil_clnt,const char * service,const char * addr)390 int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service,
391 const char *addr)
392 {
393 char *ident = ANVIL_IDENT(service, addr);
394 int status;
395
396 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
397 ATTR_FLAG_NONE, /* Query attributes. */
398 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_DISC),
399 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
400 ATTR_TYPE_END,
401 ATTR_FLAG_MISSING, /* Reply attributes. */
402 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
403 ATTR_TYPE_END) != 1)
404 status = ANVIL_STAT_FAIL;
405 else if (status != ANVIL_STAT_OK)
406 status = ANVIL_STAT_FAIL;
407 myfree(ident);
408 return (status);
409 }
410
411 #ifdef TEST
412
413 /*
414 * Stand-alone client for testing.
415 */
416 #include <unistd.h>
417 #include <string.h>
418 #include <msg_vstream.h>
419 #include <mail_conf.h>
420 #include <mail_params.h>
421 #include <vstring_vstream.h>
422
usage(void)423 static void usage(void)
424 {
425 vstream_printf("usage: "
426 ANVIL_REQ_CONN " service addr | "
427 ANVIL_REQ_DISC " service addr | "
428 ANVIL_REQ_MAIL " service addr | "
429 ANVIL_REQ_RCPT " service addr | "
430 ANVIL_REQ_NTLS " service addr | "
431 ANVIL_REQ_NTLS_STAT " service addr | "
432 ANVIL_REQ_AUTH " service addr | "
433 ANVIL_REQ_LOOKUP " service addr\n");
434 }
435
main(int unused_argc,char ** argv)436 int main(int unused_argc, char **argv)
437 {
438 VSTRING *inbuf = vstring_alloc(1);
439 char *bufp;
440 char *cmd;
441 ssize_t cmd_len;
442 char *service;
443 char *addr;
444 int count;
445 int rate;
446 int msgs;
447 int rcpts;
448 int newtls;
449 int auths;
450 ANVIL_CLNT *anvil;
451
452 msg_vstream_init(argv[0], VSTREAM_ERR);
453
454 mail_conf_read();
455 msg_info("using config files in %s", var_config_dir);
456 if (chdir(var_queue_dir) < 0)
457 msg_fatal("chdir %s: %m", var_queue_dir);
458
459 msg_verbose++;
460
461 anvil = anvil_clnt_create();
462
463 while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) {
464 bufp = vstring_str(inbuf);
465 if ((cmd = mystrtok(&bufp, " ")) == 0 || *bufp == 0
466 || (service = mystrtok(&bufp, " ")) == 0 || *service == 0
467 || (addr = mystrtok(&bufp, " ")) == 0 || *addr == 0
468 || mystrtok(&bufp, " ") != 0) {
469 vstream_printf("bad command syntax\n");
470 usage();
471 vstream_fflush(VSTREAM_OUT);
472 continue;
473 }
474 cmd_len = strlen(cmd);
475 if (strncmp(cmd, ANVIL_REQ_CONN, cmd_len) == 0) {
476 if (anvil_clnt_connect(anvil, service, addr, &count, &rate) != ANVIL_STAT_OK)
477 msg_warn("error!");
478 else
479 vstream_printf("count=%d, rate=%d\n", count, rate);
480 } else if (strncmp(cmd, ANVIL_REQ_MAIL, cmd_len) == 0) {
481 if (anvil_clnt_mail(anvil, service, addr, &msgs) != ANVIL_STAT_OK)
482 msg_warn("error!");
483 else
484 vstream_printf("rate=%d\n", msgs);
485 } else if (strncmp(cmd, ANVIL_REQ_RCPT, cmd_len) == 0) {
486 if (anvil_clnt_rcpt(anvil, service, addr, &rcpts) != ANVIL_STAT_OK)
487 msg_warn("error!");
488 else
489 vstream_printf("rate=%d\n", rcpts);
490 } else if (strncmp(cmd, ANVIL_REQ_NTLS, cmd_len) == 0) {
491 if (anvil_clnt_newtls(anvil, service, addr, &newtls) != ANVIL_STAT_OK)
492 msg_warn("error!");
493 else
494 vstream_printf("rate=%d\n", newtls);
495 } else if (strncmp(cmd, ANVIL_REQ_AUTH, cmd_len) == 0) {
496 if (anvil_clnt_auth(anvil, service, addr, &auths) != ANVIL_STAT_OK)
497 msg_warn("error!");
498 else
499 vstream_printf("rate=%d\n", auths);
500 } else if (strncmp(cmd, ANVIL_REQ_NTLS_STAT, cmd_len) == 0) {
501 if (anvil_clnt_newtls_stat(anvil, service, addr, &newtls) != ANVIL_STAT_OK)
502 msg_warn("error!");
503 else
504 vstream_printf("rate=%d\n", newtls);
505 } else if (strncmp(cmd, ANVIL_REQ_DISC, cmd_len) == 0) {
506 if (anvil_clnt_disconnect(anvil, service, addr) != ANVIL_STAT_OK)
507 msg_warn("error!");
508 else
509 vstream_printf("OK\n");
510 } else if (strncmp(cmd, ANVIL_REQ_LOOKUP, cmd_len) == 0) {
511 if (anvil_clnt_lookup(anvil, service, addr, &count, &rate, &msgs,
512 &rcpts, &newtls, &auths) != ANVIL_STAT_OK)
513 msg_warn("error!");
514 else
515 vstream_printf("count=%d, rate=%d msgs=%d rcpts=%d newtls=%d "
516 "auths=%d\n", count, rate, msgs, rcpts, newtls,
517 auths);
518 } else {
519 vstream_printf("bad command: \"%s\"\n", cmd);
520 usage();
521 }
522 vstream_fflush(VSTREAM_OUT);
523 }
524 vstring_free(inbuf);
525 anvil_clnt_free(anvil);
526 return (0);
527 }
528
529 #endif
530