smfi.c revision 98121
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> 12SM_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 32int 33smfi_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 76int 77smfi_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 126int 127smfi_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 155int 156smfi_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 185int 186smfi_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 229int 230smfi_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 271static int 272myisenhsc(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 310int 311smfi_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 383int 384#if SM_VA_STD 385smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...) 386#else /* SM_VA_STD */ 387smfi_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 493int 494smfi_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 514void * 515smfi_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 536char * 537smfi_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 599int 600smfi_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