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