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