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