Class: Receiver
- Includes:
- Config, PDKIM, Version
- Defined in:
- lib/rubymta/receiver.rb
Constant Summary collapse
- PdkimReturnCodes =
PDKIM Verify Codes
["0-Verify not completed", "1-Verify invalid", "2-Verify failed", "3-Verify passed"]
- Patterns =
[ [0, "[ /t]*QUIT[ /t]*", :quit], [1, "[ /t]*AUTH[ /t]*(.+)", :auth_base], [1, "[ /t]*EHLO(.*)", :ehlo_base], [1, "[ /t]*EXPN[ /t]*(.*)", :expn_base], [1, "[ /t]*HELO[ /t]+(.*)", :ehlo_base], [1, "[ /t]*HELP[ /t]*(.*)", :help_base], [1, "[ /t]*NOOP[ /t]*(.*)", :noop_base], [1, "[ /t]*RSET[ /t]*(.*)", :rset_base], [1, "[ /t]*TIMEOUT[ /t]*", :timeout], [1, "[ /t]*VFRY[ /t]*(.*)", :vfry_base], [2, "[ /t]*STARTTLS[ /t]*", :starttls], [2, "[ /t]*MAIL FROM[ /t]*:[ \t]*(.+)", :mail_from_base], [3, "[ /t]*RCPT TO[ /t]*:[ \t]*(.+)", :rcpt_to_base], [4, "[ /t]*DATA[ /t]*", :data_base] ]
- Unexpectedly =
"; probably caused by the client closing the connection unexpectedly"
Constants included from Version
Version::MODIFIED, Version::VERSION
Instance Method Summary collapse
-
#auth_base(value) ⇒ Object
This method MUST be supplemented to use AUTH – if the authentication succeeds, a “235 2.0.0 Authentication succeeded” message should be returned; otherwise a “530 5.7.8 Authentication failed” error should be returned.
-
#connect_base ⇒ Object
——————————————————-# — SMTP COMMAND HANDLING METHODS ———————# ——————————————————-#.
-
#data_base(value) ⇒ Object
——————————————————-# — Data ———————————————-# ——————————————————-#.
- #ehlo_base(value) ⇒ Object
- #expn_base(value) ⇒ Object
- #help_base(value) ⇒ Object
-
#initialize(connection) ⇒ Receiver
constructor
A new instance of Receiver.
-
#mail_from_base(value) ⇒ Object
——————————————————-# — Sender ——————————————–# ——————————————————-#.
- #noop_base(value) ⇒ Object
-
#psych_value(part, value) ⇒ Object
——————————————————-# — Parse the email address and investigate it ——–# ——————————————————-#.
- #quit(value) ⇒ Object
-
#rcpt_to_base(value) ⇒ Object
——————————————————-# — Recipient —————————————–# ——————————————————-#.
-
#receive(local_port, local_hostname, remote_port, remote_hostname, remote_ip) ⇒ Object
——————————————————-# — LOOP TO RECEIVE COMMANDS ————————–# ——————————————————-#.
-
#recv_text(echo = true) ⇒ Object
——————————————————-# — Receive text from the client ———————-# ——————————————————-#.
-
#rset_base(value) ⇒ Object
——————————————————-# — Reset ———————————————# ——————————————————-#.
-
#send_text(text, echo = true) ⇒ Object
——————————————————-# — Send text to the client —————————# ——————————————————-#.
-
#starttls(value) ⇒ Object
These are not overrideable.
- #timeout(value) ⇒ Object
- #vfry_base(value) ⇒ Object
Constructor Details
#initialize(connection) ⇒ Receiver
Returns a new instance of Receiver.
36 37 38 |
# File 'lib/rubymta/receiver.rb', line 36 def initialize(connection) @connection = connection end |
Instance Method Details
#auth_base(value) ⇒ Object
This method MUST be supplemented to use AUTH – if the authentication succeeds, a “235 2.0.0 Authentication succeeded” message should be returned; otherwise a “530 5.7.8 Authentication failed” error should be returned
593 594 595 596 597 598 599 600 |
# File 'lib/rubymta/receiver.rb', line 593 def auth_base(value) if respond_to?(:auth) msg = auth(value) return msg if !msg.nil? end return "504 5.7.4 authentication mechanism not supported; use TLS and PLAIN" end |
#connect_base ⇒ Object
——————————————————-# — SMTP COMMAND HANDLING METHODS ———————# ——————————————————-#
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/rubymta/receiver.rb', line 262 def connect_base if @contact.prohibited? # after the first denied message, we just slam the channel shut: no more nice guy LOG.warn(@mail[:mail_id]) {"Slammed connection shut. No more nice guy with #{@mail[:remote_ip]}"} raise Quit end if @contact.warning? # this is the first denied message @warning_given = true expires_at = @contact.violation.strftime('%Y-%m-%d %H:%M:%S %Z') # to kick it up to prohibited LOG.warn(@mail[:mail_id]) {"Access TEMPORARILY denied to #{@mail[:remote_ip]} (#{@mail[:remote_hostname]}) until #{expires_at}"} return "454 4.7.1 Access TEMPORARILY denied to #{@mail[:remote_ip]}: you may try again after #{expires_at}" end if respond_to?(:connect) msg = connect(value) return msg if !msg.nil? end # 8 bells and all is well @level = 1 return "220 2.0.0 #{@mail[:local_hostname]} ESMTP RubyMTA 0.01 #{Time.new.strftime("%^a, %d %^b %Y %H:%M:%S %z")}" end |
#data_base(value) ⇒ Object
——————————————————-# — Data ———————————————-# ——————————————————-#
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 |
# File 'lib/rubymta/receiver.rb', line 411 def data_base(value) @mail[:data] = body = {} # make sure that there is at least 1 recipient count = 0; @mail[:rcptto].each { |rcpt| count += 1 if rcpt[:accepted] } @mail[:recipients] = count return "500 5.0.0 There must be at least 1 acceptable recipient" if count==0 # receive the body of the mail body[:value] = value # this should be nil -- no argument on the DATA command body[:text] = lines = [] send_text("354 Enter message, ending with \".\" on a line by itself") LOG.info(@mail[:mail_id]) {" -> (data)"} if LogReceiverConversation && !ShowIncomingData while true text = recv_text(ShowIncomingData) if text.nil? # the client closed the channel abruptly @contact.violation break end break if text=="." lines << text end # hold the new headers here (insert them down below) new_headers = [] # check DKIM signatures, if any pdkim = [] ok, signatures = pdkim_verify_an_email(PDKIM_INPUT_NORMAL, lines) signatures.each do |signature| pdkim << (status = PdkimReturnCodes[signature[:verify_status]]) end if !pdkim.empty? body[:pdkim] = pdkim LOG.info(@mail[:mail_id]) {"DKIM signatures (from last to first): #{body[:pdkim].inspect})"} new_headers << "DKIM-Status: #{body[:pdkim].inspect[1..-2]}" # strip off the '[]' end #Return-Path: <[email protected]> new_headers << "Return-Path: <#{@mail[:mailfrom][:url]}>" #Delivered-To: <[email protected]> new_headers << "Delivered-To: <#{@mail[:rcptto][0][:url]}>" @mail[:rcptto][1..-1].each do |rcpt| new_headers << "\t<#{rcpt[:url]}>" end #Received: from cpe-107-185-187-182.socal.res.rr.com ([::ffff:107.185.187.182]) # by mail.tzarmail.com (RubyMTA 0.0.1) with ESMTP # (envelope from <[email protected]>) # id 1dYwrI-0iDWWN-0y; Sat, 22 Jul 2017 16:02:24 +0000 new_headers << "Received: from #{@mail[:remote_hostname]} ([#{@mail[:remote_ip]}])" new_headers << "\tby #{@mail[:local_hostname]} (RubyMTA #{VERSION}) with ESMTP" new_headers << "\t(envelope from <#{@mail[:mailfrom][:url]}>)" new_headers << "\tid #{@mail[:mail_id]}; #{@mail[:time]}" # insert the new headers into the message text new_headers.reverse.each { |hdr| @mail[:data][:text].insert(0,hdr) } # always add a DKIM signature which will include our headers if $app[:dkim] && !@mail[:dkim_added] ok, = pdkim_sign_an_email(PDKIM_INPUT_NORMAL, ServerName, 'key', $app[:dkim], PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE, @mail[:data][:text]) if ok==PDKIM_OK @mail[:data][:text] = @mail[:dkim_added] = true else LOG.info(@mail[:mail_id]) {"Unsuccessful at signing #{mail[:id]} to #{host}"} end end # parse the headers for easier inspection, if any @mail.parse_headers @level = 1 if respond_to?(:data) msg = data(value) return msg if !msg.nil? end #-------------------------------------------------------# #--- EMail queueing here -------------------------------# #-------------------------------------------------------# LOG.info(@mail[:mail_id]) {"#{@mail[:mail_id]} accepted with #{count} recipient#{if count>1 then 's' end}"} # the email appears good, queue it @mail[:accepted] = true case when !@mail.insert_parcels "500 5.0.0 #{ServerName} error: unable to save packet id=#{@mail[:mail_id]}" when !@mail.save_mail_into_queue_folder "500 5.0.0 #{ServerName} error: unable to save queue id=#{@mail[:mail_id]}" else "250 2.0.0 OK id=#{@mail[:mail_id]}" end end |
#ehlo_base(value) ⇒ Object
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/rubymta/receiver.rb', line 287 def ehlo_base(value) @mail[:ehlo] = ehlo = {} ehlo[:value] = value # The email specs call for EHLO or HELO to be followed by a domain, # but this behavior can be turned off, if you want -- also, we look # to see if it's a real domain (well, duh! makes sense to do that) if EhloDomainRequired if value.index(".") ehlo[:domain] = domain = value.split(".").collect{ |item| item.strip }[-2..-1].join(".") ehlo[:ip] = ip = if EhloDomainVerifies then domain.dig_a else nil end else ehlo[:domain] = nil ehlo[:ip] = nil end return "501 5.5.1 Domain required after EHLO/HELO" \ if ehlo.nil? || ehlo[:domain].nil? return "502 5.1.8 EHLO domain #{ehlo[:domain].inspect} was not found in the DNS system (maybe a fake domain?)" \ if EhloDomainVerifies && ehlo[:ip].nil? && @mail[:local_port]==StandardMailPort end if respond_to?(:ehlo) msg = ehlo(value) return msg if !msg.nil? end text = "250-2.0.0 #{ServerName} Hello" text << " #{domain}" if domain text << " at #{ip}" if ip @level = 2 return [text, "250-AUTH PLAIN", "250-STARTTLS", "250 HELP"] end |
#expn_base(value) ⇒ Object
553 554 555 556 557 558 559 560 |
# File 'lib/rubymta/receiver.rb', line 553 def expn_base(value) if respond_to?(:expn) msg = expn(value) return msg if !msg.nil? end return "252 2.5.1 Administrative prohibition" end |
#help_base(value) ⇒ Object
562 563 564 565 566 567 568 569 |
# File 'lib/rubymta/receiver.rb', line 562 def help_base(value) if respond_to?(:help) msg = help(value) return msg if !msg.nil? end return "250 2.0.0 QUIT AUTH, EHLO, EXPN, HELO, HELP, NOOP, RSET, VFRY, STARTTLS, MAIL FROM, RCPT TO, DATA" end |
#mail_from_base(value) ⇒ Object
——————————————————-# — Sender ——————————————–# ——————————————————-#
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/rubymta/receiver.rb', line 324 def mail_from_base(value) @mail[:mailfrom] = from = {} @mail[:rcptto] = [] @mail[:saved] = false # enable saving from[:accepted] = false ok = psych_value(from, value) # these criteria MUST be met for any sender return "550 5.1.7 '#{from[:value]}' No proper sender (<...>) on the MAIL FROM line" if !ok # we check to see if this is a reasonable MAIL FROM address, or garbage return "550-5.1.7 local part #{from[:local_part].inspect} cannot contain", \ "550 5.1.7 beginning or ending '.' or 2 or more '.'s in a row" \ if from[:dot_error] return "550-5.1.7 #{from[:local_part].inspect} can only", \ "550 5.1.7 contain a-z, A_Z, 0-9, and !#\$%&'*+-/?^_`{|}~.=" \ if from[:char_error] LOG.info(@mail[:mail_id]) {"Receiving mail from sender #{from[:url]}"} # Check to see if this sender is one of ours -- how that is done is up to you -- # You must implement 'client_lookup(url)' where url is the full email address -- # Also, members MUST use use authenticated email on the SubmissionPort to # submit mail; non-members MUST use non-authenticated email on the # StandardMailPort to submit mail case @mail[:local_port] when StandardMailPort # '25'--non client must come in here return "556 5.7.27 #{ServerTitle} members must use port #{InternalSubmitPort} or #{SubmissionPort} to send mail" \ if from[:mailbox_id] when InternalSubmitPort # '465'-- client, internal use only, block external use with iptables return "556 5.7.27 Non #{ServerTitle} members must use port #{StandardMailPort} to send mail" \ if !from[:mailbox_id] when SubmissionPort # '587'--client, external or internal req. auth & enc return "556 5.7.27 Non #{ServerTitle} members must use port #{StandardMailPort} to send mail" \ if !from[:mailbox_id] return "556 5.7.27 Traffic on port #{SubmissionPort} must be authenticated (i.e., #{ServerTitle} client)" \ if !@mail[:authenticated] return "556 5.7.27 Traffic on port #{SubmissionPort} must be encrypted" \ if !@mail[:encrypted] end # use the mail_from(value) method in the configuration file to add # more rules for filtering senders; psych_value will determine if # the sender is a member, if you have a 'client_lookup(url)', as mentioned above if respond_to?(:mail_from) msg = mail_from(value) return msg if !msg.nil? end @level = 3 from[:accepted] = true return "250 2.0.0 OK" end |
#noop_base(value) ⇒ Object
571 572 573 574 575 576 577 578 |
# File 'lib/rubymta/receiver.rb', line 571 def noop_base(value) if respond_to?(:noop) msg = noop(value) return msg if !msg.nil? end return "250 2.0.0 OK" end |
#psych_value(part, value) ⇒ Object
——————————————————-# — Parse the email address and investigate it ——–# ——————————————————-#
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/rubymta/receiver.rb', line 107 def psych_value(part, value) # these get set in both MAIL FROM and RCPT TO part[:value] = value part[:accepted] = false # check for the special case of "... <postmaster>" n = value.match(/^(.*)<(.+)>$/) if n && n[2].downcase=="postmaster" m = Array.new m[0] = n[0] m[1] = n[1] m[2] = PostMasterName else # parse out the name (if any) and the address (required) m = value.match(/^(.*)<(.+@.+\..+)>$/) # there MUST be a sender/recipient address return false if m.nil? end # break up the address part[:name] = m[1].strip part[:url] = url = m[2].strip # parse out the local-part and domain local_part, domain = url.split("@") part[:local_part] = local_part part[:domain] = domain # check the local part for validity? # Uppercase and lowercase English letters (a-z, A-Z) # Digits 0 to 9 # Characters ! # $ % & ' * + - / = ? ^ _ ` { | } ~ # Character . provided that it is not the first or last character, # and provided also that it does not appear two or more times consecutively. part[:dot_error] = true if (local_part[0]=='.' || local_part[-1]=='.' || local_part.index('..')) m = local_part.match(/^[a-zA-Z0-9\!\#\$%&'*+-\/?^_`{|}~=]+$/) part[:char_error] = m.nil? # lookup the email to see if it's one of ours if respond_to?(:client_lookup) part[:mailbox_id], part[:owner_id], part[:delivery] = client_lookup(part[:url]) else part[:mailbox_id], part[:owner_id], part[:delivery] = [nil, nil, :remote] end # get the MXs, if needed and if any -- # if we deliver to a mailbox which has an owner_id, # delivery will be made with LMTP and no MXs will be needed part[:mxs] = mxs = if part[:owner_id].nil? then domain.dig_mxs else nil end return true #---------------------------------------------------------------------------------------# #--- WHAT WE KNOW AFTER PSYCH #--- 1. if the return value is true, the value has the correct form #--- 2. the url is in part[:url] #--- 3. the part[:local_part] and part[:domain] are have values #--- 4. the MXs, if any, are in part[:mxs] => { preference => [ [mx,ip], ... ], ... } #--- 5. if it's our member, part[:mailbox_id], part[:owner_id] have values #---------------------------------------------------------------------------------------# end |
#quit(value) ⇒ Object
580 581 582 583 584 585 586 587 |
# File 'lib/rubymta/receiver.rb', line 580 def quit(value) @done = true if (@mail[:saved].nil?) && (@contact.violations? == 0) LOG.warn(@mail[:mail_id]) {"Quitting before a message is finished is considered a violation"} @contact.violation end return "221 2.0.0 OK #{ServerName} closing connection" end |
#rcpt_to_base(value) ⇒ Object
——————————————————-# — Recipient —————————————–# ——————————————————-#
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/rubymta/receiver.rb', line 381 def rcpt_to_base(value) @mail[:rcptto] ||= [] @mail[:rcptto] << rcpt = {} rcpt[:accepted] = false ok = psych_value(rcpt, value) # these criteria MUST be met for any recipient if !ok rcpt[:message] = "'#{value}' No proper recipient (<...>) on the RCPT TO line" LOG.info(@mail[:mail_id]) {rcpt[:message]} return "550 5.1.7 #{rcpt[:message]}" end # use the rcpt_to(value) method in the configuration file to add # more rules for filtering recipients; psych_value will determine if # the recipient is a member, if you have a 'client_lookup(url)', as mentioned above if respond_to?(:rcpt_to) msg = rcpt_to(value) return msg if !msg.nil? end @contact.allow @level = 4 rcpt[:accepted] = true return "250 2.0.0 ACCEPTED" end |
#receive(local_port, local_hostname, remote_port, remote_hostname, remote_ip) ⇒ Object
——————————————————-# — LOOP TO RECEIVE COMMANDS ————————–# ——————————————————-#
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/rubymta/receiver.rb', line 172 def receive(local_port, local_hostname, remote_port, remote_hostname, remote_ip) # Start a hash to collect the information gathered from the receive process @contact = Contact::new(remote_ip) @mail = ItemOfMail::new @mail[:contact_id] = @contact[:id] @mail[:local_port] = local_port @mail[:local_hostname] = local_hostname @mail[:remote_port] = remote_port @mail[:remote_hostname] = remote_hostname @mail[:remote_ip] = remote_ip @mail[:saved] = true # prevent saving until we have at least a MAIL FROM LOG.info(@mail[:mail_id]) {"New item of mail opened with id '#{@mail[:mail_id]}'"} # start the main receiving process here @done = false @encrypted = false @authenticated = false @warning_given = false @mail[:encrypted] = false @mail[:authenticated] = nil send_text(connect_base) @level = 1 response = "252 2.5.1 Administrative prohibition" begin begin break if @done text = recv_text # the client closed the channel abruptly or we're forcing QUIT if (text.nil?) || @warning_given text = "QUIT" @contact.violation end # this handles an attempt to connect with HTTP if text.start_with?("GET") LOG.error(@mail[:mail_id]) {"An attempt was made to connect with a web browser"} @mail[:saved] = true # prevent saving raise Quit end # main command detect loop unrecognized = true Patterns.each do |pattern| break if pattern[0]>@level m = text.match(/^#{pattern[1].upcase}$/i) if m case when pattern[2]==:quit send_text(quit(m[1])) when pattern[0]>@level send_text("500 5.5.1 Command out of sequence") else response = send(pattern[2], m[1]) @contact.violation if send_text(response)=='5' end unrecognized = false break end end if unrecognized response = "500 5.5.1 Unrecognized command #{text.inspect}, incorrectly formatted command, or command out of sequence" @contact.violation send_text(response) end end until @done rescue => e LOG.fatal(@mail[:mail_id]) {e.inspect} exit(1) end # print the intermediate structure into the log (for debugging) (LOG.info(@mail[:mail_id]) { "Received Mail:\n#{@mail.pretty_inspect}" }) if DumpMailIntoLog ensure # run the mail queue queue runner now, if it's not running already ok = nil File.open(LockFilePath,"w") do |f| ok = f.flock( File::LOCK_NB | File::LOCK_EX ) f.flock(File::LOCK_UN) if ok end if ok pid = Process::spawn("#{$app[:path]}/run_queue.rb") Process::detach(pid) LOG.info(@mail[:mail_id]) {"Spawned run_queue.rb, pwd=#{`pwd`.chomp}, path=>#{$app[:path]}, pid=>#{pid.inspect}"} end end |
#recv_text(echo = true) ⇒ Object
——————————————————-# — Receive text from the client ———————-# ——————————————————-#
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/rubymta/receiver.rb', line 74 def recv_text(echo=true) begin Timeout.timeout(ReceiverTimeout) do begin temp = @connection.gets case when temp.nil? LOG.warn(@mail[:mail_id]) {"The client abruptly closed the connection"} text = nil else text = temp.chomp.utf8 end rescue Errno::ECONNRESET => e LOG.warn(@mail[:mail_id]) {"The client slammed the connection shut"} text = nil end LOG.info(@mail[:mail_id]) {" -> #{if text.nil? then "<eod>" else text end}"} \ if echo && LogReceiverConversation puts " -> #{text.inspect}" if DisplayReceiverDialog return text end rescue Errno::EIO => e LOG.error(@mail[:mail_id]) {"#{e}#{Unexpectedly}"} raise Quit rescue Timeout::Error => e LOG.info(@mail[:mail_id]) {" -> <eod>"} if LogReceiverConversation return nil end end |
#rset_base(value) ⇒ Object
——————————————————-# — Reset ———————————————# ——————————————————-#
511 512 513 514 515 516 517 518 519 520 521 522 |
# File 'lib/rubymta/receiver.rb', line 511 def rset_base(value) if respond_to?(:rset) msg = rset(value) return msg if !msg.nil? end @level = 2 if @level>2 @mail.delete(:mailfrom) @mail.delete(:rcptto) @mail.delete(:data) return "250 2.0.0 Reset OK" end |
#send_text(text, echo = true) ⇒ Object
——————————————————-# — Send text to the client —————————# ——————————————————-#
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/rubymta/receiver.rb', line 45 def send_text(text,echo=true) puts "<- #{text.inspect}" if DisplayReceiverDialog begin case when text.nil? # do nothing when text.class==Array text.each do |line| @connection.write(line+CRLF) LOG.info(@mail[:mail_id]) {"<- #{line}"} if echo && LogReceiverConversation end return text.last[0] else @connection.write(text+CRLF) LOG.info(@mail[:mail_id]) {"<- #{text}"} if echo && LogReceiverConversation return text[0] end rescue Errno::EPIPE => e LOG.error(@mail[:mail_id]) {"#{e}#{Unexpectedly}"} raise Quit rescue Errno::EIO => e LOG.error(@mail[:mail_id]) {"#{e}#{Unexpectedly}"} raise Quit end end |
#starttls(value) ⇒ Object
These are not overrideable
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 |
# File 'lib/rubymta/receiver.rb', line 604 def starttls(value) send_text("220 2.0.0 TLS go ahead") LOG.info(@mail[:mail_id]) {"<-> (handshake)"} if LogReceiverConversation conn = @connection.deepclone # save the unencrypted connection in case of error begin @connection.accept @mail[:encrypted] = @encrypted = true rescue OpenSSL::SSL::SSLError => e # STARTTLS failed: restore the unencrypted connection LOG.error(@mail[:mail_id]) {"Error during STARTTLS: #{e}"} @connection = conn # restore original @mail[:encrypted] = @encrypted = false return "500 5.0.0 STARTTLS failed: #{e}" end return nil end |
#timeout(value) ⇒ Object
621 622 623 624 |
# File 'lib/rubymta/receiver.rb', line 621 def timeout(value) @done = true return ("500 5.7.1 #{"<mail id>"} closing connection due to inactivity--%s was NOT saved") end |
#vfry_base(value) ⇒ Object
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 |
# File 'lib/rubymta/receiver.rb', line 524 def vfry_base(value) # SMTP includes commands called "VRFY" and "EXPN" which do exactly what verification services offer. # While those two functions are technically different, they both reveal to a third party whether email # addresses exist in the server's userbase. Nearly every Postmaster (mail server administrator) on the # Internet has turned off VRFY and EXPN due to abuse by spammers trying to harvest addresses, as well # as a general security and privacy measure required by most network's operational policies. In fact, # since about 1999 or before, all mail servers are installed with those off by default. That should # give a clear indication to email verifiers about the opinion of Postmasters of the service they # intend to offer. Doing verification against systems that have disabled those functions, whether # successful or not, constitutes an attempted breach of the receiver's security policies and may be # considered a hostile act by site administrators. Sending high volumes of verification probes without # an attempt to actually send an email will often trigger filters or firewalls, thus invalidating the # data and impairing future verification accuracy. # -- http://www.spamhaus.org/news/article/722/on-the-dubious-merits-of-email-verification-services # # What this means for us is: if a spammer sends spam and we try to validate the sender's email # address, or bounce the message, and it's a SPAMHAUS or other blacklist company's trap address, # *WE* will be blacklisted. So we don't use VFRY or EXPN, and don't use a EHLO, MAIL FROM, RCPT TO, # QUIT sequence either. The takeaway here: thanks to spammers and Spamhaus, one can't verify a # sender's or recipient's address safely. if respond_to?(:vfry) msg = vfry(value) return msg if !msg.nil? end return "252 2.5.1 Administrative prohibition" end |