1 /* $NetBSD: anvil_clnt.c,v 1.3 2020/03/18 19:05:16 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_create - instantiate connection rate service client */ 176 177 ANVIL_CLNT *anvil_clnt_create(void) 178 { 179 ATTR_CLNT *anvil_clnt; 180 181 /* 182 * Use whatever IPC is preferred for internal use: UNIX-domain sockets or 183 * Solaris streams. 184 */ 185 #ifndef VAR_ANVIL_SERVICE 186 anvil_clnt = attr_clnt_create("local:" ANVIL_CLASS "/" ANVIL_SERVICE, 187 var_ipc_timeout, 0, 0); 188 #else 189 anvil_clnt = attr_clnt_create(var_anvil_service, var_ipc_timeout, 0, 0); 190 #endif 191 return ((ANVIL_CLNT *) anvil_clnt); 192 } 193 194 /* anvil_clnt_free - destroy connection rate service client */ 195 196 void anvil_clnt_free(ANVIL_CLNT *anvil_clnt) 197 { 198 attr_clnt_free((ATTR_CLNT *) anvil_clnt); 199 } 200 201 /* anvil_clnt_lookup - status query */ 202 203 int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service, 204 const char *addr, int *count, int *rate, 205 int *msgs, int *rcpts, int *newtls, int *auths) 206 { 207 char *ident = ANVIL_IDENT(service, addr); 208 int status; 209 210 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, 211 ATTR_FLAG_NONE, /* Query attributes. */ 212 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_LOOKUP), 213 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident), 214 ATTR_TYPE_END, 215 ATTR_FLAG_MISSING, /* Reply attributes. */ 216 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status), 217 RECV_ATTR_INT(ANVIL_ATTR_COUNT, count), 218 RECV_ATTR_INT(ANVIL_ATTR_RATE, rate), 219 RECV_ATTR_INT(ANVIL_ATTR_MAIL, msgs), 220 RECV_ATTR_INT(ANVIL_ATTR_RCPT, rcpts), 221 RECV_ATTR_INT(ANVIL_ATTR_NTLS, newtls), 222 RECV_ATTR_INT(ANVIL_ATTR_AUTH, auths), 223 ATTR_TYPE_END) != 7) 224 status = ANVIL_STAT_FAIL; 225 else if (status != ANVIL_STAT_OK) 226 status = ANVIL_STAT_FAIL; 227 myfree(ident); 228 return (status); 229 } 230 231 /* anvil_clnt_connect - heads-up and status query */ 232 233 int anvil_clnt_connect(ANVIL_CLNT *anvil_clnt, const char *service, 234 const char *addr, int *count, int *rate) 235 { 236 char *ident = ANVIL_IDENT(service, addr); 237 int status; 238 239 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, 240 ATTR_FLAG_NONE, /* Query attributes. */ 241 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_CONN), 242 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident), 243 ATTR_TYPE_END, 244 ATTR_FLAG_MISSING, /* Reply attributes. */ 245 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status), 246 RECV_ATTR_INT(ANVIL_ATTR_COUNT, count), 247 RECV_ATTR_INT(ANVIL_ATTR_RATE, rate), 248 ATTR_TYPE_END) != 3) 249 status = ANVIL_STAT_FAIL; 250 else if (status != ANVIL_STAT_OK) 251 status = ANVIL_STAT_FAIL; 252 myfree(ident); 253 return (status); 254 } 255 256 /* anvil_clnt_mail - heads-up and status query */ 257 258 int anvil_clnt_mail(ANVIL_CLNT *anvil_clnt, const char *service, 259 const char *addr, int *msgs) 260 { 261 char *ident = ANVIL_IDENT(service, addr); 262 int status; 263 264 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, 265 ATTR_FLAG_NONE, /* Query attributes. */ 266 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_MAIL), 267 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident), 268 ATTR_TYPE_END, 269 ATTR_FLAG_MISSING, /* Reply attributes. */ 270 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status), 271 RECV_ATTR_INT(ANVIL_ATTR_RATE, msgs), 272 ATTR_TYPE_END) != 2) 273 status = ANVIL_STAT_FAIL; 274 else if (status != ANVIL_STAT_OK) 275 status = ANVIL_STAT_FAIL; 276 myfree(ident); 277 return (status); 278 } 279 280 /* anvil_clnt_rcpt - heads-up and status query */ 281 282 int anvil_clnt_rcpt(ANVIL_CLNT *anvil_clnt, const char *service, 283 const char *addr, int *rcpts) 284 { 285 char *ident = ANVIL_IDENT(service, addr); 286 int status; 287 288 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, 289 ATTR_FLAG_NONE, /* Query attributes. */ 290 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_RCPT), 291 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident), 292 ATTR_TYPE_END, 293 ATTR_FLAG_MISSING, /* Reply attributes. */ 294 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status), 295 RECV_ATTR_INT(ANVIL_ATTR_RATE, rcpts), 296 ATTR_TYPE_END) != 2) 297 status = ANVIL_STAT_FAIL; 298 else if (status != ANVIL_STAT_OK) 299 status = ANVIL_STAT_FAIL; 300 myfree(ident); 301 return (status); 302 } 303 304 /* anvil_clnt_newtls - heads-up and status query */ 305 306 int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service, 307 const char *addr, int *newtls) 308 { 309 char *ident = ANVIL_IDENT(service, addr); 310 int status; 311 312 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, 313 ATTR_FLAG_NONE, /* Query attributes. */ 314 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_NTLS), 315 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident), 316 ATTR_TYPE_END, 317 ATTR_FLAG_MISSING, /* Reply attributes. */ 318 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status), 319 RECV_ATTR_INT(ANVIL_ATTR_RATE, newtls), 320 ATTR_TYPE_END) != 2) 321 status = ANVIL_STAT_FAIL; 322 else if (status != ANVIL_STAT_OK) 323 status = ANVIL_STAT_FAIL; 324 myfree(ident); 325 return (status); 326 } 327 328 /* anvil_clnt_newtls_stat - status query */ 329 330 int anvil_clnt_newtls_stat(ANVIL_CLNT *anvil_clnt, const char *service, 331 const char *addr, int *newtls) 332 { 333 char *ident = ANVIL_IDENT(service, addr); 334 int status; 335 336 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, 337 ATTR_FLAG_NONE, /* Query attributes. */ 338 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_NTLS_STAT), 339 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident), 340 ATTR_TYPE_END, 341 ATTR_FLAG_MISSING, /* Reply attributes. */ 342 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status), 343 RECV_ATTR_INT(ANVIL_ATTR_RATE, newtls), 344 ATTR_TYPE_END) != 2) 345 status = ANVIL_STAT_FAIL; 346 else if (status != ANVIL_STAT_OK) 347 status = ANVIL_STAT_FAIL; 348 myfree(ident); 349 return (status); 350 } 351 352 /* anvil_clnt_auth - heads-up and status query */ 353 354 int anvil_clnt_auth(ANVIL_CLNT *anvil_clnt, const char *service, 355 const char *addr, int *auths) 356 { 357 char *ident = ANVIL_IDENT(service, addr); 358 int status; 359 360 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, 361 ATTR_FLAG_NONE, /* Query attributes. */ 362 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_AUTH), 363 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident), 364 ATTR_TYPE_END, 365 ATTR_FLAG_MISSING, /* Reply attributes. */ 366 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status), 367 RECV_ATTR_INT(ANVIL_ATTR_RATE, auths), 368 ATTR_TYPE_END) != 2) 369 status = ANVIL_STAT_FAIL; 370 else if (status != ANVIL_STAT_OK) 371 status = ANVIL_STAT_FAIL; 372 myfree(ident); 373 return (status); 374 } 375 376 /* anvil_clnt_disconnect - heads-up only */ 377 378 int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service, 379 const char *addr) 380 { 381 char *ident = ANVIL_IDENT(service, addr); 382 int status; 383 384 if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, 385 ATTR_FLAG_NONE, /* Query attributes. */ 386 SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_DISC), 387 SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident), 388 ATTR_TYPE_END, 389 ATTR_FLAG_MISSING, /* Reply attributes. */ 390 RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status), 391 ATTR_TYPE_END) != 1) 392 status = ANVIL_STAT_FAIL; 393 else if (status != ANVIL_STAT_OK) 394 status = ANVIL_STAT_FAIL; 395 myfree(ident); 396 return (status); 397 } 398 399 #ifdef TEST 400 401 /* 402 * Stand-alone client for testing. 403 */ 404 #include <unistd.h> 405 #include <string.h> 406 #include <msg_vstream.h> 407 #include <mail_conf.h> 408 #include <mail_params.h> 409 #include <vstring_vstream.h> 410 411 static void usage(void) 412 { 413 vstream_printf("usage: " 414 ANVIL_REQ_CONN " service addr | " 415 ANVIL_REQ_DISC " service addr | " 416 ANVIL_REQ_MAIL " service addr | " 417 ANVIL_REQ_RCPT " service addr | " 418 ANVIL_REQ_NTLS " service addr | " 419 ANVIL_REQ_NTLS_STAT " service addr | " 420 ANVIL_REQ_AUTH " service addr | " 421 ANVIL_REQ_LOOKUP " service addr\n"); 422 } 423 424 int main(int unused_argc, char **argv) 425 { 426 VSTRING *inbuf = vstring_alloc(1); 427 char *bufp; 428 char *cmd; 429 ssize_t cmd_len; 430 char *service; 431 char *addr; 432 int count; 433 int rate; 434 int msgs; 435 int rcpts; 436 int newtls; 437 int auths; 438 ANVIL_CLNT *anvil; 439 440 msg_vstream_init(argv[0], VSTREAM_ERR); 441 442 mail_conf_read(); 443 msg_info("using config files in %s", var_config_dir); 444 if (chdir(var_queue_dir) < 0) 445 msg_fatal("chdir %s: %m", var_queue_dir); 446 447 msg_verbose++; 448 449 anvil = anvil_clnt_create(); 450 451 while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) { 452 bufp = vstring_str(inbuf); 453 if ((cmd = mystrtok(&bufp, " ")) == 0 || *bufp == 0 454 || (service = mystrtok(&bufp, " ")) == 0 || *service == 0 455 || (addr = mystrtok(&bufp, " ")) == 0 || *addr == 0 456 || mystrtok(&bufp, " ") != 0) { 457 vstream_printf("bad command syntax\n"); 458 usage(); 459 vstream_fflush(VSTREAM_OUT); 460 continue; 461 } 462 cmd_len = strlen(cmd); 463 if (strncmp(cmd, ANVIL_REQ_CONN, cmd_len) == 0) { 464 if (anvil_clnt_connect(anvil, service, addr, &count, &rate) != ANVIL_STAT_OK) 465 msg_warn("error!"); 466 else 467 vstream_printf("count=%d, rate=%d\n", count, rate); 468 } else if (strncmp(cmd, ANVIL_REQ_MAIL, cmd_len) == 0) { 469 if (anvil_clnt_mail(anvil, service, addr, &msgs) != ANVIL_STAT_OK) 470 msg_warn("error!"); 471 else 472 vstream_printf("rate=%d\n", msgs); 473 } else if (strncmp(cmd, ANVIL_REQ_RCPT, cmd_len) == 0) { 474 if (anvil_clnt_rcpt(anvil, service, addr, &rcpts) != ANVIL_STAT_OK) 475 msg_warn("error!"); 476 else 477 vstream_printf("rate=%d\n", rcpts); 478 } else if (strncmp(cmd, ANVIL_REQ_NTLS, cmd_len) == 0) { 479 if (anvil_clnt_newtls(anvil, service, addr, &newtls) != ANVIL_STAT_OK) 480 msg_warn("error!"); 481 else 482 vstream_printf("rate=%d\n", newtls); 483 } else if (strncmp(cmd, ANVIL_REQ_AUTH, cmd_len) == 0) { 484 if (anvil_clnt_auth(anvil, service, addr, &auths) != ANVIL_STAT_OK) 485 msg_warn("error!"); 486 else 487 vstream_printf("rate=%d\n", auths); 488 } else if (strncmp(cmd, ANVIL_REQ_NTLS_STAT, cmd_len) == 0) { 489 if (anvil_clnt_newtls_stat(anvil, service, addr, &newtls) != ANVIL_STAT_OK) 490 msg_warn("error!"); 491 else 492 vstream_printf("rate=%d\n", newtls); 493 } else if (strncmp(cmd, ANVIL_REQ_DISC, cmd_len) == 0) { 494 if (anvil_clnt_disconnect(anvil, service, addr) != ANVIL_STAT_OK) 495 msg_warn("error!"); 496 else 497 vstream_printf("OK\n"); 498 } else if (strncmp(cmd, ANVIL_REQ_LOOKUP, cmd_len) == 0) { 499 if (anvil_clnt_lookup(anvil, service, addr, &count, &rate, &msgs, 500 &rcpts, &newtls, &auths) != ANVIL_STAT_OK) 501 msg_warn("error!"); 502 else 503 vstream_printf("count=%d, rate=%d msgs=%d rcpts=%d newtls=%d " 504 "auths=%d\n", count, rate, msgs, rcpts, newtls, 505 auths); 506 } else { 507 vstream_printf("bad command: \"%s\"\n", cmd); 508 usage(); 509 } 510 vstream_fflush(VSTREAM_OUT); 511 } 512 vstring_free(inbuf); 513 anvil_clnt_free(anvil); 514 return (0); 515 } 516 517 #endif 518