public void IsLocalによるドメインに属するかどうかの確認(string mailaddress, string[] domainList, bool expected) { //setUp var sut = new MailAddress(mailaddress); //exercise var actual = sut.IsLocal(domainList.ToList()); //verify Assert.That(actual, Is.EqualTo(expected)); }
//代表アドレスの種類判定 public MlAddrKind GetKind(MailAddress mailAddress) { if (mailAddress.IsLocal(DomainList)) { //「投稿アドレス」 if (mailAddress.User.ToUpper() == Name.ToUpper()) return MlAddrKind.Post; //「制御アドレス」 if (mailAddress.User.ToUpper() == (Name + "-ctl").ToUpper()) return MlAddrKind.Ctrl; //「管理者アドレス」 if (mailAddress.User.ToUpper() == (Name + "-admin").ToUpper()) return MlAddrKind.Admin; } return MlAddrKind.None;//無効 }
//接続単位の処理 protected override void OnSubThread(SockObj sockObj) { var sockTcp = (SockTcp)sockObj; //WebApi関連 if (!Kernel.WebApi.ServiceSmtp) { if (sockTcp != null) sockTcp.Close(); return; } //グリーティングメッセージの表示 sockTcp.AsciiSend("220 " + Kernel.ChangeTag((string)Conf.Get("bannerMessage"))); var checkParam = new CheckParam((bool)Conf.Get("useNullFrom"), (bool)Conf.Get("useNullDomain")); var session = new Session(); SmtpAuth smtpAuth = null; var useEsmtp = (bool)Conf.Get("useEsmtp"); if (useEsmtp) { if (_smtpAuthRange.IsHit(sockTcp.RemoteIp)) { var usePlain = (bool)Conf.Get("useAuthPlain"); var useLogin = (bool)Conf.Get("useAuthLogin"); var useCramMd5 = (bool)Conf.Get("useAuthCramMD5"); smtpAuth = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); } } //受信サイズ制限 var sizeLimit = (int)Conf.Get("sizeLimit"); //Ver5.0.0-b8 Frmo:偽造の拒否 var useCheckFrom = (bool)Conf.Get("useCheckFrom"); while (IsLife()) { Thread.Sleep(0); var cmd = recvCmd(sockTcp); if (cmd == null){ break;//切断された } if (cmd.Str == "") { Thread.Sleep(100);//受信待機中 continue; } var smtpCmd = new SmtpCmd(cmd); //WebApi関連 var responseSmtp = Kernel.WebApi.ResponseSmtp(cmd.CmdStr); if (responseSmtp != -1){ sockTcp.AsciiSend(string.Format("{0} WebAPI response", responseSmtp)); continue; } if (smtpCmd.Kind == SmtpCmdKind.Unknown) {//無効コマンド //SMTP認証 if (smtpAuth != null) { if (!smtpAuth.IsFinish) { var ret = smtpAuth.Job(smtpCmd.Str); if (ret != null) { sockTcp.AsciiSend(ret); continue; } } } sockTcp.AsciiSend(string.Format("500 command not understood: {0}", smtpCmd.Str)); //無効コマンドが10回続くと不正アクセスとして切断する session.UnknownCmdCounter++; if (session.UnknownCmdCounter > 10) { Logger.Set(LogKind.Secure, sockTcp, 54, string.Format("unknownCmdCount={0}", session.UnknownCmdCounter)); break; } continue; } session.UnknownCmdCounter = 0; //不正でない場合クリアする //QUIT・NOOP・RSETはいつでも受け付ける if (smtpCmd.Kind == SmtpCmdKind.Quit) { sockTcp.AsciiSend("221 closing connection"); break; } if (smtpCmd.Kind == SmtpCmdKind.Noop) { sockTcp.AsciiSend("250 OK"); continue; } if (smtpCmd.Kind == SmtpCmdKind.Rset) { session.Rest(); sockTcp.AsciiSend("250 Reset state"); continue; } //下記のコマンド以外は、SMTP認証の前には使用できない if (smtpCmd.Kind != SmtpCmdKind.Noop && smtpCmd.Kind != SmtpCmdKind.Helo && smtpCmd.Kind != SmtpCmdKind.Ehlo && smtpCmd.Kind != SmtpCmdKind.Rset) { if (smtpAuth != null) { if (!smtpAuth.IsFinish) { sockTcp.AsciiSend("530 Authentication required."); continue; } } } if (smtpCmd.Kind == SmtpCmdKind.Helo || smtpCmd.Kind == SmtpCmdKind.Ehlo) { if (session.Hello != null) {//HELO/EHLOは1回しか受け取らない sockTcp.AsciiSend(string.Format("503 {0} Duplicate HELO/EHLO", Kernel.ServerName)); continue; } if (smtpCmd.ParamList.Count < 1) { sockTcp.AsciiSend(string.Format("501 {0} requires domain address", smtpCmd.Kind.ToString().ToUpper())); continue; } session.Helo(smtpCmd.ParamList[0]); Logger.Set(LogKind.Normal, sockTcp, 1, string.Format("{0} {1} from {2}[{3}]", smtpCmd.Kind.ToString().ToUpper(), session.Hello, sockObj.RemoteHostname, sockTcp.RemoteAddress)); if (smtpCmd.Kind == SmtpCmdKind.Ehlo) { sockTcp.AsciiSend(string.Format("250-{0} Helo {1}[{2}], Pleased to meet you.", Kernel.ServerName, sockObj.RemoteHostname, sockObj.RemoteAddress)); sockTcp.AsciiSend("250-8BITMIME"); sockTcp.AsciiSend(string.Format("250-SIZE={0}", sizeLimit)); if (smtpAuth != null) { string ret = smtpAuth.EhloStr();//SMTP認証に関するhelp文字列の取得 if (ret != null) { sockTcp.AsciiSend(ret); } } sockTcp.AsciiSend("250 HELP"); } else { sockTcp.AsciiSend(string.Format("250 {0} Helo {1}[{2}], Pleased to meet you.", Kernel.ServerName, sockObj.RemoteHostname, sockObj.RemoteAddress)); } continue; } if (smtpCmd.Kind == SmtpCmdKind.Mail) { if (!checkParam.Mail(smtpCmd.ParamList)){ sockTcp.AsciiSend(checkParam.Message); continue; } session.Mail(new MailAddress(smtpCmd.ParamList[1]));//MAILコマンドを取得完了(""もあり得る) sockTcp.AsciiSend(string.Format("250 {0}... Sender ok", smtpCmd.ParamList[1])); continue; } if (smtpCmd.Kind == SmtpCmdKind.Rcpt) { if (session.From == null) {//RCPTの前にMAILコマンドが必要 sockTcp.AsciiSend("503 Need MAIL before RCPT"); continue; } if (!checkParam.Rcpt(smtpCmd.ParamList)) { sockTcp.AsciiSend(checkParam.Message); continue; } var mailAddress = new MailAddress(smtpCmd.ParamList[1]); if (mailAddress.Domain == "") {//ドメイン指定の無い場合は、自ドメイン宛と判断する mailAddress = new MailAddress(mailAddress.User, DomainList[0]); } //自ドメイン宛かどうかの確認 if (mailAddress.IsLocal(DomainList)) { //Ver5.0.0-b4 エリアスで指定したユーザ名の確認 if (!Alias.IsUser(mailAddress.User)) { //有効なユーザかどうかの確認 if (!Kernel.MailBox.IsUser(mailAddress.User)) { //Ver_Ml //有効なメーリングリスト名かどうかの確認 //********************************************************************** //Ver_Ml //********************************************************************** //#if ML_SERVER if(!_mlList.IsUser(mailAddress)){ this.Logger.Set(LogKind.Secure,sockTcp,6,mailAddress.User); sockTcp.AsciiSend(string.Format("550 {0}... User unknown",mailAddress.User)); continue; } //#else // Logger.Set(LogKind.Secure, sockTcp, 6, mailAddress.User); // sockTcp.AsciiSend(string.Format("550 {0}... User unknown", mailAddress.User)); // continue; //#endif //********************************************************************** } } } else {//中継(リレー)が許可されているかどうかのチェック if (!_popBeforeSmtp.Auth(sockObj.RemoteIp)) { //Allow及びDenyリストで中継(リレー)が許可されているかどうかのチェック if (!_relay.IsAllow(sockObj.RemoteIp)) { sockTcp.AsciiSend(string.Format("553 {0}... Relay operation rejected", mailAddress)); continue; } } } //メールアドレスをRCPTリストへ追加する session.Rcpt(mailAddress); sockTcp.AsciiSend(string.Format("250 {0}... Recipient ok", mailAddress)); continue; } if (smtpCmd.Kind == SmtpCmdKind.Data) { if (session.From == null) { sockTcp.AsciiSend("503 Need MAIL command"); continue; } if (session.To.Count == 0) { sockTcp.AsciiSend("503 Need RCPT (recipient)"); continue; } sockTcp.AsciiSend("354 Enter mail,end with \".\" on a line by ltself"); var data = new Data(sizeLimit); if(!data.Recv(sockTcp,20,Logger,this)){ Thread.Sleep(1000); break; } //以降は、メール受信完了の場合 if (useCheckFrom) {//Frmo:偽造の拒否 var mailAddress = new MailAddress(data.Mail.GetHeader("From")); if (mailAddress.User == "") { Logger.Set(LogKind.Secure, sockTcp, 52, string.Format("From:{0}", mailAddress)); sockTcp.AsciiSend("530 There is not an email address in a local user"); continue; } //ローカルドメインでない場合は拒否する if (!mailAddress.IsLocal(DomainList)) { Logger.Set(LogKind.Secure, sockTcp, 28, string.Format("From:{0}", mailAddress)); sockTcp.AsciiSend("530 There is not an email address in a local domain"); continue; } //有効なユーザでない場合拒否する if (!Kernel.MailBox.IsUser(mailAddress.User)) { Logger.Set(LogKind.Secure, sockTcp, 29, string.Format("From:{0}", mailAddress)); sockTcp.AsciiSend("530 There is not an email address in a local user"); continue; } } //ヘッダの変換及び追加 _changeHeader.Exec(data.Mail, Logger); //テンポラリバッファの内容でMailオブジェクトを生成する var error = false; foreach (var to in Alias.Reflection(session.To, Logger)) { if (!MailSave2(session.From, to, data.Mail, sockTcp.RemoteHostname, sockTcp.RemoteIp)) {//MLとそれ以外を振り分けて保存する error = true; break; } } sockTcp.AsciiSend(error ? "554 MailBox Error" : "250 OK"); session.To.Clear(); } } if (sockTcp != null) sockTcp.Close(); }
//Server及びMlから使用される //メールの保存(宛先はML以外であることが確定してから使用する) //テスト用のモックオブジェクト(TsMailSaveでSave()をオーバーライドできるようにvirtualにする public virtual bool Save(MailAddress from, MailAddress to, Mail orgMail, string host, Ip addr) { //Mailのヘッダ内容等を変更するので、この関数内だけの変更にとどめるため、テンポラリを作成する var mail = new Mail(); //orgMail.CreateClone(); mail.Init2(orgMail.GetBytes()); //ユニークなID文字列の生成 var uidStr = string.Format("bjd.{0:D20}.{1:D3}", DateTime.Now.Ticks, _idCounter++); //日付文字列の生成 //var date = Util.LocalTime2Str(DateTime.Now); //Receivedヘッダの追加 mail.AddHeader("Received", _receivedHeader.Get(to, host, addr)); // //Message-Idの追加 // if (null == mail.GetHeader("Message-ID")) // mail.AddHeader("Message-ID", string.Format("<{0}@{1}>", uidStr, _domainList[0])); // //Fromの追加 // if (null == mail.GetHeader("From")) // mail.AddHeader("From", string.Format("<{0}>", @from)); // //Dateの追加 // if (null == mail.GetHeader("Date")) // mail.AddHeader("Date", string.Format("{0}", date)); //ローカル宛(若しくはローカルファイル) if (to.IsLocal(_domainList)) { //ローカル保存の場合は、X-UIDLを追加する mail.AddHeader("X-UIDL", uidStr); //ヘッダを追加してサイズが変わるので、ここで初期化する var mailInfo = new MailInfo(uidStr, mail.Length, host, addr, from, to); if (to.IsFile()) { //ローカルファイルの場合(直接ファイルにAppendする) if (!_localBox.Save(to,mail,mailInfo)){ return false; } } else { //ローカルユーザの場合(メールボックスへSaveする) if (!_mailBox.Save(to.User, mail, mailInfo)){ return false; } } _logger.Set(LogKind.Normal, null, 8, mailInfo.ToString()); } else { //Toの追加 // if (null == mail.GetHeader("To")) { // mail.AddHeader("To", string.Format("<{0}>", to)); // } //ヘッダを追加してサイズが変わるので、ここで初期化する var mailInfo = new MailInfo(uidStr, mail.Length, host, addr, from, to); if (!_mailQueue.Save(mail, mailInfo)) { _logger.Set(LogKind.Error, null, 9000059, mail.GetLastError()); return false; } _logger.Set(LogKind.Normal, null, 9, mailInfo.ToString()); } return true; }