1 21 package org.jsmtpd.core.receive; 22 23 import java.io.BufferedWriter ; 24 import java.io.IOException ; 25 import java.io.OutputStreamWriter ; 26 import java.net.InetSocketAddress ; 27 import java.net.Socket ; 28 import java.util.Date ; 29 import java.util.Iterator ; 30 import java.util.LinkedList ; 31 import java.util.List ; 32 33 import org.apache.commons.logging.Log; 34 import org.apache.commons.logging.LogFactory; 35 import org.jsmtpd.config.ReadConfig; 36 import org.jsmtpd.core.common.PluginStore; 37 import org.jsmtpd.core.common.acl.IACL; 38 import org.jsmtpd.core.common.io.BareLFException; 39 import org.jsmtpd.core.common.io.InputSizeToBig; 40 import org.jsmtpd.core.common.io.InvalidStreamParserInitialisation; 41 import org.jsmtpd.core.common.io.commandStream.CommandStreamParser; 42 import org.jsmtpd.core.common.io.dataStream.DataStreamParser; 43 import org.jsmtpd.core.common.smtpExtension.IProtocolHandler; 44 import org.jsmtpd.core.common.smtpExtension.ISmtpExtension; 45 import org.jsmtpd.core.common.smtpExtension.SmtpExtensionException; 46 import org.jsmtpd.core.mail.Email; 47 import org.jsmtpd.core.mail.EmailAddress; 48 import org.jsmtpd.core.mail.InvalidAddress; 49 import org.jsmtpd.core.mail.Rcpt; 50 import org.jsmtpd.core.send.QueueService; 51 import org.jsmtpd.tools.DateUtil; 52 53 87 public class ProtocolHandler implements IProtocolHandler { 88 89 92 private Socket sock = null; 93 94 97 private BufferedWriter wr = null; 98 101 private Email mail = new Email(); 102 103 106 private int maxMessageSize = ReadConfig.getInstance().getMaxMessageSize(); 107 110 private int timeout = ReadConfig.getInstance().getConnectionTimeout(); 111 114 private IACL acl = PluginStore.getInstance().getAcl(); 115 116 private Log log = LogFactory.getLog(ProtocolHandler.class); 117 120 private String remote = ""; 121 124 private String localHost = ReadConfig.getInstance().getLocalDomain(); 125 126 130 private QueueService dsvc = QueueService.getInstance(); 131 132 135 private boolean relayed=false; 136 137 140 private boolean secured=false; 141 142 private CommandStreamParser csp; 143 144 private InputIPFilterChecker checker = new InputIPFilterChecker(); 145 146 private List <ISmtpExtension> smtpExtensions=PluginStore.getInstance().getSmtpExtensions(); 147 148 private List <String > commandHistory; 149 155 156 private String authContext = null; 157 158 private int maxRcpt =ReadConfig.getInstance().getMaxRcpt(); 159 160 public void init(Socket sock) { 161 commandHistory=new LinkedList <String >(); 162 remote = ((InetSocketAddress ) sock.getRemoteSocketAddress()).getAddress().getHostAddress(); 164 closeConnection(); 166 reset(); 167 this.sock = sock; 168 169 try { 170 sock.setSoTimeout(timeout * 1000); wr = new BufferedWriter (new OutputStreamWriter (sock.getOutputStream())); 172 runDialog(); 174 } catch (IOException e) { 175 log.error("Network error for " + remote); 176 } catch (EndofProtocol e) { 177 log.info("End service for " + remote); 178 } finally { 179 closeConnection(); 180 reset(); 181 } 182 } 183 184 189 private void runDialog() throws IOException , EndofProtocol { 190 int errors = 0; 193 int lastCommand = -1; 195 int command; 197 198 if (!checker.checkIPAgainstFilters(sock.getInetAddress())) { 199 send(MSG_BLACKLISTED); 200 return; 201 } 202 203 log.info( "Service for " + remote + " running"); 204 send(MSG_HELLO_CODE + localHost + MSG_HELLO_MESG); mail.setReceivedFrom(remote); 207 csp = new CommandStreamParser(sock.getInputStream(), 4096, true); 208 String commandString; 209 try { 210 while ((commandString = csp.readLine()) != null) 211 { 212 command = getCommand(commandString); 213 log.debug("Command: " + commandString); 214 try { 215 if (executePreExtensions(commandString,this)) 216 continue; 217 } catch (SmtpExtensionException e1) { 218 log.error("Failed to execute smtp extension"); 219 break; 220 } 221 switch (command) { 222 case CMD_EHLO: 223 if (smtpExtensions.size()<=0) { 224 send("250 Command ok"); 225 } else { 226 send("250-ok"); 227 for (ISmtpExtension ext : smtpExtensions) { 228 if ( (ext.getWelcome()!=null) && (!ext.getWelcome().equals(""))) 229 send ("250-"+ext.getWelcome()); 230 } 231 send ("250 HELP"); 232 } 233 lastCommand = CMD_HELLO; 234 break; 235 case CMD_HELLO: 236 send("250 Command ok"); 237 lastCommand = CMD_HELLO; 238 break; 239 case CMD_QUIT: 240 lastCommand = CMD_RESET; 241 throw new EndofProtocol(); 242 case CMD_NOOP: 243 send(MSG_OK); 244 break; 245 case CMD_RESET: 246 mail = new Email(); 247 mail.setReceivedFrom(remote); 248 mail.setAuthContext(authContext); send(MSG_OK); 250 lastCommand = CMD_RESET; 251 break; 252 case CMD_MAIL_FROM: 253 if (lastCommand == CMD_RESET || lastCommand == CMD_HELLO) { 254 if (decodeFrom(commandString)) 255 lastCommand = CMD_MAIL_FROM; 256 } else 257 send(MSG_CMD_NOT_ALLOWED); 258 break; 259 260 case CMD_RCPT: 261 if (lastCommand == CMD_MAIL_FROM || lastCommand == CMD_RCPT) { 262 if (decodeRcpt(commandString)) 263 lastCommand = CMD_RCPT; 264 } else { 265 send(MSG_CMD_NOT_ALLOWED); 266 } 267 break; 268 269 case CMD_HELP: 270 send ("214 See rfc 2821"); 271 lastCommand=CMD_HELP; 272 break; 273 274 case CMD_DATA: 275 if (lastCommand == CMD_RCPT) { 276 parseData(); 277 mail = new Email(); 278 mail.setReceivedFrom(remote); 279 mail.setAuthContext(authContext); lastCommand = CMD_RESET; 281 } else { 282 send(MSG_CMD_NOT_ALLOWED); 283 } 284 break; 285 286 default: 287 try { 288 if (!executeExtensions(commandString,this)) { 289 send(MSG_INVALID_CMD); 290 log.error("Invalid command: " + commandString + " from " + remote); 291 errors++; 292 if (errors > 10) 293 return; 294 break; 295 } 296 } catch (IOException e) { 297 throw new EndofProtocol(); 298 } catch (SmtpExtensionException e) { 299 log.error("Error executing SMTP Extensions"); 300 errors++; 301 } 302 } 303 } 304 305 } catch (InputSizeToBig e) { 306 send(MSG_CMD_TO_BIG); 307 return; 308 } catch (BareLFException e) { 309 send(MSG_ERROR_LF); 310 return; 311 } 312 } 313 314 private boolean executeExtensions (String cmd, IProtocolHandler protocol) throws SmtpExtensionException, IOException , InputSizeToBig, BareLFException { 315 for (ISmtpExtension extension : smtpExtensions) { 316 if (extension.smtpTrigger(cmd,protocol)) 317 return true; 318 } 319 return false; 320 } 321 322 private boolean executePreExtensions (String cmd, IProtocolHandler protocol) throws SmtpExtensionException, IOException , InputSizeToBig, BareLFException { 323 boolean over=false; 324 for (ISmtpExtension extension : smtpExtensions) { 325 if (extension.smtpPreTrigger(cmd,protocol)) 326 over=true; 327 } 328 return over; 329 } 330 331 private void parseData() throws IOException { 332 send(MSG_DATA_BEGIN); 333 mail.setArrival(new Date ()); 334 335 try { 336 DataStreamParser dsp = new DataStreamParser(1024 * 512, 1024 * 1024 * maxMessageSize); 337 List <Rcpt> rcpts = mail.getRcpt(); 338 String rc = ""; 339 for (Iterator iter = rcpts.iterator(); iter.hasNext();) { 340 Rcpt element = (Rcpt) iter.next(); 341 rc += "<" + element.getEmailAddress().toString() + ">;"; 342 } 343 dsp.appendString("Received: from " + remote + " by " + localHost + " (Jsmtpd) for " + rc + " " + DateUtil.currentRFCDate()); 344 345 try { 346 dsp.parseInputStream(sock.getInputStream()); 347 mail.setDataBuffer(dsp.getData()); 348 if (!dsvc.queueMail(mail)) 349 send(MSG_NO_SPACE_LEFT); 350 else 351 send(MSG_OK); 352 } catch (InputSizeToBig e1) { 353 send(MSG_ERROR_SIZE); 354 } catch (IOException e1) { 355 throw e1; 356 } catch (BareLFException e1) { 357 send(MSG_ERROR_LF); 358 } 359 360 } catch (InvalidStreamParserInitialisation e) { 361 send(MSG_SAVE_ERROR); 362 } 363 364 } 365 366 private boolean decodeRcpt(String rcpt) throws IOException { 367 if ((rcpt == null) || (rcpt.length() < 10)) 368 return false; 369 370 String rc = rcpt.substring(9).trim().replace("<", "").replace(">", ""); 371 EmailAddress e = null; 372 try { 373 e = EmailAddress.parseAddress(rc); 374 375 } catch (InvalidAddress e1) { 376 send(MSG_USER_INVALID); 377 return false; 378 } 379 if (isAcceptable(e)) { 380 if (mail.getRcpt().size() >= maxRcpt) { 382 send(MSG_TO_MANY_RCPT); 383 return false; 384 } else { 385 mail.addRcpt(e); 386 send(MSG_OK); 387 return true; 388 } 389 } else { 390 send(MSG_USER_UNKOWN); return false; 392 } 393 394 } 395 396 397 398 private boolean decodeFrom(String from) throws IOException { 399 if ((from == null) || (from.length() < 11)) 400 return false; 401 String fr = from.substring(10).trim(); 402 if (fr.contains("<>")) { 403 EmailAddress e = new EmailAddress(); 404 e.setUser("<>"); 405 mail.setFrom(e); 406 send(MSG_OK); 407 return true; 408 } 409 410 fr = fr.replace("<", "").replace(">", ""); 411 EmailAddress e = null; 412 try { 413 e = EmailAddress.parseAddress(fr); 414 mail.setFrom(e); 415 send(MSG_OK); 416 return true; 417 418 } catch (InvalidAddress ia) { 419 send(MSG_USER_INVALID); 420 return false; 421 } 422 423 } 424 425 private boolean isAcceptable(EmailAddress addr) { 426 if (relayed) { 428 log.debug("RCPT: " + addr + " is valid for relayed host :" + mail.getReceivedFrom()+" allowed by SMTP extension(s)"); 429 return true; 430 } 431 432 433 if (acl.isValidRelay(mail.getReceivedFrom())) { 435 log.debug("RCPT: " + addr + " is valid for relayed host :" + mail.getReceivedFrom()); 436 return true; 437 } 438 if (addr.getHost().equals("localhost") || addr.getHost().equals("127.0.0.1")) { 440 log.debug("RCPT is not valid for " + mail.getReceivedFrom() + ", this is for localhost but sender is not to be relayed"); 441 return false; 442 } 443 444 if (acl.isValidAddress(addr)) { 446 log.debug("RCPT " + addr + " is valid (local domain)"); 447 return true; 448 } 449 log.debug("RCPT is not valid for " + mail.getReceivedFrom() + ", this is not from relayed host and not for local domain"); 450 return false; 451 } 452 453 private void closeConnection() { 454 if (wr != null) { 455 try { 456 wr.write("221 Closing channel. Good Bye " + remote + "\r\n"); 457 wr.flush(); 458 log.debug("Closing connection "); 459 } catch (IOException e) { 460 } 462 } 463 } 464 465 private void reset() { 466 relayed=false; 467 secured=false; 468 mail = new Email(); 469 commandHistory=new LinkedList <String >(); 470 authContext=null; 471 472 if (wr != null) { 473 try { 474 wr.close(); 475 wr = null; 476 } catch (IOException e) { 477 e.printStackTrace(); 478 } 479 } 480 if (sock != null) { 481 try { 482 sock.close(); 483 sock = null; 484 } catch (IOException e) { 485 e.printStackTrace(); 486 } 487 } 488 } 489 490 private void send(String msg) throws IOException { 491 wr.write(msg + "\r\n"); 492 wr.flush(); 493 log.debug("Sent: " + msg); 494 } 495 496 private int getCommand(String input) { 497 if ((input == null) || (input.length() < 4)) 498 return CMD_UNKW; 499 String tmp = input.substring(0, 4).toUpperCase(); 500 501 if ("HELO".equals(tmp)) 502 return CMD_HELLO; 503 504 if ("EHLO".equals(tmp)) 505 return CMD_EHLO; 506 507 if ("QUIT".equals(tmp)) 508 return CMD_QUIT; 509 510 if ("MAIL".equals(tmp)) 511 return CMD_MAIL_FROM; 512 513 if ("RCPT".equals(tmp)) 514 return CMD_RCPT; 515 516 if ("DATA".equals(tmp)) 517 return CMD_DATA; 518 519 if ("RSET".equals(tmp)) 520 return CMD_RESET; 521 522 if ("NOOP".equals(tmp)) 523 return CMD_NOOP; 524 525 if ("HELP".equals(tmp)) 526 return CMD_NOOP; 527 528 return CMD_UNKW; 529 } 530 531 public void setRelayed (boolean value) { 532 this.relayed=value; 533 } 534 public Socket getSock() { 535 return sock; 536 } 537 public void setSock(Socket sock) throws IOException { 538 this.sock = sock; 539 csp = new CommandStreamParser(sock.getInputStream(), 4096, true); 541 wr=new BufferedWriter (new OutputStreamWriter (sock.getOutputStream())); 542 } 543 public boolean isSecured() { 544 return secured; 545 } 546 public void setSecured(boolean secured) { 547 this.secured = secured; 548 } 549 550 551 public List getCommandHistory() { 552 return commandHistory; 553 } 554 555 public void addCommandHistory(String command) { 556 if (commandHistory.size()>50) 557 commandHistory.remove(commandHistory.size()); 558 commandHistory.add(0,command); 559 } 560 561 public void setAuthContext(String context) { 562 this.authContext=context; 563 mail.setAuthContext(context); 564 } 565 566 private static final String MSG_HELLO_CODE = "220 "; 567 private static final String MSG_HELLO_MESG = " Welcome to Jsmtpd."; 568 private static final String MSG_OK = "250 Command OK"; 569 private static final String MSG_CMD_NOT_ALLOWED = "503 Command not allowed"; 570 private static final String MSG_USER_UNKOWN = "550 User does not exists, and you are not relayed"; 571 private static final String MSG_USER_INVALID = "501 Address is not valid"; private static final String MSG_DATA_BEGIN = "354 Listening for data input"; 573 private static final String MSG_SAVE_ERROR = "500 Error processing message"; 574 private static final String MSG_INVALID_CMD = "500 Invalid command"; 575 private static final String MSG_ERROR_SIZE = "552 Message size is to big"; 576 private static final String MSG_ERROR_LF = "551 Bare LF in data"; 577 private static final String MSG_CMD_TO_BIG = "500 Line size is to big"; 578 private static final String MSG_TO_MANY_RCPT = "552 To many RCPT"; 579 private static final String MSG_BLACKLISTED = "421 Your IP is blacklisted"; private static final String MSG_NO_SPACE_LEFT = "552 No space left on storage"; 581 582 private static final int CMD_HELLO = 0; 583 private static final int CMD_EHLO = 10; 584 private static final int CMD_QUIT = 1; 585 private static final int CMD_MAIL_FROM = 2; 586 private static final int CMD_RCPT = 3; 587 private static final int CMD_DATA = 4; 588 private static final int CMD_RESET = 6; 589 private static final int CMD_NOOP = 7; 590 private static final int CMD_UNKW = -1; 591 private static final int CMD_HELP = 8; 592 593 public boolean isRelayed() { 594 return relayed; 595 } 596 597 598 public Email getMail() { 599 return mail; 600 } 601 602 603 604 } | Popular Tags |