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