Home | History | Annotate | Line # | Download | only in global
      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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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