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