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