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 1.1.1.3 2002/04/10 03:05:00 gshapiro 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 static int 271 myisenhsc(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 309 int 310 smfi_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 382 int 383 #if SM_VA_STD 384 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...) 385 #else /* SM_VA_STD */ 386 smfi_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 492 int 493 smfi_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 513 void * 514 smfi_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 535 char * 536 smfi_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 598 int 599 smfi_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