1 # include <errno.h> 2 # include "sendmail.h" 3 4 # ifndef SMTP 5 SCCSID(@(#)srvrsmtp.c 3.39 11/28/82 (no SMTP)); 6 # else SMTP 7 8 SCCSID(@(#)srvrsmtp.c 3.39 11/28/82); 9 10 /* 11 ** SMTP -- run the SMTP protocol. 12 ** 13 ** Parameters: 14 ** none. 15 ** 16 ** Returns: 17 ** never. 18 ** 19 ** Side Effects: 20 ** Reads commands from the input channel and processes 21 ** them. 22 */ 23 24 struct cmd 25 { 26 char *cmdname; /* command name */ 27 int cmdcode; /* internal code, see below */ 28 }; 29 30 /* values for cmdcode */ 31 # define CMDERROR 0 /* bad command */ 32 # define CMDMAIL 1 /* mail -- designate sender */ 33 # define CMDRCPT 2 /* rcpt -- designate recipient */ 34 # define CMDDATA 3 /* data -- send message text */ 35 # define CMDRSET 4 /* rset -- reset state */ 36 # define CMDVRFY 5 /* vrfy -- verify address */ 37 # define CMDHELP 6 /* help -- give usage info */ 38 # define CMDNOOP 7 /* noop -- do nothing */ 39 # define CMDQUIT 8 /* quit -- close connection and die */ 40 # define CMDHELO 9 /* helo -- be polite */ 41 # define CMDDBGQSHOW 10 /* showq -- show send queue (DEBUG) */ 42 # define CMDDBGDEBUG 11 /* debug -- set debug mode */ 43 # define CMDVERB 12 /* verb -- go into verbose mode */ 44 # define CMDDBGKILL 13 /* kill -- kill sendmail */ 45 # define CMDDBGWIZ 14 /* wiz -- become a wizard */ 46 # define CMDONEX 15 /* onex -- sending one transaction only */ 47 # define CMDDBGSHELL 16 /* shell -- give us a shell */ 48 49 static struct cmd CmdTab[] = 50 { 51 "mail", CMDMAIL, 52 "rcpt", CMDRCPT, 53 "data", CMDDATA, 54 "rset", CMDRSET, 55 "vrfy", CMDVRFY, 56 "expn", CMDVRFY, 57 "help", CMDHELP, 58 "noop", CMDNOOP, 59 "quit", CMDQUIT, 60 "helo", CMDHELO, 61 "verb", CMDVERB, 62 "onex", CMDONEX, 63 # ifdef DEBUG 64 "showq", CMDDBGQSHOW, 65 "debug", CMDDBGDEBUG, 66 "kill", CMDDBGKILL, 67 "wiz", CMDDBGWIZ, 68 "shell", CMDDBGSHELL, 69 # endif DEBUG 70 NULL, CMDERROR, 71 }; 72 73 # ifdef DEBUG 74 bool IsWiz = FALSE; /* set if we are a wizard */ 75 char *WizWord = NULL; /* the wizard word to compare against */ 76 # endif DEBUG 77 bool InChild = FALSE; /* true if running in a subprocess */ 78 bool OneXact = FALSE; /* one xaction only this run */ 79 #define EX_QUIT 22 /* special code for QUIT command */ 80 81 smtp() 82 { 83 register char *p; 84 register struct cmd *c; 85 char *cmd; 86 extern char *skipword(); 87 extern bool sameword(); 88 bool hasmail; /* mail command received */ 89 int rcps; /* number of recipients */ 90 auto ADDRESS *vrfyqueue; 91 char inp[MAXLINE]; 92 extern char Version[]; 93 extern tick(); 94 extern bool iswiz(); 95 extern char *arpadate(); 96 97 hasmail = FALSE; 98 rcps = 0; 99 if (OutChannel != stdout) 100 { 101 /* arrange for debugging output to go to remote host */ 102 (void) close(1); 103 (void) dup(fileno(OutChannel)); 104 } 105 message("220", "%s Sendmail %s ready at %s", HostName, 106 Version, arpadate((char *) NULL)); 107 (void) setjmp(TopFrame); 108 QuickAbort = FALSE; 109 for (;;) 110 { 111 /* setup for the read */ 112 CurEnv->e_to = NULL; 113 Errors = 0; 114 (void) fflush(stdout); 115 116 /* read the input line */ 117 p = sfgets(inp, sizeof inp, InChannel); 118 119 /* handle errors */ 120 if (p == NULL) 121 { 122 /* end of file, just die */ 123 message("421", "%s Lost input channel", HostName); 124 finis(); 125 } 126 127 /* clean up end of line */ 128 fixcrlf(inp, TRUE); 129 130 /* echo command to transcript */ 131 if (Xscript != NULL) 132 fprintf(Xscript, "<<< %s\n", inp); 133 134 /* break off command */ 135 for (p = inp; isspace(*p); p++) 136 continue; 137 cmd = p; 138 while (*++p != '\0' && !isspace(*p)) 139 continue; 140 if (*p != '\0') 141 *p++ = '\0'; 142 143 /* decode command */ 144 for (c = CmdTab; c->cmdname != NULL; c++) 145 { 146 if (sameword(c->cmdname, cmd)) 147 break; 148 } 149 150 /* process command */ 151 switch (c->cmdcode) 152 { 153 case CMDHELO: /* hello -- introduce yourself */ 154 define('s', newstr(p), CurEnv); 155 message("250", "%s Hello %s, pleased to meet you", 156 HostName, p); 157 break; 158 159 case CMDMAIL: /* mail -- designate sender */ 160 /* check for validity of this command */ 161 if (hasmail) 162 { 163 message("503", "Sender already specified"); 164 break; 165 } 166 if (InChild) 167 { 168 syserr("Nested MAIL command"); 169 exit(0); 170 } 171 172 /* fork a subprocess to process this command */ 173 if (runinchild("SMTP-MAIL") > 0) 174 break; 175 initsys(); 176 177 /* child -- go do the processing */ 178 p = skipword(p, "from"); 179 if (p == NULL) 180 break; 181 setsender(p); 182 if (Errors == 0) 183 { 184 message("250", "Sender ok"); 185 hasmail = TRUE; 186 } 187 else if (InChild) 188 finis(); 189 break; 190 191 case CMDRCPT: /* rcpt -- designate recipient */ 192 p = skipword(p, "to"); 193 if (p == NULL) 194 break; 195 sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 196 if (Errors == 0) 197 { 198 message("250", "%s... Recipient ok", p); 199 rcps++; 200 } 201 break; 202 203 case CMDDATA: /* data -- text of mail */ 204 if (!hasmail) 205 { 206 message("503", "Need MAIL command"); 207 break; 208 } 209 else if (rcps <= 0) 210 { 211 message("503", "Need RCPT (recipient)"); 212 break; 213 } 214 215 /* collect the text of the message */ 216 collect(TRUE); 217 if (Errors != 0) 218 break; 219 220 /* 221 ** Arrange to send to everyone. 222 ** If sending to multiple people, mail back 223 ** errors rather than reporting directly. 224 ** In any case, don't mail back errors for 225 ** anything that has happened up to 226 ** now (the other end will do this). 227 ** Then send to everyone. 228 ** Finally give a reply code. If an error has 229 ** already been given, don't mail a 230 ** message back. 231 ** We goose error returns by clearing error bit. 232 */ 233 234 if (rcps != 1) 235 { 236 HoldErrs = TRUE; 237 ErrorMode == EM_MAIL; 238 } 239 CurEnv->e_flags &= ~EF_FATALERRS; 240 241 /* send to all recipients */ 242 sendall(CurEnv, SendMode); 243 CurEnv->e_to = NULL; 244 245 /* issue success if appropriate and reset */ 246 if (Errors == 0 || HoldErrs) 247 { 248 HoldErrs = FALSE; 249 message("250", "Ok"); 250 } 251 else 252 CurEnv->e_flags &= ~EF_FATALERRS; 253 254 /* if in a child, pop back to our parent */ 255 if (InChild) 256 finis(); 257 break; 258 259 case CMDRSET: /* rset -- reset state */ 260 message("250", "Reset state"); 261 if (InChild) 262 finis(); 263 break; 264 265 case CMDVRFY: /* vrfy -- verify address */ 266 if (runinchild("SMTP-VRFY") > 0) 267 break; 268 vrfyqueue = NULL; 269 QuickAbort = TRUE; 270 sendto(p, (ADDRESS *) NULL, &vrfyqueue); 271 if (Errors != 0) 272 { 273 if (InChild) 274 finis(); 275 break; 276 } 277 while (vrfyqueue != NULL) 278 { 279 register ADDRESS *a = vrfyqueue->q_next; 280 char *code; 281 282 while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 283 a = a->q_next; 284 285 if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 286 { 287 if (a != NULL) 288 code = "250-"; 289 else 290 code = "250"; 291 if (vrfyqueue->q_fullname == NULL) 292 message(code, "<%s>", vrfyqueue->q_paddr); 293 else 294 message(code, "%s <%s>", 295 vrfyqueue->q_fullname, vrfyqueue->q_paddr); 296 } 297 else if (a == NULL) 298 message("554", "Self destructive alias loop"); 299 vrfyqueue = a; 300 } 301 if (InChild) 302 finis(); 303 break; 304 305 case CMDHELP: /* help -- give user info */ 306 if (*p == '\0') 307 p = "SMTP"; 308 help(p); 309 break; 310 311 case CMDNOOP: /* noop -- do nothing */ 312 message("200", "OK"); 313 break; 314 315 case CMDQUIT: /* quit -- leave mail */ 316 message("221", "%s closing connection", HostName); 317 if (InChild) 318 ExitStat = EX_QUIT; 319 finis(); 320 321 case CMDVERB: /* set verbose mode */ 322 Verbose = TRUE; 323 message("200", "Verbose mode"); 324 break; 325 326 case CMDONEX: /* doing one transaction only */ 327 OneXact = TRUE; 328 message("200", "Only one transaction"); 329 break; 330 331 # ifdef DEBUG 332 case CMDDBGQSHOW: /* show queues */ 333 printf("Send Queue="); 334 printaddr(CurEnv->e_sendqueue, TRUE); 335 break; 336 337 case CMDDBGDEBUG: /* set debug mode */ 338 tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 339 tTflag(p); 340 message("200", "Debug set"); 341 break; 342 343 case CMDDBGKILL: /* kill the parent */ 344 if (!iswiz()) 345 break; 346 if (kill(MotherPid, SIGTERM) >= 0) 347 message("200", "Mother is dead"); 348 else 349 message("500", "Can't kill Mom"); 350 break; 351 352 case CMDDBGSHELL: /* give us an interactive shell */ 353 if (!iswiz()) 354 break; 355 if (fileno(InChannel) != 0) 356 { 357 (void) close(0); 358 (void) dup(fileno(InChannel)); 359 (void) fclose(InChannel); 360 InChannel = stdin; 361 } 362 if (fileno(OutChannel) != 1) 363 { 364 (void) close(1); 365 (void) dup(fileno(OutChannel)); 366 (void) fclose(OutChannel); 367 OutChannel = stdout; 368 } 369 execl("/bin/csh", "sendmail", 0); 370 execl("/bin/sh", "sendmail", 0); 371 message("500", "Can't"); 372 exit(EX_UNAVAILABLE); 373 374 case CMDDBGWIZ: /* become a wizard */ 375 if (WizWord != NULL) 376 { 377 char seed[3]; 378 extern char *crypt(); 379 380 strncpy(seed, WizWord, 2); 381 if (strcmp(WizWord, crypt(p, seed)) != 0) 382 { 383 message("500", "You are no wizard!"); 384 break; 385 } 386 } 387 IsWiz = TRUE; 388 message("200", "Please pass, oh mighty wizard"); 389 break; 390 # endif DEBUG 391 392 case CMDERROR: /* unknown command */ 393 message("500", "Command unrecognized"); 394 break; 395 396 default: 397 syserr("smtp: unknown code %d", c->cmdcode); 398 break; 399 } 400 } 401 } 402 /* 403 ** SKIPWORD -- skip a fixed word. 404 ** 405 ** Parameters: 406 ** p -- place to start looking. 407 ** w -- word to skip. 408 ** 409 ** Returns: 410 ** p following w. 411 ** NULL on error. 412 ** 413 ** Side Effects: 414 ** clobbers the p data area. 415 */ 416 417 static char * 418 skipword(p, w) 419 register char *p; 420 char *w; 421 { 422 register char *q; 423 extern bool sameword(); 424 425 /* find beginning of word */ 426 while (isspace(*p)) 427 p++; 428 q = p; 429 430 /* find end of word */ 431 while (*p != '\0' && *p != ':' && !isspace(*p)) 432 p++; 433 while (isspace(*p)) 434 *p++ = '\0'; 435 if (*p != ':') 436 { 437 syntax: 438 message("501", "Syntax error"); 439 Errors++; 440 return (NULL); 441 } 442 *p++ = '\0'; 443 while (isspace(*p)) 444 p++; 445 446 /* see if the input word matches desired word */ 447 if (!sameword(q, w)) 448 goto syntax; 449 450 return (p); 451 } 452 /* 453 ** HELP -- implement the HELP command. 454 ** 455 ** Parameters: 456 ** topic -- the topic we want help for. 457 ** 458 ** Returns: 459 ** none. 460 ** 461 ** Side Effects: 462 ** outputs the help file to message output. 463 */ 464 465 help(topic) 466 char *topic; 467 { 468 register FILE *hf; 469 int len; 470 char buf[MAXLINE]; 471 bool noinfo; 472 473 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 474 { 475 /* no help */ 476 message("502", "HELP not implemented"); 477 return; 478 } 479 480 len = strlen(topic); 481 makelower(topic); 482 noinfo = TRUE; 483 484 while (fgets(buf, sizeof buf, hf) != NULL) 485 { 486 if (strncmp(buf, topic, len) == 0) 487 { 488 register char *p; 489 490 p = index(buf, '\t'); 491 if (p == NULL) 492 p = buf; 493 else 494 p++; 495 fixcrlf(p, TRUE); 496 message("214-", p); 497 noinfo = FALSE; 498 } 499 } 500 501 if (noinfo) 502 message("504", "HELP topic unknown"); 503 else 504 message("214", "End of HELP info"); 505 (void) fclose(hf); 506 } 507 /* 508 ** ISWIZ -- tell us if we are a wizard 509 ** 510 ** If not, print a nasty message. 511 ** 512 ** Parameters: 513 ** none. 514 ** 515 ** Returns: 516 ** TRUE if we are a wizard. 517 ** FALSE if we are not a wizard. 518 ** 519 ** Side Effects: 520 ** Prints a 500 exit stat if we are not a wizard. 521 */ 522 523 bool 524 iswiz() 525 { 526 if (!IsWiz) 527 message("500", "Mere mortals musn't mutter that mantra"); 528 return (IsWiz); 529 } 530 /* 531 ** RUNINCHILD -- return twice -- once in the child, then in the parent again 532 ** 533 ** Parameters: 534 ** label -- a string used in error messages 535 ** 536 ** Returns: 537 ** zero in the child 538 ** one in the parent 539 ** 540 ** Side Effects: 541 ** none. 542 */ 543 544 runinchild(label) 545 char *label; 546 { 547 int childpid; 548 549 if (OneXact) 550 return (0); 551 552 childpid = dofork(); 553 if (childpid < 0) 554 { 555 syserr("%s: cannot fork", label); 556 return (1); 557 } 558 if (childpid > 0) 559 { 560 auto int st; 561 562 /* parent -- wait for child to complete */ 563 st = waitfor(childpid); 564 if (st == -1) 565 syserr("%s: lost child", label); 566 567 /* if we exited on a QUIT command, complete the process */ 568 if (st == (EX_QUIT << 8)) 569 finis(); 570 571 return (1); 572 } 573 else 574 { 575 /* child */ 576 InChild = TRUE; 577 return (0); 578 } 579 } 580 581 # endif SMTP 582