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.15 (Berkeley) 06/01/90 (with SMTP)"; 14 #else 15 static char sccsid[] = "@(#)usersmtp.c 5.15 (Berkeley) 06/01/90 (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 extern 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 tempfail; 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 tempfail; 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 tempfail; 147 else if (REPLYTYPE(r) == 5) 148 goto unavailable; 149 else if (REPLYTYPE(r) != 2) 150 goto tempfail; 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 tempfail; 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 tempfail; 170 } 171 172 /* 173 ** Send the MAIL command. 174 ** Designates the sender. 175 */ 176 177 expand("\001g", 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 tempfail; 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 tempfail: 204 smtpquit(m); 205 return (EX_TEMPFAIL); 206 207 /* signal service unavailable */ 208 unavailable: 209 smtpquit(m); 210 return (EX_UNAVAILABLE); 211 } 212 213 214 static 215 greettimeout() 216 { 217 /* timeout reading the greeting message */ 218 longjmp(CtxGreeting, 1); 219 } 220 /* 221 ** SMTPRCPT -- designate recipient. 222 ** 223 ** Parameters: 224 ** to -- address of recipient. 225 ** m -- the mailer we are sending to. 226 ** 227 ** Returns: 228 ** exit status corresponding to recipient status. 229 ** 230 ** Side Effects: 231 ** Sends the mail via SMTP. 232 */ 233 234 smtprcpt(to, m) 235 ADDRESS *to; 236 register MAILER *m; 237 { 238 register int r; 239 extern char *remotename(); 240 241 smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); 242 243 SmtpPhase = "RCPT wait"; 244 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 245 r = reply(m); 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, e) 271 struct mailer *m; 272 register ENVELOPE *e; 273 { 274 register int r; 275 276 /* 277 ** Send the data. 278 ** First send the command and check that it is ok. 279 ** Then send the data. 280 ** Follow it up with a dot to terminate. 281 ** Finally get the results of the transaction. 282 */ 283 284 /* send the command and check ok to proceed */ 285 smtpmessage("DATA", m); 286 SmtpPhase = "DATA wait"; 287 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 288 r = reply(m); 289 if (r < 0 || REPLYTYPE(r) == 4) 290 return (EX_TEMPFAIL); 291 else if (r == 554) 292 return (EX_UNAVAILABLE); 293 else if (r != 354) 294 return (EX_PROTOCOL); 295 296 /* now output the actual message */ 297 (*e->e_puthdr)(SmtpOut, m, CurEnv); 298 putline("\n", SmtpOut, m); 299 (*e->e_putbody)(SmtpOut, m, CurEnv); 300 301 /* terminate the message */ 302 fprintf(SmtpOut, ".%s", m->m_eol); 303 if (Verbose && !HoldErrs) 304 nmessage(Arpa_Info, ">>> ."); 305 306 /* check for the results of the transaction */ 307 SmtpPhase = "result wait"; 308 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 309 r = reply(m); 310 if (r < 0 || REPLYTYPE(r) == 4) 311 return (EX_TEMPFAIL); 312 else if (r == 250) 313 return (EX_OK); 314 else if (r == 552 || r == 554) 315 return (EX_UNAVAILABLE); 316 return (EX_PROTOCOL); 317 } 318 /* 319 ** SMTPQUIT -- close the SMTP connection. 320 ** 321 ** Parameters: 322 ** m -- a pointer to the mailer. 323 ** 324 ** Returns: 325 ** none. 326 ** 327 ** Side Effects: 328 ** sends the final protocol and closes the connection. 329 */ 330 331 smtpquit(m) 332 register MAILER *m; 333 { 334 int i; 335 336 /* if the connection is already closed, don't bother */ 337 if (SmtpIn == NULL) 338 return; 339 340 /* send the quit message if not a forced quit */ 341 if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 342 { 343 smtpmessage("QUIT", m); 344 (void) reply(m); 345 if (SmtpState == SMTP_CLOSED) 346 return; 347 } 348 349 /* now actually close the connection */ 350 (void) fclose(SmtpIn); 351 (void) fclose(SmtpOut); 352 SmtpIn = SmtpOut = NULL; 353 SmtpState = SMTP_CLOSED; 354 355 /* and pick up the zombie */ 356 i = endmailer(SmtpPid, m->m_argv[0]); 357 if (i != EX_OK) 358 syserr("smtpquit %s: stat %d", m->m_argv[0], i); 359 } 360 /* 361 ** REPLY -- read arpanet reply 362 ** 363 ** Parameters: 364 ** m -- the mailer we are reading the reply from. 365 ** 366 ** Returns: 367 ** reply code it reads. 368 ** 369 ** Side Effects: 370 ** flushes the mail file. 371 */ 372 373 reply(m) 374 MAILER *m; 375 { 376 (void) fflush(SmtpOut); 377 378 if (tTd(18, 1)) 379 printf("reply\n"); 380 381 /* 382 ** Read the input line, being careful not to hang. 383 */ 384 385 for (;;) 386 { 387 register int r; 388 register char *p; 389 390 /* actually do the read */ 391 if (CurEnv->e_xfp != NULL) 392 (void) fflush(CurEnv->e_xfp); /* for debugging */ 393 394 /* if we are in the process of closing just give the code */ 395 if (SmtpState == SMTP_CLOSED) 396 return (SMTPCLOSING); 397 398 /* get the line from the other side */ 399 p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 400 if (p == NULL) 401 { 402 extern char MsgBuf[]; /* err.c */ 403 extern char Arpa_TSyserr[]; /* conf.c */ 404 405 /* if the remote end closed early, fake an error */ 406 if (errno == 0) 407 # ifdef ECONNRESET 408 errno = ECONNRESET; 409 # else ECONNRESET 410 errno = EPIPE; 411 # endif ECONNRESET 412 413 message(Arpa_TSyserr, "reply: read error"); 414 /* if debugging, pause so we can see state */ 415 if (tTd(18, 100)) 416 pause(); 417 # ifdef LOG 418 syslog(LOG_INFO, "%s", &MsgBuf[4]); 419 # endif LOG 420 SmtpState = SMTP_CLOSED; 421 smtpquit(m); 422 return (-1); 423 } 424 fixcrlf(SmtpReplyBuffer, TRUE); 425 426 if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 427 { 428 /* serious error -- log the previous command */ 429 if (SmtpMsgBuffer[0] != '\0') 430 fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 431 SmtpMsgBuffer[0] = '\0'; 432 433 /* now log the message as from the other side */ 434 fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 435 } 436 437 /* display the input for verbose mode */ 438 if (Verbose && !HoldErrs) 439 nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 440 441 /* if continuation is required, we can go on */ 442 if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 443 continue; 444 445 /* decode the reply code */ 446 r = atoi(SmtpReplyBuffer); 447 448 /* extra semantics: 0xx codes are "informational" */ 449 if (r < 100) 450 continue; 451 452 /* reply code 421 is "Service Shutting Down" */ 453 if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 454 { 455 /* send the quit protocol */ 456 SmtpState = SMTP_SSD; 457 smtpquit(m); 458 } 459 460 /* save temporary failure messages for posterity */ 461 if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 462 (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 463 464 return (r); 465 } 466 } 467 /* 468 ** SMTPMESSAGE -- send message to server 469 ** 470 ** Parameters: 471 ** f -- format 472 ** m -- the mailer to control formatting. 473 ** a, b, c -- parameters 474 ** 475 ** Returns: 476 ** none. 477 ** 478 ** Side Effects: 479 ** writes message to SmtpOut. 480 */ 481 482 /*VARARGS1*/ 483 smtpmessage(f, m, a, b, c) 484 char *f; 485 MAILER *m; 486 { 487 (void) sprintf(SmtpMsgBuffer, f, a, b, c); 488 if (tTd(18, 1) || (Verbose && !HoldErrs)) 489 nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 490 if (SmtpOut != NULL) 491 fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, 492 m == 0 ? "\r\n" : m->m_eol); 493 } 494 495 # endif SMTP 496