1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 # include "sendmail.h" 10 11 #ifndef lint 12 #ifdef SMTP 13 static char sccsid[] = "@(#)usersmtp.c 5.20.1.2 (Berkeley) 05/29/92 (with SMTP)"; 14 #else 15 static char sccsid[] = "@(#)usersmtp.c 5.20.1.2 (Berkeley) 05/29/92 (without SMTP)"; 16 #endif 17 #endif /* not lint */ 18 19 # include <sysexits.h> 20 # include <errno.h> 21 22 # ifdef SMTP 23 24 /* 25 ** USERSMTP -- run SMTP protocol from the user end. 26 ** 27 ** This protocol is described in RFC821. 28 */ 29 30 #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 31 #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 32 #define SMTPCLOSING 421 /* "Service Shutting Down" */ 33 34 char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 35 char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 36 char SmtpError[MAXLINE] = ""; /* save failure error messages */ 37 int SmtpPid; /* pid of mailer */ 38 /* 39 ** SMTPINIT -- initialize SMTP. 40 ** 41 ** Opens the connection and sends the initial protocol. 42 ** 43 ** Parameters: 44 ** m -- mailer to create connection to. 45 ** pvp -- pointer to parameter vector to pass to 46 ** the mailer. 47 ** 48 ** Returns: 49 ** appropriate exit status -- EX_OK on success. 50 ** If not EX_OK, it should close the connection. 51 ** 52 ** Side Effects: 53 ** creates connection and sends initial protocol. 54 */ 55 56 jmp_buf CtxGreeting; 57 58 smtpinit(m, mci, pvp) 59 struct mailer *m; 60 MCONINFO *mci; 61 char **pvp; 62 { 63 register int r; 64 EVENT *gte; 65 register STAB *st; 66 char buf[MAXNAME]; 67 static int greettimeout(); 68 extern STAB *stab(); 69 extern MCONINFO *openmailer(); 70 71 /* 72 ** Open the connection to the mailer. 73 */ 74 75 if (mci->mci_state == MCIS_OPEN) 76 syserr("smtpinit: already open"); 77 78 mci->mci_state = MCIS_CLOSED; 79 SmtpError[0] = '\0'; 80 SmtpPhase = "user open"; 81 setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase); 82 mci = openmailer(m, pvp, (ADDRESS *) NULL, TRUE); 83 if (SmtpPid < 0) 84 { 85 if (tTd(18, 1)) 86 printf("smtpinit: cannot open %s: stat %d errno %d\n", 87 pvp[0], ExitStat, errno); 88 if (CurEnv->e_xfp != NULL) 89 { 90 register char *p; 91 extern char *errstring(); 92 extern char *statstring(); 93 94 if (errno == 0) 95 { 96 p = statstring(ExitStat); 97 fprintf(CurEnv->e_xfp, 98 "%.3s %s.%s... %s\n", 99 p, pvp[1], m->m_name, p); 100 } 101 else 102 { 103 r = errno; 104 fprintf(CurEnv->e_xfp, 105 "421 %s.%s... Deferred: %s\n", 106 pvp[1], m->m_name, errstring(errno)); 107 errno = r; 108 } 109 } 110 return (ExitStat); 111 } 112 mci->mci_state = MCIS_OPEN; 113 114 /* 115 ** Get the greeting message. 116 ** This should appear spontaneously. Give it five minutes to 117 ** happen. 118 */ 119 120 if (setjmp(CtxGreeting) != 0) 121 goto tempfail1; 122 gte = setevent((time_t) 300, greettimeout, 0); 123 SmtpPhase = "greeting wait"; 124 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 125 r = reply(m, mci); 126 clrevent(gte); 127 if (r < 0 || REPLYTYPE(r) != 2) 128 goto tempfail1; 129 130 /* 131 ** Send the HELO command. 132 ** My mother taught me to always introduce myself. 133 */ 134 135 smtpmessage("HELO %s", m, MyHostName); 136 SmtpPhase = "HELO wait"; 137 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 138 r = reply(m, mci); 139 if (r < 0) 140 goto tempfail1; 141 else if (REPLYTYPE(r) == 5) 142 goto unavailable; 143 else if (REPLYTYPE(r) != 2) 144 goto tempfail1; 145 146 /* 147 ** If this is expected to be another sendmail, send some internal 148 ** commands. 149 */ 150 151 if (bitnset(M_INTERNAL, m->m_flags)) 152 { 153 /* tell it to be verbose */ 154 smtpmessage("VERB", m); 155 r = reply(m, mci); 156 if (r < 0) 157 goto tempfail2; 158 159 /* tell it we will be sending one transaction only */ 160 smtpmessage("ONEX", m); 161 r = reply(m, mci); 162 if (r < 0) 163 goto tempfail2; 164 } 165 166 /* 167 ** Send the MAIL command. 168 ** Designates the sender. 169 */ 170 171 expand("\001<", buf, &buf[sizeof buf - 1], CurEnv); 172 if (CurEnv->e_from.q_mailer == LocalMailer || 173 !bitnset(M_FROMPATH, m->m_flags)) 174 { 175 smtpmessage("MAIL From:<%s>", m, buf); 176 } 177 else 178 { 179 smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName, 180 buf[0] == '@' ? ',' : ':', buf); 181 } 182 SmtpPhase = "MAIL wait"; 183 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 184 r = reply(m, mci); 185 if (r < 0 || REPLYTYPE(r) == 4) 186 goto tempfail2; 187 else if (r == 250) 188 return (EX_OK); 189 else if (r == 552) 190 goto unavailable; 191 192 /* protocol error -- close up */ 193 smtpquit(m, mci); 194 return (EX_PROTOCOL); 195 196 tempfail1: 197 /* log this as an error to avoid sure-to-be-void connections */ 198 mci->mci_exitstat = EX_TEMPFAIL; 199 mci->mci_errno = errno; 200 201 tempfail2: 202 /* signal a temporary failure */ 203 smtpquit(m, mci); 204 return (EX_TEMPFAIL); 205 206 unavailable: 207 /* signal service unavailable */ 208 smtpquit(m, mci); 209 return (EX_UNAVAILABLE); 210 } 211 212 213 static 214 greettimeout() 215 { 216 /* timeout reading the greeting message */ 217 longjmp(CtxGreeting, 1); 218 } 219 /* 220 ** SMTPRCPT -- designate recipient. 221 ** 222 ** Parameters: 223 ** to -- address of recipient. 224 ** m -- the mailer we are sending to. 225 ** 226 ** Returns: 227 ** exit status corresponding to recipient status. 228 ** 229 ** Side Effects: 230 ** Sends the mail via SMTP. 231 */ 232 233 smtprcpt(to, m, mci) 234 ADDRESS *to; 235 register MAILER *m; 236 MCONINFO *mci; 237 { 238 register int r; 239 extern char *remotename(); 240 241 smtpmessage("RCPT To:<%s>", m, to->q_user); 242 243 SmtpPhase = "RCPT wait"; 244 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 245 r = reply(m, mci); 246 if (r < 0 || REPLYTYPE(r) == 4) 247 return (EX_TEMPFAIL); 248 else if (REPLYTYPE(r) == 2) 249 return (EX_OK); 250 else if (r == 550 || r == 551 || r == 553) 251 return (EX_NOUSER); 252 else if (r == 552 || r == 554) 253 return (EX_UNAVAILABLE); 254 return (EX_PROTOCOL); 255 } 256 /* 257 ** SMTPDATA -- send the data and clean up the transaction. 258 ** 259 ** Parameters: 260 ** m -- mailer being sent to. 261 ** e -- the envelope for this message. 262 ** 263 ** Returns: 264 ** exit status corresponding to DATA command. 265 ** 266 ** Side Effects: 267 ** none. 268 */ 269 270 smtpdata(m, mci, e) 271 struct mailer *m; 272 register MCONINFO *mci; 273 register ENVELOPE *e; 274 { 275 register int r; 276 277 /* 278 ** Send the data. 279 ** First send the command and check that it is ok. 280 ** Then send the data. 281 ** Follow it up with a dot to terminate. 282 ** Finally get the results of the transaction. 283 */ 284 285 /* send the command and check ok to proceed */ 286 smtpmessage("DATA", m); 287 SmtpPhase = "DATA wait"; 288 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 289 r = reply(m, mci); 290 if (r < 0 || REPLYTYPE(r) == 4) 291 return (EX_TEMPFAIL); 292 else if (r == 554) 293 return (EX_UNAVAILABLE); 294 else if (r != 354) 295 return (EX_PROTOCOL); 296 297 /* now output the actual message */ 298 (*e->e_puthdr)(mci->mci_out, m, CurEnv); 299 putline("\n", mci->mci_out, m); 300 (*e->e_putbody)(mci->mci_out, m, CurEnv); 301 302 /* terminate the message */ 303 fprintf(mci->mci_out, ".%s", m->m_eol); 304 if (Verbose && !HoldErrs) 305 nmessage(Arpa_Info, ">>> ."); 306 307 /* check for the results of the transaction */ 308 SmtpPhase = "result wait"; 309 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 310 r = reply(m, mci); 311 if (r < 0 || REPLYTYPE(r) == 4) 312 return (EX_TEMPFAIL); 313 else if (r == 250) 314 return (EX_OK); 315 else if (r == 552 || r == 554) 316 return (EX_UNAVAILABLE); 317 return (EX_PROTOCOL); 318 } 319 /* 320 ** SMTPQUIT -- close the SMTP connection. 321 ** 322 ** Parameters: 323 ** m -- a pointer to the mailer. 324 ** 325 ** Returns: 326 ** none. 327 ** 328 ** Side Effects: 329 ** sends the final protocol and closes the connection. 330 */ 331 332 smtpquit(mci, m) 333 register MCONINFO *mci; 334 register MAILER *m; 335 { 336 int i; 337 338 /* if the connection is already closed, don't bother */ 339 if (mci->mci_state == MCIS_CLOSED) 340 return; 341 342 /* send the quit message if not a forced quit */ 343 if (mci->mci_state == MCIS_OPEN || mci->mci_state == MCIS_SSD) 344 { 345 smtpmessage("QUIT", m); 346 (void) reply(m, mci); 347 if (mci->mci_state == MCIS_CLOSED) 348 return; 349 } 350 351 /* now actually close the connection and pick up the zombie */ 352 i = endmailer(mci, m->m_argv[0]); 353 if (i != EX_OK) 354 syserr("smtpquit %s: stat %d", m->m_argv[0], i); 355 } 356 /* 357 ** REPLY -- read arpanet reply 358 ** 359 ** Parameters: 360 ** m -- the mailer we are reading the reply from. 361 ** 362 ** Returns: 363 ** reply code it reads. 364 ** 365 ** Side Effects: 366 ** flushes the mail file. 367 */ 368 369 reply(mci, m) 370 MCONINFO *mci; 371 MAILER *m; 372 { 373 (void) fflush(mci->mci_out); 374 375 if (tTd(18, 1)) 376 printf("reply\n"); 377 378 /* 379 ** Read the input line, being careful not to hang. 380 */ 381 382 for (;;) 383 { 384 register int r; 385 register char *p; 386 387 /* actually do the read */ 388 if (CurEnv->e_xfp != NULL) 389 (void) fflush(CurEnv->e_xfp); /* for debugging */ 390 391 /* if we are in the process of closing just give the code */ 392 if (mci->mci_state == MCIS_CLOSED) 393 return (SMTPCLOSING); 394 395 /* get the line from the other side */ 396 p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, mci->mci_in); 397 if (p == NULL) 398 { 399 extern char MsgBuf[]; /* err.c */ 400 extern char Arpa_TSyserr[]; /* conf.c */ 401 402 /* if the remote end closed early, fake an error */ 403 if (errno == 0) 404 # ifdef ECONNRESET 405 errno = ECONNRESET; 406 # else ECONNRESET 407 errno = EPIPE; 408 # endif ECONNRESET 409 410 message(Arpa_TSyserr, "reply: read error"); 411 /* if debugging, pause so we can see state */ 412 if (tTd(18, 100)) 413 pause(); 414 # ifdef LOG 415 syslog(LOG_INFO, "%s", &MsgBuf[4]); 416 # endif LOG 417 mci->mci_state = MCIS_CLOSED; 418 smtpquit(m); 419 return (-1); 420 } 421 fixcrlf(SmtpReplyBuffer, TRUE); 422 423 if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 424 { 425 /* serious error -- log the previous command */ 426 if (SmtpMsgBuffer[0] != '\0') 427 fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 428 SmtpMsgBuffer[0] = '\0'; 429 430 /* now log the message as from the other side */ 431 fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 432 } 433 434 /* display the input for verbose mode */ 435 if (Verbose && !HoldErrs) 436 nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 437 438 /* if continuation is required, we can go on */ 439 if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 440 continue; 441 442 /* decode the reply code */ 443 r = atoi(SmtpReplyBuffer); 444 445 /* extra semantics: 0xx codes are "informational" */ 446 if (r < 100) 447 continue; 448 449 /* reply code 421 is "Service Shutting Down" */ 450 if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 451 { 452 /* send the quit protocol */ 453 mci->mci_state = MCIS_SSD; 454 smtpquit(m); 455 } 456 457 /* save temporary failure messages for posterity */ 458 if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 459 (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 460 461 return (r); 462 } 463 } 464 /* 465 ** SMTPMESSAGE -- send message to server 466 ** 467 ** Parameters: 468 ** f -- format 469 ** m -- the mailer to control formatting. 470 ** a, b, c -- parameters 471 ** 472 ** Returns: 473 ** none. 474 ** 475 ** Side Effects: 476 ** writes message to mci->mci_out. 477 */ 478 479 /*VARARGS1*/ 480 smtpmessage(f, m, mci, a, b, c) 481 char *f; 482 MAILER *m; 483 MCONINFO *mci; 484 { 485 (void) sprintf(SmtpMsgBuffer, f, a, b, c); 486 if (tTd(18, 1) || (Verbose && !HoldErrs)) 487 nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 488 if (mci->mci_out != NULL) 489 fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 490 m == 0 ? "\r\n" : m->m_eol); 491 } 492 493 # endif SMTP 494