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