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