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