xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/anvil_clnt.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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