smfi.c revision 94334
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.63 2002/02/07 01:16:13 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*/ 270static int 271myisenhsc(s, delim) 272 const char *s; 273 int delim; 274{ 275 int l, h; 276 277 if (s == NULL) 278 return 0; 279 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 280 return 0; 281 h = 0; 282 l = 2; 283 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 284 ++h; 285 if (h == 0 || s[l + h] != '.') 286 return 0; 287 l += h + 1; 288 h = 0; 289 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 290 ++h; 291 if (h == 0 || s[l + h] != delim) 292 return 0; 293 return l + h; 294} 295 296/* 297** SMFI_SETREPLY -- set the reply code for the next reply to the MTA 298** 299** Parameters: 300** ctx -- Opaque context structure 301** rcode -- The three-digit (RFC 821) SMTP reply code. 302** xcode -- The extended (RFC 2034) reply code. 303** message -- The text part of the SMTP reply. 304** 305** Returns: 306** MI_SUCCESS/MI_FAILURE 307*/ 308 309int 310smfi_setreply(ctx, rcode, xcode, message) 311 SMFICTX *ctx; 312 char *rcode; 313 char *xcode; 314 char *message; 315{ 316 size_t len; 317 char *buf; 318 319 if (rcode == NULL || ctx == NULL) 320 return MI_FAILURE; 321 322 /* ### <sp> \0 */ 323 len = strlen(rcode) + 2; 324 if (len != 5) 325 return MI_FAILURE; 326 if ((rcode[0] != '4' && rcode[0] != '5') || 327 !isascii(rcode[1]) || !isdigit(rcode[1]) || 328 !isascii(rcode[2]) || !isdigit(rcode[2])) 329 return MI_FAILURE; 330 if (xcode != NULL) 331 { 332 if (!myisenhsc(xcode, '\0')) 333 return MI_FAILURE; 334 len += strlen(xcode) + 1; 335 } 336 if (message != NULL) 337 { 338 size_t ml; 339 340 /* XXX check also for unprintable chars? */ 341 if (strpbrk(message, "\r\n") != NULL) 342 return MI_FAILURE; 343 ml = strlen(message); 344 if (ml > MAXREPLYLEN) 345 return MI_FAILURE; 346 len += ml + 1; 347 } 348 buf = malloc(len); 349 if (buf == NULL) 350 return MI_FAILURE; /* oops */ 351 (void) sm_strlcpy(buf, rcode, len); 352 (void) sm_strlcat(buf, " ", len); 353 if (xcode != NULL) 354 (void) sm_strlcat(buf, xcode, len); 355 if (message != NULL) 356 { 357 if (xcode != NULL) 358 (void) sm_strlcat(buf, " ", len); 359 (void) sm_strlcat(buf, message, len); 360 } 361 if (ctx->ctx_reply != NULL) 362 free(ctx->ctx_reply); 363 ctx->ctx_reply = buf; 364 return MI_SUCCESS; 365} 366 367#if _FFR_MULTILINE 368/* 369** SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA 370** 371** Parameters: 372** ctx -- Opaque context structure 373** rcode -- The three-digit (RFC 821) SMTP reply code. 374** xcode -- The extended (RFC 2034) reply code. 375** txt, ... -- The text part of the SMTP reply, 376** MUST be terminated with NULL. 377** 378** Returns: 379** MI_SUCCESS/MI_FAILURE 380*/ 381 382int 383#if SM_VA_STD 384smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...) 385#else /* SM_VA_STD */ 386smfi_setmlreply(ctx, rcode, xcode, va_alist) 387 SMFICTX *ctx; 388 const char *rcode; 389 const char *xcode; 390 va_dcl 391#endif /* SM_VA_STD */ 392{ 393 size_t len; 394 size_t rlen; 395 int args; 396 char *buf, *txt; 397 const char *xc; 398 char repl[16]; 399 SM_VA_LOCAL_DECL 400 401 if (rcode == NULL || ctx == NULL) 402 return MI_FAILURE; 403 404 /* ### <sp> */ 405 len = strlen(rcode) + 1; 406 if (len != 4) 407 return MI_FAILURE; 408 if ((rcode[0] != '4' && rcode[0] != '5') || 409 !isascii(rcode[1]) || !isdigit(rcode[1]) || 410 !isascii(rcode[2]) || !isdigit(rcode[2])) 411 return MI_FAILURE; 412 if (xcode != NULL) 413 { 414 if (!myisenhsc(xcode, '\0')) 415 return MI_FAILURE; 416 xc = xcode; 417 } 418 else 419 { 420 if (rcode[0] == '4') 421 xc = "4.0.0"; 422 else 423 xc = "5.0.0"; 424 } 425 426 /* add trailing space */ 427 len += strlen(xc) + 1; 428 rlen = len; 429 args = 0; 430 SM_VA_START(ap, xcode); 431 while ((txt = SM_VA_ARG(ap, char *)) != NULL) 432 { 433 size_t tl; 434 435 tl = strlen(txt); 436 if (tl > MAXREPLYLEN) 437 break; 438 439 /* this text, reply codes, \r\n */ 440 len += tl + 2 + rlen; 441 if (++args > MAXREPLIES) 442 break; 443 444 /* XXX check also for unprintable chars? */ 445 if (strpbrk(txt, "\r\n") != NULL) 446 break; 447 } 448 SM_VA_END(ap); 449 if (txt != NULL) 450 return MI_FAILURE; 451 452 /* trailing '\0' */ 453 ++len; 454 buf = malloc(len); 455 if (buf == NULL) 456 return MI_FAILURE; /* oops */ 457 (void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc); 458 (void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-", 459 xc, " "); 460 SM_VA_START(ap, xcode); 461 txt = SM_VA_ARG(ap, char *); 462 if (txt != NULL) 463 { 464 (void) sm_strlcat2(buf, " ", txt, len); 465 while ((txt = SM_VA_ARG(ap, char *)) != NULL) 466 { 467 if (--args <= 1) 468 repl[3] = ' '; 469 (void) sm_strlcat2(buf, "\r\n", repl, len); 470 (void) sm_strlcat(buf, txt, len); 471 } 472 } 473 if (ctx->ctx_reply != NULL) 474 free(ctx->ctx_reply); 475 ctx->ctx_reply = buf; 476 SM_VA_END(ap); 477 return MI_SUCCESS; 478} 479#endif /* _FFR_MULTILINE */ 480 481/* 482** SMFI_SETPRIV -- set private data 483** 484** Parameters: 485** ctx -- Opaque context structure 486** privatedata -- pointer to private data 487** 488** Returns: 489** MI_SUCCESS/MI_FAILURE 490*/ 491 492int 493smfi_setpriv(ctx, privatedata) 494 SMFICTX *ctx; 495 void *privatedata; 496{ 497 if (ctx == NULL) 498 return MI_FAILURE; 499 ctx->ctx_privdata = privatedata; 500 return MI_SUCCESS; 501} 502 503/* 504** SMFI_GETPRIV -- get private data 505** 506** Parameters: 507** ctx -- Opaque context structure 508** 509** Returns: 510** pointer to private data 511*/ 512 513void * 514smfi_getpriv(ctx) 515 SMFICTX *ctx; 516{ 517 if (ctx == NULL) 518 return NULL; 519 return ctx->ctx_privdata; 520} 521 522/* 523** SMFI_GETSYMVAL -- get the value of a macro 524** 525** See explanation in mfapi.h about layout of the structures. 526** 527** Parameters: 528** ctx -- Opaque context structure 529** symname -- name of macro 530** 531** Returns: 532** value of macro (NULL in case of failure) 533*/ 534 535char * 536smfi_getsymval(ctx, symname) 537 SMFICTX *ctx; 538 char *symname; 539{ 540 int i; 541 char **s; 542 char one[2]; 543 char braces[4]; 544 545 if (ctx == NULL || symname == NULL || *symname == '\0') 546 return NULL; 547 548 if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}') 549 { 550 one[0] = symname[1]; 551 one[1] = '\0'; 552 } 553 else 554 one[0] = '\0'; 555 if (strlen(symname) == 1) 556 { 557 braces[0] = '{'; 558 braces[1] = *symname; 559 braces[2] = '}'; 560 braces[3] = '\0'; 561 } 562 else 563 braces[0] = '\0'; 564 565 /* search backwards through the macro array */ 566 for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i) 567 { 568 if ((s = ctx->ctx_mac_ptr[i]) == NULL || 569 ctx->ctx_mac_buf[i] == NULL) 570 continue; 571 while (s != NULL && *s != NULL) 572 { 573 if (strcmp(*s, symname) == 0) 574 return *++s; 575 if (one[0] != '\0' && strcmp(*s, one) == 0) 576 return *++s; 577 if (braces[0] != '\0' && strcmp(*s, braces) == 0) 578 return *++s; 579 ++s; /* skip over macro value */ 580 ++s; /* points to next macro name */ 581 } 582 } 583 return NULL; 584} 585 586#if _FFR_SMFI_PROGRESS 587/* 588** SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature 589** timeouts during long milter-side operations 590** 591** Parameters: 592** ctx -- Opaque context structure 593** 594** Return value: 595** MI_SUCCESS/MI_FAILURE 596*/ 597 598int 599smfi_progress(ctx) 600 SMFICTX *ctx; 601{ 602 struct timeval timeout; 603 604 if (ctx == NULL) 605 return MI_FAILURE; 606 607 timeout.tv_sec = ctx->ctx_timeout; 608 timeout.tv_usec = 0; 609 610 return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0); 611} 612#endif /* _FFR_SMFI_PROGRESS */ 613