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