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