1 # include <errno.h> 2 # include "sendmail.h" 3 4 # ifndef SMTP 5 SCCSID(@(#)srvrsmtp.c 3.38 11/24/82 (no SMTP)); 6 # else SMTP 7 8 SCCSID(@(#)srvrsmtp.c 3.38 11/24/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 #define EX_QUIT 22 /* special code for QUIT command */ 79 80 smtp() 81 { 82 register char *p; 83 register struct cmd *c; 84 char *cmd; 85 extern char *skipword(); 86 extern bool sameword(); 87 bool hasmail; /* mail command received */ 88 int rcps; /* number of recipients */ 89 auto ADDRESS *vrfyqueue; 90 bool onexact = FALSE; /* one transaction this connection */ 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)); 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 HoldErrs = MailBack = TRUE; 236 CurEnv->e_flags &= ~EF_FATALERRS; 237 238 /* send to all recipients */ 239 sendall(CurEnv, SendMode); 240 CurEnv->e_to = NULL; 241 242 /* issue success if appropriate and reset */ 243 if (Errors == 0 || HoldErrs) 244 { 245 HoldErrs = FALSE; 246 message("250", "Ok"); 247 } 248 else 249 CurEnv->e_flags &= ~EF_FATALERRS; 250 251 /* if in a child, pop back to our parent */ 252 if (InChild) 253 finis(); 254 break; 255 256 case CMDRSET: /* rset -- reset state */ 257 message("250", "Reset state"); 258 if (InChild) 259 finis(); 260 break; 261 262 case CMDVRFY: /* vrfy -- verify address */ 263 if (runinchild("SMTP-VRFY") > 0) 264 break; 265 vrfyqueue = NULL; 266 QuickAbort = TRUE; 267 sendto(p, (ADDRESS *) NULL, &vrfyqueue); 268 if (Errors != 0) 269 { 270 if (InChild) 271 finis(); 272 break; 273 } 274 while (vrfyqueue != NULL) 275 { 276 register ADDRESS *a = vrfyqueue->q_next; 277 char *code; 278 279 while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 280 a = a->q_next; 281 282 if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 283 { 284 if (a != NULL) 285 code = "250-"; 286 else 287 code = "250"; 288 if (vrfyqueue->q_fullname == NULL) 289 message(code, "<%s>", vrfyqueue->q_paddr); 290 else 291 message(code, "%s <%s>", 292 vrfyqueue->q_fullname, vrfyqueue->q_paddr); 293 } 294 else if (a == NULL) 295 message("554", "Self destructive alias loop"); 296 vrfyqueue = a; 297 } 298 if (InChild) 299 finis(); 300 break; 301 302 case CMDHELP: /* help -- give user info */ 303 if (*p == '\0') 304 p = "SMTP"; 305 help(p); 306 break; 307 308 case CMDNOOP: /* noop -- do nothing */ 309 message("200", "OK"); 310 break; 311 312 case CMDQUIT: /* quit -- leave mail */ 313 message("221", "%s closing connection", HostName); 314 if (InChild) 315 ExitStat = EX_QUIT; 316 finis(); 317 318 case CMDVERB: /* set verbose mode */ 319 Verbose = TRUE; 320 message("200", "Verbose mode"); 321 break; 322 323 case CMDONEX: /* doing one transaction only */ 324 onexact = TRUE; 325 message("200", "Only one transaction"); 326 break; 327 328 # ifdef DEBUG 329 case CMDDBGQSHOW: /* show queues */ 330 printf("Send Queue="); 331 printaddr(CurEnv->e_sendqueue, TRUE); 332 break; 333 334 case CMDDBGDEBUG: /* set debug mode */ 335 tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 336 tTflag(p); 337 message("200", "Debug set"); 338 break; 339 340 case CMDDBGKILL: /* kill the parent */ 341 if (!iswiz()) 342 break; 343 if (kill(MotherPid, SIGTERM) >= 0) 344 message("200", "Mother is dead"); 345 else 346 message("500", "Can't kill Mom"); 347 break; 348 349 case CMDDBGSHELL: /* give us an interactive shell */ 350 if (!iswiz()) 351 break; 352 execl("/bin/csh", "sendmail", 0); 353 execl("/bin/sh", "sendmail", 0); 354 message("500", "Can't"); 355 break; 356 357 case CMDDBGWIZ: /* become a wizard */ 358 if (WizWord != NULL) 359 { 360 char seed[3]; 361 extern char *crypt(); 362 363 strncpy(seed, WizWord, 2); 364 if (strcmp(WizWord, crypt(p, seed)) != 0) 365 { 366 message("500", "You are no wizard!"); 367 break; 368 } 369 } 370 IsWiz = TRUE; 371 message("200", "Please pass, oh mighty wizard"); 372 break; 373 # endif DEBUG 374 375 case CMDERROR: /* unknown command */ 376 message("500", "Command unrecognized"); 377 break; 378 379 default: 380 syserr("smtp: unknown code %d", c->cmdcode); 381 break; 382 } 383 } 384 } 385 /* 386 ** SKIPWORD -- skip a fixed word. 387 ** 388 ** Parameters: 389 ** p -- place to start looking. 390 ** w -- word to skip. 391 ** 392 ** Returns: 393 ** p following w. 394 ** NULL on error. 395 ** 396 ** Side Effects: 397 ** clobbers the p data area. 398 */ 399 400 static char * 401 skipword(p, w) 402 register char *p; 403 char *w; 404 { 405 register char *q; 406 extern bool sameword(); 407 408 /* find beginning of word */ 409 while (isspace(*p)) 410 p++; 411 q = p; 412 413 /* find end of word */ 414 while (*p != '\0' && *p != ':' && !isspace(*p)) 415 p++; 416 while (isspace(*p)) 417 *p++ = '\0'; 418 if (*p != ':') 419 { 420 syntax: 421 message("501", "Syntax error"); 422 Errors++; 423 return (NULL); 424 } 425 *p++ = '\0'; 426 while (isspace(*p)) 427 p++; 428 429 /* see if the input word matches desired word */ 430 if (!sameword(q, w)) 431 goto syntax; 432 433 return (p); 434 } 435 /* 436 ** HELP -- implement the HELP command. 437 ** 438 ** Parameters: 439 ** topic -- the topic we want help for. 440 ** 441 ** Returns: 442 ** none. 443 ** 444 ** Side Effects: 445 ** outputs the help file to message output. 446 */ 447 448 help(topic) 449 char *topic; 450 { 451 register FILE *hf; 452 int len; 453 char buf[MAXLINE]; 454 bool noinfo; 455 456 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 457 { 458 /* no help */ 459 message("502", "HELP not implemented"); 460 return; 461 } 462 463 len = strlen(topic); 464 makelower(topic); 465 noinfo = TRUE; 466 467 while (fgets(buf, sizeof buf, hf) != NULL) 468 { 469 if (strncmp(buf, topic, len) == 0) 470 { 471 register char *p; 472 473 p = index(buf, '\t'); 474 if (p == NULL) 475 p = buf; 476 else 477 p++; 478 fixcrlf(p, TRUE); 479 message("214-", p); 480 noinfo = FALSE; 481 } 482 } 483 484 if (noinfo) 485 message("504", "HELP topic unknown"); 486 else 487 message("214", "End of HELP info"); 488 (void) fclose(hf); 489 } 490 /* 491 ** ISWIZ -- tell us if we are a wizard 492 ** 493 ** If not, print a nasty message. 494 ** 495 ** Parameters: 496 ** none. 497 ** 498 ** Returns: 499 ** TRUE if we are a wizard. 500 ** FALSE if we are not a wizard. 501 ** 502 ** Side Effects: 503 ** Prints a 500 exit stat if we are not a wizard. 504 */ 505 506 bool 507 iswiz() 508 { 509 if (!IsWiz) 510 message("500", "Mere mortals musn't mutter that mantra"); 511 return (IsWiz); 512 } 513 /* 514 ** RUNINCHILD -- return twice -- once in the child, then in the parent again 515 ** 516 ** Parameters: 517 ** label -- a string used in error messages 518 ** 519 ** Returns: 520 ** zero in the child 521 ** one in the parent 522 ** 523 ** Side Effects: 524 ** none. 525 */ 526 527 runinchild(label) 528 char *label; 529 { 530 int childpid; 531 532 childpid = dofork(); 533 if (childpid < 0) 534 { 535 syserr("%s: cannot fork", label); 536 return (1); 537 } 538 if (childpid > 0) 539 { 540 /* parent -- wait for child to complete */ 541 auto int st; 542 int i; 543 544 while ((i = wait(&st)) != childpid) 545 { 546 if (i < 0 && errno != EINTR) 547 break; 548 } 549 if (i < 0) 550 syserr("%s: lost child", label); 551 552 /* if we exited on a QUIT command, complete the process */ 553 if (st == (EX_QUIT << 8)) 554 finis(); 555 556 return (1); 557 } 558 else 559 { 560 /* child */ 561 InChild = TRUE; 562 return (0); 563 } 564 } 565 566 # endif SMTP 567