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