1 /* 2 * Copyright (c) 1999-2004 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * 5 * By using this file, you agree to the terms and conditions set 6 * forth in the LICENSE file which can be found at the top level of 7 * the sendmail distribution. 8 * 9 */ 10 11 #pragma ident "%Z%%M% %I% %E% SMI" 12 13 #include <sm/gen.h> 14 SM_RCSID("@(#)$Id: smfi.c,v 8.73 2004/09/20 21:26:57 ca Exp $") 15 #include <sm/varargs.h> 16 #include "libmilter.h" 17 18 static int smfi_header __P((SMFICTX *, int, int, char *, char *)); 19 static int myisenhsc __P((const char *, int)); 20 21 /* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */ 22 #define MAXREPLYLEN 980 /* max. length of a reply string */ 23 #define MAXREPLIES 32 /* max. number of reply strings */ 24 25 /* 26 ** SMFI_HEADER -- send a header to the MTA 27 ** 28 ** Parameters: 29 ** ctx -- Opaque context structure 30 ** cmd -- Header modification command 31 ** hdridx -- Header index 32 ** headerf -- Header field name 33 ** headerv -- Header field value 34 ** 35 ** 36 ** Returns: 37 ** MI_SUCCESS/MI_FAILURE 38 */ 39 40 static int 41 smfi_header(ctx, cmd, hdridx, headerf, headerv) 42 SMFICTX *ctx; 43 int cmd; 44 int hdridx; 45 char *headerf; 46 char *headerv; 47 { 48 size_t len, l1, l2, offset; 49 int r; 50 mi_int32 v; 51 char *buf; 52 struct timeval timeout; 53 54 if (headerf == NULL || *headerf == '\0' || headerv == NULL) 55 return MI_FAILURE; 56 timeout.tv_sec = ctx->ctx_timeout; 57 timeout.tv_usec = 0; 58 l1 = strlen(headerf) + 1; 59 l2 = strlen(headerv) + 1; 60 len = l1 + l2; 61 if (hdridx >= 0) 62 len += MILTER_LEN_BYTES; 63 buf = malloc(len); 64 if (buf == NULL) 65 return MI_FAILURE; 66 offset = 0; 67 if (hdridx >= 0) 68 { 69 v = htonl(hdridx); 70 (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES); 71 offset += MILTER_LEN_BYTES; 72 } 73 (void) memcpy(buf + offset, headerf, l1); 74 (void) memcpy(buf + offset + l1, headerv, l2); 75 r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len); 76 free(buf); 77 return r; 78 } 79 80 /* 81 ** SMFI_ADDHEADER -- send a new header to the MTA 82 ** 83 ** Parameters: 84 ** ctx -- Opaque context structure 85 ** headerf -- Header field name 86 ** headerv -- Header field value 87 ** 88 ** Returns: 89 ** MI_SUCCESS/MI_FAILURE 90 */ 91 92 int 93 smfi_addheader(ctx, headerf, headerv) 94 SMFICTX *ctx; 95 char *headerf; 96 char *headerv; 97 { 98 if (!mi_sendok(ctx, SMFIF_ADDHDRS)) 99 return MI_FAILURE; 100 101 return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv); 102 } 103 104 /* 105 ** SMFI_INSHEADER -- send a new header to the MTA (to be inserted) 106 ** 107 ** Parameters: 108 ** ctx -- Opaque context structure 109 ** hdridx -- index into header list where insertion should occur 110 ** headerf -- Header field name 111 ** headerv -- Header field value 112 ** 113 ** Returns: 114 ** MI_SUCCESS/MI_FAILURE 115 */ 116 117 int 118 smfi_insheader(ctx, hdridx, headerf, headerv) 119 SMFICTX *ctx; 120 int hdridx; 121 char *headerf; 122 char *headerv; 123 { 124 if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0) 125 return MI_FAILURE; 126 127 return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv); 128 } 129 130 /* 131 ** SMFI_CHGHEADER -- send a changed header to the MTA 132 ** 133 ** Parameters: 134 ** ctx -- Opaque context structure 135 ** headerf -- Header field name 136 ** hdridx -- Header index value 137 ** headerv -- Header field value 138 ** 139 ** Returns: 140 ** MI_SUCCESS/MI_FAILURE 141 */ 142 143 int 144 smfi_chgheader(ctx, headerf, hdridx, headerv) 145 SMFICTX *ctx; 146 char *headerf; 147 mi_int32 hdridx; 148 char *headerv; 149 { 150 if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0) 151 return MI_FAILURE; 152 if (headerv == NULL) 153 headerv = ""; 154 155 return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv); 156 } 157 158 /* 159 ** SMFI_ADDRCPT -- send an additional recipient to the MTA 160 ** 161 ** Parameters: 162 ** ctx -- Opaque context structure 163 ** rcpt -- recipient address 164 ** 165 ** Returns: 166 ** MI_SUCCESS/MI_FAILURE 167 */ 168 169 int 170 smfi_addrcpt(ctx, rcpt) 171 SMFICTX *ctx; 172 char *rcpt; 173 { 174 size_t len; 175 struct timeval timeout; 176 177 if (rcpt == NULL || *rcpt == '\0') 178 return MI_FAILURE; 179 if (!mi_sendok(ctx, SMFIF_ADDRCPT)) 180 return MI_FAILURE; 181 timeout.tv_sec = ctx->ctx_timeout; 182 timeout.tv_usec = 0; 183 len = strlen(rcpt) + 1; 184 return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len); 185 } 186 187 /* 188 ** SMFI_DELRCPT -- send a recipient to be removed to the MTA 189 ** 190 ** Parameters: 191 ** ctx -- Opaque context structure 192 ** rcpt -- recipient address 193 ** 194 ** Returns: 195 ** MI_SUCCESS/MI_FAILURE 196 */ 197 198 int 199 smfi_delrcpt(ctx, rcpt) 200 SMFICTX *ctx; 201 char *rcpt; 202 { 203 size_t len; 204 struct timeval timeout; 205 206 if (rcpt == NULL || *rcpt == '\0') 207 return MI_FAILURE; 208 if (!mi_sendok(ctx, SMFIF_DELRCPT)) 209 return MI_FAILURE; 210 timeout.tv_sec = ctx->ctx_timeout; 211 timeout.tv_usec = 0; 212 len = strlen(rcpt) + 1; 213 return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len); 214 } 215 216 /* 217 ** SMFI_REPLACEBODY -- send a body chunk to the MTA 218 ** 219 ** Parameters: 220 ** ctx -- Opaque context structure 221 ** bodyp -- body chunk 222 ** bodylen -- length of body chunk 223 ** 224 ** Returns: 225 ** MI_SUCCESS/MI_FAILURE 226 */ 227 228 int 229 smfi_replacebody(ctx, bodyp, bodylen) 230 SMFICTX *ctx; 231 unsigned char *bodyp; 232 int bodylen; 233 { 234 int len, off, r; 235 struct timeval timeout; 236 237 if (bodylen < 0 || 238 (bodyp == NULL && bodylen > 0)) 239 return MI_FAILURE; 240 if (!mi_sendok(ctx, SMFIF_CHGBODY)) 241 return MI_FAILURE; 242 timeout.tv_sec = ctx->ctx_timeout; 243 timeout.tv_usec = 0; 244 245 /* split body chunk if necessary */ 246 off = 0; 247 while (bodylen > 0) 248 { 249 len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE : 250 bodylen; 251 if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY, 252 (char *) (bodyp + off), len)) != MI_SUCCESS) 253 return r; 254 off += len; 255 bodylen -= len; 256 } 257 return MI_SUCCESS; 258 } 259 260 /* 261 ** SMFI_QUARANTINE -- quarantine an envelope 262 ** 263 ** Parameters: 264 ** ctx -- Opaque context structure 265 ** reason -- why? 266 ** 267 ** Returns: 268 ** MI_SUCCESS/MI_FAILURE 269 */ 270 271 int 272 smfi_quarantine(ctx, reason) 273 SMFICTX *ctx; 274 char *reason; 275 { 276 size_t len; 277 int r; 278 char *buf; 279 struct timeval timeout; 280 281 if (reason == NULL || *reason == '\0') 282 return MI_FAILURE; 283 if (!mi_sendok(ctx, SMFIF_QUARANTINE)) 284 return MI_FAILURE; 285 timeout.tv_sec = ctx->ctx_timeout; 286 timeout.tv_usec = 0; 287 len = strlen(reason) + 1; 288 buf = malloc(len); 289 if (buf == NULL) 290 return MI_FAILURE; 291 (void) memcpy(buf, reason, len); 292 r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len); 293 free(buf); 294 return r; 295 } 296 297 /* 298 ** MYISENHSC -- check whether a string contains an enhanced status code 299 ** 300 ** Parameters: 301 ** s -- string with possible enhanced status code. 302 ** delim -- delim for enhanced status code. 303 ** 304 ** Returns: 305 ** 0 -- no enhanced status code. 306 ** >4 -- length of enhanced status code. 307 ** 308 ** Side Effects: 309 ** none. 310 */ 311 312 static int 313 myisenhsc(s, delim) 314 const char *s; 315 int delim; 316 { 317 int l, h; 318 319 if (s == NULL) 320 return 0; 321 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 322 return 0; 323 h = 0; 324 l = 2; 325 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 326 ++h; 327 if (h == 0 || s[l + h] != '.') 328 return 0; 329 l += h + 1; 330 h = 0; 331 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 332 ++h; 333 if (h == 0 || s[l + h] != delim) 334 return 0; 335 return l + h; 336 } 337 338 /* 339 ** SMFI_SETREPLY -- set the reply code for the next reply to the MTA 340 ** 341 ** Parameters: 342 ** ctx -- Opaque context structure 343 ** rcode -- The three-digit (RFC 821) SMTP reply code. 344 ** xcode -- The extended (RFC 2034) reply code. 345 ** message -- The text part of the SMTP reply. 346 ** 347 ** Returns: 348 ** MI_SUCCESS/MI_FAILURE 349 */ 350 351 int 352 smfi_setreply(ctx, rcode, xcode, message) 353 SMFICTX *ctx; 354 char *rcode; 355 char *xcode; 356 char *message; 357 { 358 size_t len; 359 char *buf; 360 361 if (rcode == NULL || ctx == NULL) 362 return MI_FAILURE; 363 364 /* ### <sp> \0 */ 365 len = strlen(rcode) + 2; 366 if (len != 5) 367 return MI_FAILURE; 368 if ((rcode[0] != '4' && rcode[0] != '5') || 369 !isascii(rcode[1]) || !isdigit(rcode[1]) || 370 !isascii(rcode[2]) || !isdigit(rcode[2])) 371 return MI_FAILURE; 372 if (xcode != NULL) 373 { 374 if (!myisenhsc(xcode, '\0')) 375 return MI_FAILURE; 376 len += strlen(xcode) + 1; 377 } 378 if (message != NULL) 379 { 380 size_t ml; 381 382 /* XXX check also for unprintable chars? */ 383 if (strpbrk(message, "\r\n") != NULL) 384 return MI_FAILURE; 385 ml = strlen(message); 386 if (ml > MAXREPLYLEN) 387 return MI_FAILURE; 388 len += ml + 1; 389 } 390 buf = malloc(len); 391 if (buf == NULL) 392 return MI_FAILURE; /* oops */ 393 (void) sm_strlcpy(buf, rcode, len); 394 (void) sm_strlcat(buf, " ", len); 395 if (xcode != NULL) 396 (void) sm_strlcat(buf, xcode, len); 397 if (message != NULL) 398 { 399 if (xcode != NULL) 400 (void) sm_strlcat(buf, " ", len); 401 (void) sm_strlcat(buf, message, len); 402 } 403 if (ctx->ctx_reply != NULL) 404 free(ctx->ctx_reply); 405 ctx->ctx_reply = buf; 406 return MI_SUCCESS; 407 } 408 409 /* 410 ** SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA 411 ** 412 ** Parameters: 413 ** ctx -- Opaque context structure 414 ** rcode -- The three-digit (RFC 821) SMTP reply code. 415 ** xcode -- The extended (RFC 2034) reply code. 416 ** txt, ... -- The text part of the SMTP reply, 417 ** MUST be terminated with NULL. 418 ** 419 ** Returns: 420 ** MI_SUCCESS/MI_FAILURE 421 */ 422 423 int 424 #if SM_VA_STD 425 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...) 426 #else /* SM_VA_STD */ 427 smfi_setmlreply(ctx, rcode, xcode, va_alist) 428 SMFICTX *ctx; 429 const char *rcode; 430 const char *xcode; 431 va_dcl 432 #endif /* SM_VA_STD */ 433 { 434 size_t len; 435 size_t rlen; 436 int args; 437 char *buf, *txt; 438 const char *xc; 439 char repl[16]; 440 SM_VA_LOCAL_DECL 441 442 if (rcode == NULL || ctx == NULL) 443 return MI_FAILURE; 444 445 /* ### <sp> */ 446 len = strlen(rcode) + 1; 447 if (len != 4) 448 return MI_FAILURE; 449 if ((rcode[0] != '4' && rcode[0] != '5') || 450 !isascii(rcode[1]) || !isdigit(rcode[1]) || 451 !isascii(rcode[2]) || !isdigit(rcode[2])) 452 return MI_FAILURE; 453 if (xcode != NULL) 454 { 455 if (!myisenhsc(xcode, '\0')) 456 return MI_FAILURE; 457 xc = xcode; 458 } 459 else 460 { 461 if (rcode[0] == '4') 462 xc = "4.0.0"; 463 else 464 xc = "5.0.0"; 465 } 466 467 /* add trailing space */ 468 len += strlen(xc) + 1; 469 rlen = len; 470 args = 0; 471 SM_VA_START(ap, xcode); 472 while ((txt = SM_VA_ARG(ap, char *)) != NULL) 473 { 474 size_t tl; 475 476 tl = strlen(txt); 477 if (tl > MAXREPLYLEN) 478 break; 479 480 /* this text, reply codes, \r\n */ 481 len += tl + 2 + rlen; 482 if (++args > MAXREPLIES) 483 break; 484 485 /* XXX check also for unprintable chars? */ 486 if (strpbrk(txt, "\r\n") != NULL) 487 break; 488 } 489 SM_VA_END(ap); 490 if (txt != NULL) 491 return MI_FAILURE; 492 493 /* trailing '\0' */ 494 ++len; 495 buf = malloc(len); 496 if (buf == NULL) 497 return MI_FAILURE; /* oops */ 498 (void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc); 499 (void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-", 500 xc, " "); 501 SM_VA_START(ap, xcode); 502 txt = SM_VA_ARG(ap, char *); 503 if (txt != NULL) 504 { 505 (void) sm_strlcat2(buf, " ", txt, len); 506 while ((txt = SM_VA_ARG(ap, char *)) != NULL) 507 { 508 if (--args <= 1) 509 repl[3] = ' '; 510 (void) sm_strlcat2(buf, "\r\n", repl, len); 511 (void) sm_strlcat(buf, txt, len); 512 } 513 } 514 if (ctx->ctx_reply != NULL) 515 free(ctx->ctx_reply); 516 ctx->ctx_reply = buf; 517 SM_VA_END(ap); 518 return MI_SUCCESS; 519 } 520 521 /* 522 ** SMFI_SETPRIV -- set private data 523 ** 524 ** Parameters: 525 ** ctx -- Opaque context structure 526 ** privatedata -- pointer to private data 527 ** 528 ** Returns: 529 ** MI_SUCCESS/MI_FAILURE 530 */ 531 532 int 533 smfi_setpriv(ctx, privatedata) 534 SMFICTX *ctx; 535 void *privatedata; 536 { 537 if (ctx == NULL) 538 return MI_FAILURE; 539 ctx->ctx_privdata = privatedata; 540 return MI_SUCCESS; 541 } 542 543 /* 544 ** SMFI_GETPRIV -- get private data 545 ** 546 ** Parameters: 547 ** ctx -- Opaque context structure 548 ** 549 ** Returns: 550 ** pointer to private data 551 */ 552 553 void * 554 smfi_getpriv(ctx) 555 SMFICTX *ctx; 556 { 557 if (ctx == NULL) 558 return NULL; 559 return ctx->ctx_privdata; 560 } 561 562 /* 563 ** SMFI_GETSYMVAL -- get the value of a macro 564 ** 565 ** See explanation in mfapi.h about layout of the structures. 566 ** 567 ** Parameters: 568 ** ctx -- Opaque context structure 569 ** symname -- name of macro 570 ** 571 ** Returns: 572 ** value of macro (NULL in case of failure) 573 */ 574 575 char * 576 smfi_getsymval(ctx, symname) 577 SMFICTX *ctx; 578 char *symname; 579 { 580 int i; 581 char **s; 582 char one[2]; 583 char braces[4]; 584 585 if (ctx == NULL || symname == NULL || *symname == '\0') 586 return NULL; 587 588 if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}') 589 { 590 one[0] = symname[1]; 591 one[1] = '\0'; 592 } 593 else 594 one[0] = '\0'; 595 if (strlen(symname) == 1) 596 { 597 braces[0] = '{'; 598 braces[1] = *symname; 599 braces[2] = '}'; 600 braces[3] = '\0'; 601 } 602 else 603 braces[0] = '\0'; 604 605 /* search backwards through the macro array */ 606 for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i) 607 { 608 if ((s = ctx->ctx_mac_ptr[i]) == NULL || 609 ctx->ctx_mac_buf[i] == NULL) 610 continue; 611 while (s != NULL && *s != NULL) 612 { 613 if (strcmp(*s, symname) == 0) 614 return *++s; 615 if (one[0] != '\0' && strcmp(*s, one) == 0) 616 return *++s; 617 if (braces[0] != '\0' && strcmp(*s, braces) == 0) 618 return *++s; 619 ++s; /* skip over macro value */ 620 ++s; /* points to next macro name */ 621 } 622 } 623 return NULL; 624 } 625 626 /* 627 ** SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature 628 ** timeouts during long milter-side operations 629 ** 630 ** Parameters: 631 ** ctx -- Opaque context structure 632 ** 633 ** Return value: 634 ** MI_SUCCESS/MI_FAILURE 635 */ 636 637 int 638 smfi_progress(ctx) 639 SMFICTX *ctx; 640 { 641 struct timeval timeout; 642 643 if (ctx == NULL) 644 return MI_FAILURE; 645 646 timeout.tv_sec = ctx->ctx_timeout; 647 timeout.tv_usec = 0; 648 649 return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0); 650 } 651