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