1 /* 2 * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * By using this file, you agree to the terms and conditions set 8 * forth in the LICENSE file which can be found at the top level of 9 * the sendmail distribution. 10 * 11 */ 12 13 #ifndef lint 14 static char copyright[] = 15 "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ 16 All rights reserved.\n\ 17 Copyright (c) 1988, 1993\n\ 18 The Regents of the University of California. All rights reserved.\n"; 19 #endif /* ! lint */ 20 21 #ifndef lint 22 static char id[] = "@(#)$Id: rmail.c,v 8.39.4.12 2001/05/07 22:06:39 gshapiro Exp $"; 23 #endif /* ! lint */ 24 25 /* $FreeBSD$ */ 26 27 /* 28 * RMAIL -- UUCP mail server. 29 * 30 * This program reads the >From ... remote from ... lines that UUCP is so 31 * fond of and turns them into something reasonable. It then execs sendmail 32 * with various options built from these lines. 33 * 34 * The expected syntax is: 35 * 36 * <user> := [-a-z0-9]+ 37 * <date> := ctime format 38 * <site> := [-a-z0-9!]+ 39 * <blank line> := "^\n$" 40 * <from> := "From" <space> <user> <space> <date> 41 * [<space> "remote from" <space> <site>] 42 * <forward> := ">" <from> 43 * msg := <from> <forward>* <blank-line> <body> 44 * 45 * The output of rmail(8) compresses the <forward> lines into a single 46 * from path. 47 * 48 * The err(3) routine is included here deliberately to make this code 49 * a bit more portable. 50 */ 51 52 #include <sys/types.h> 53 #include <sys/param.h> 54 #include <sys/stat.h> 55 #include <sys/wait.h> 56 57 #include <ctype.h> 58 #include <fcntl.h> 59 #ifdef BSD4_4 60 # define FORK vfork 61 # include <paths.h> 62 #else /* BSD4_4 */ 63 # define FORK fork 64 # ifndef _PATH_SENDMAIL 65 # define _PATH_SENDMAIL "/usr/lib/sendmail" 66 # endif /* ! _PATH_SENDMAIL */ 67 #endif /* BSD4_4 */ 68 #include <stdio.h> 69 #include <stdlib.h> 70 #include <string.h> 71 #include <unistd.h> 72 #ifdef EX_OK 73 # undef EX_OK /* unistd.h may have another use for this */ 74 #endif /* EX_OK */ 75 #include <sysexits.h> 76 77 #ifndef MAX 78 # define MAX(a, b) ((a) < (b) ? (b) : (a)) 79 #endif /* ! MAX */ 80 81 #ifndef __P 82 # ifdef __STDC__ 83 # define __P(protos) protos 84 # else /* __STDC__ */ 85 # define __P(protos) () 86 # define const 87 # endif /* __STDC__ */ 88 #endif /* ! __P */ 89 90 #ifndef STDIN_FILENO 91 # define STDIN_FILENO 0 92 #endif /* ! STDIN_FILENO */ 93 94 #if defined(BSD4_4) || defined(linux) || SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) || _AIX4 >= 40300 || defined(HPUX11) 95 # define HASSNPRINTF 1 96 #endif /* defined(BSD4_4) || defined(linux) || SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) || _AIX4 >= 40300 || defined(HPUX11) */ 97 98 #if defined(sun) && !defined(BSD) && !defined(SOLARIS) && !defined(__svr4__) && !defined(__SVR4) 99 # define memmove(d, s, l) (bcopy((s), (d), (l))) 100 #endif /* defined(sun) && !defined(BSD) && !defined(SOLARIS) && !defined(__svr4__) && !defined(__SVR4) */ 101 102 #if !HASSNPRINTF && !SFIO 103 extern int snprintf __P((char *, size_t, const char *, ...)); 104 #endif /* !HASSNPRINTF && !SFIO */ 105 106 #if defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) || defined(IRIX64) || defined(IRIX5) || defined(IRIX6) 107 # ifndef HASSTRERROR 108 # define HASSTRERROR 1 109 # endif /* ! HASSTRERROR */ 110 #endif /* defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) || 111 defined(IRIX64) || defined(IRIX5) || defined(IRIX6) */ 112 113 #if defined(SUNOS403) || defined(NeXT) || (defined(MACH) && defined(i386) && !defined(__GNU__)) || defined(oldBSD43) || defined(MORE_BSD) || defined(umipsbsd) || defined(ALTOS_SYSTEM_V) || defined(RISCOS) || defined(_AUX_SOURCE) || defined(UMAXV) || defined(titan) || defined(UNIXWARE) || defined(sony_news) || defined(luna) || defined(nec_ews_svr4) || defined(_nec_ews_svr4) || defined(__MAXION__) 114 # undef WIFEXITED 115 # undef WEXITSTATUS 116 # define WIFEXITED(st) (((st) & 0377) == 0) 117 # define WEXITSTATUS(st) (((st) >> 8) & 0377) 118 #endif /* defined(SUNOS403) || defined(NeXT) || (defined(MACH) && defined(i386) && !defined(__GNU__)) || defined(oldBSD43) || defined(MORE_BSD) || defined(umipsbsd) || defined(ALTOS_SYSTEM_V) || defined(RISCOS) || defined(_AUX_SOURCE) || defined(UMAXV) || defined(titan) || defined(UNIXWARE) || defined(sony_news) || defined(luna) || defined(nec_ews_svr4) || defined(_nec_ews_svr4) || defined(__MAXION__) */ 119 120 121 #include "sendmail/errstring.h" 122 123 static void err __P((int, const char *, ...)); 124 static void usage __P((void)); 125 static char *xalloc __P((int)); 126 127 #define newstr(s) strcpy(xalloc(strlen(s) + 1), s) 128 129 static char * 130 xalloc(sz) 131 register int sz; 132 { 133 register char *p; 134 135 /* some systems can't handle size zero mallocs */ 136 if (sz <= 0) 137 sz = 1; 138 139 p = malloc((unsigned) sz); 140 if (p == NULL) 141 err(EX_TEMPFAIL, "out of memory"); 142 return (p); 143 } 144 145 int 146 main(argc, argv) 147 int argc; 148 char *argv[]; 149 { 150 int ch, debug, i, pdes[2], pid, status; 151 size_t fplen = 0, fptlen = 0, len; 152 off_t offset; 153 FILE *fp; 154 char *addrp = NULL, *domain, *p, *t; 155 char *from_path, *from_sys, *from_user; 156 char **args, buf[2048], lbuf[2048]; 157 struct stat sb; 158 extern char *optarg; 159 extern int optind; 160 161 debug = 0; 162 domain = "UUCP"; /* Default "domain". */ 163 while ((ch = getopt(argc, argv, "D:T")) != -1) 164 { 165 switch (ch) 166 { 167 case 'T': 168 debug = 1; 169 break; 170 171 case 'D': 172 domain = optarg; 173 break; 174 175 case '?': 176 default: 177 usage(); 178 } 179 } 180 181 argc -= optind; 182 argv += optind; 183 184 if (argc < 1) 185 usage(); 186 187 from_path = from_sys = from_user = NULL; 188 for (offset = 0; ; ) 189 { 190 /* Get and nul-terminate the line. */ 191 if (fgets(lbuf, sizeof(lbuf), stdin) == NULL) 192 exit(EX_DATAERR); 193 if ((p = strchr(lbuf, '\n')) == NULL) 194 err(EX_DATAERR, "line too long"); 195 *p = '\0'; 196 197 /* Parse lines until reach a non-"From" line. */ 198 if (!strncmp(lbuf, "From ", 5)) 199 addrp = lbuf + 5; 200 else if (!strncmp(lbuf, ">From ", 6)) 201 addrp = lbuf + 6; 202 else if (offset == 0) 203 err(EX_DATAERR, 204 "missing or empty From line: %s", lbuf); 205 else 206 { 207 *p = '\n'; 208 break; 209 } 210 211 if (addrp == NULL || *addrp == '\0') 212 err(EX_DATAERR, "corrupted From line: %s", lbuf); 213 214 /* Use the "remote from" if it exists. */ 215 for (p = addrp; (p = strchr(p + 1, 'r')) != NULL; ) 216 { 217 if (!strncmp(p, "remote from ", 12)) 218 { 219 for (t = p += 12; *t != '\0'; ++t) 220 { 221 if (isascii(*t) && isspace(*t)) 222 break; 223 } 224 *t = '\0'; 225 if (debug) 226 fprintf(stderr, "remote from: %s\n", p); 227 break; 228 } 229 } 230 231 /* Else use the string up to the last bang. */ 232 if (p == NULL) 233 { 234 if (*addrp == '!') 235 err(EX_DATAERR, "bang starts address: %s", 236 addrp); 237 else if ((t = strrchr(addrp, '!')) != NULL) 238 { 239 *t = '\0'; 240 p = addrp; 241 addrp = t + 1; 242 if (*addrp == '\0') 243 err(EX_DATAERR, 244 "corrupted From line: %s", lbuf); 245 if (debug) 246 fprintf(stderr, "bang: %s\n", p); 247 } 248 } 249 250 /* 'p' now points to any system string from this line. */ 251 if (p != NULL) 252 { 253 /* Nul terminate it as necessary. */ 254 for (t = p; *t != '\0'; ++t) 255 { 256 if (isascii(*t) && isspace(*t)) 257 break; 258 } 259 *t = '\0'; 260 261 /* If the first system, copy to the from_sys string. */ 262 if (from_sys == NULL) 263 { 264 from_sys = newstr(p); 265 if (debug) 266 fprintf(stderr, "from_sys: %s\n", 267 from_sys); 268 } 269 270 /* Concatenate to the path string. */ 271 len = t - p; 272 if (from_path == NULL) 273 { 274 fplen = 0; 275 if ((from_path = malloc(fptlen = 256)) == NULL) 276 err(EX_TEMPFAIL, NULL); 277 } 278 if (fplen + len + 2 > fptlen) 279 { 280 fptlen += MAX(fplen + len + 2, 256); 281 if ((from_path = realloc(from_path, 282 fptlen)) == NULL) 283 err(EX_TEMPFAIL, NULL); 284 } 285 memmove(from_path + fplen, p, len); 286 fplen += len; 287 from_path[fplen++] = '!'; 288 from_path[fplen] = '\0'; 289 } 290 291 /* Save off from user's address; the last one wins. */ 292 for (p = addrp; *p != '\0'; ++p) 293 { 294 if (isascii(*p) && isspace(*p)) 295 break; 296 } 297 *p = '\0'; 298 if (*addrp == '\0') 299 addrp = "<>"; 300 if (from_user != NULL) 301 free(from_user); 302 from_user = newstr(addrp); 303 304 if (debug) 305 { 306 if (from_path != NULL) 307 fprintf(stderr, "from_path: %s\n", from_path); 308 fprintf(stderr, "from_user: %s\n", from_user); 309 } 310 311 if (offset != -1) 312 offset = (off_t)ftell(stdin); 313 } 314 315 316 /* Allocate args (with room for sendmail args as well as recipients) */ 317 args = (char **)xalloc(sizeof(*args) * (10 + argc)); 318 319 i = 0; 320 args[i++] = _PATH_SENDMAIL; /* Build sendmail's argument list. */ 321 args[i++] = "-oee"; /* No errors, just status. */ 322 #ifdef QUEUE_ONLY 323 args[i++] = "-odq"; /* Queue it, don't try to deliver. */ 324 #else 325 args[i++] = "-odi"; /* Deliver in foreground. */ 326 #endif 327 args[i++] = "-oi"; /* Ignore '.' on a line by itself. */ 328 329 /* set from system and protocol used */ 330 if (from_sys == NULL) 331 snprintf(buf, sizeof(buf), "-p%s", domain); 332 else if (strchr(from_sys, '.') == NULL) 333 snprintf(buf, sizeof(buf), "-p%s:%s.%s", 334 domain, from_sys, domain); 335 else 336 snprintf(buf, sizeof(buf), "-p%s:%s", domain, from_sys); 337 args[i++] = newstr(buf); 338 339 /* Set name of ``from'' person. */ 340 snprintf(buf, sizeof(buf), "-f%s%s", 341 from_path ? from_path : "", from_user); 342 args[i++] = newstr(buf); 343 344 /* 345 ** Don't copy arguments beginning with - as they will be 346 ** passed to sendmail and could be interpreted as flags. 347 ** To prevent confusion of sendmail wrap < and > around 348 ** the address (helps to pass addrs like @gw1,@gw2:aa@bb) 349 */ 350 351 while (*argv != NULL) 352 { 353 if (**argv == '-') 354 err(EX_USAGE, "dash precedes argument: %s", *argv); 355 356 if (strchr(*argv, ',') == NULL || strchr(*argv, '<') != NULL) 357 args[i++] = *argv; 358 else 359 { 360 len = strlen(*argv) + 3; 361 if ((args[i] = malloc(len)) == NULL) 362 err(EX_TEMPFAIL, "Cannot malloc"); 363 snprintf(args[i++], len, "<%s>", *argv); 364 } 365 argv++; 366 argc--; 367 368 /* Paranoia check, argc used for args[] bound */ 369 if (argc < 0) 370 err(EX_SOFTWARE, "Argument count mismatch"); 371 } 372 args[i] = NULL; 373 374 if (debug) 375 { 376 fprintf(stderr, "Sendmail arguments:\n"); 377 for (i = 0; args[i] != NULL; i++) 378 fprintf(stderr, "\t%s\n", args[i]); 379 } 380 381 /* 382 ** If called with a regular file as standard input, seek to the right 383 ** position in the file and just exec sendmail. Could probably skip 384 ** skip the stat, but it's not unreasonable to believe that a failed 385 ** seek will cause future reads to fail. 386 */ 387 388 if (!fstat(STDIN_FILENO, &sb) && S_ISREG(sb.st_mode)) 389 { 390 if (lseek(STDIN_FILENO, offset, SEEK_SET) != offset) 391 err(EX_TEMPFAIL, "stdin seek"); 392 (void) execv(_PATH_SENDMAIL, args); 393 err(EX_OSERR, "%s", _PATH_SENDMAIL); 394 } 395 396 if (pipe(pdes) < 0) 397 err(EX_OSERR, NULL); 398 399 switch (pid = FORK()) 400 { 401 case -1: /* Err. */ 402 err(EX_OSERR, NULL); 403 /* NOTREACHED */ 404 405 case 0: /* Child. */ 406 if (pdes[0] != STDIN_FILENO) 407 { 408 (void) dup2(pdes[0], STDIN_FILENO); 409 (void) close(pdes[0]); 410 } 411 (void) close(pdes[1]); 412 (void) execv(_PATH_SENDMAIL, args); 413 _exit(127); 414 /* NOTREACHED */ 415 } 416 417 if ((fp = fdopen(pdes[1], "w")) == NULL) 418 err(EX_OSERR, NULL); 419 (void) close(pdes[0]); 420 421 /* Copy the file down the pipe. */ 422 do 423 { 424 (void) fprintf(fp, "%s", lbuf); 425 } while (fgets(lbuf, sizeof(lbuf), stdin) != NULL); 426 427 if (ferror(stdin)) 428 err(EX_TEMPFAIL, "stdin: %s", errstring(errno)); 429 430 if (fclose(fp)) 431 err(EX_OSERR, NULL); 432 433 if ((waitpid(pid, &status, 0)) == -1) 434 err(EX_OSERR, "%s", _PATH_SENDMAIL); 435 436 if (!WIFEXITED(status)) 437 err(EX_OSERR, "%s: did not terminate normally", _PATH_SENDMAIL); 438 439 if (WEXITSTATUS(status)) 440 err(status, "%s: terminated with %d (non-zero) status", 441 _PATH_SENDMAIL, WEXITSTATUS(status)); 442 exit(EX_OK); 443 /* NOTREACHED */ 444 return EX_OK; 445 } 446 447 static void 448 usage() 449 { 450 (void) fprintf(stderr, "usage: rmail [-T] [-D domain] user ...\n"); 451 exit(EX_USAGE); 452 } 453 454 #ifdef __STDC__ 455 # include <stdarg.h> 456 #else /* __STDC__ */ 457 # include <varargs.h> 458 #endif /* __STDC__ */ 459 460 static void 461 #ifdef __STDC__ 462 err(int eval, const char *fmt, ...) 463 #else /* __STDC__ */ 464 err(eval, fmt, va_alist) 465 int eval; 466 const char *fmt; 467 va_dcl 468 #endif /* __STDC__ */ 469 { 470 va_list ap; 471 #ifdef __STDC__ 472 va_start(ap, fmt); 473 #else /* __STDC__ */ 474 va_start(ap); 475 #endif /* __STDC__ */ 476 (void) fprintf(stderr, "rmail: "); 477 (void) vfprintf(stderr, fmt, ap); 478 va_end(ap); 479 (void) fprintf(stderr, "\n"); 480 exit(eval); 481 } 482