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