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