Class: MidiSmtpServer::Smtpd
- Inherits:
-
Object
- Object
- MidiSmtpServer::Smtpd
- Defined in:
- lib/midi-smtp-server.rb
Overview
class for SmtpServer
Instance Attribute Summary collapse
-
#auth_mode ⇒ Object
readonly
Authentication mode.
-
#crlf_mode ⇒ Object
readonly
CRLF handling based on conformity to RFC(2)822.
-
#do_dns_reverse_lookup ⇒ Object
readonly
Flag if should do reverse DNS lookups on incoming connections.
-
#encrypt_mode ⇒ Object
readonly
Encryption mode.
-
#internationalization_extensions ⇒ Object
readonly
handle SMTP 8BITMIME and SMTPUTF8 extension.
-
#io_buffer_chunk_size ⇒ Object
readonly
Bytes to read non-blocking from socket into buffer, as a FixNum.
-
#io_buffer_max_size ⇒ Object
readonly
Maximum bytes to read as buffer before expecting completed incoming data line, as a FixNum.
-
#io_cmd_timeout ⇒ Object
readonly
Maximum time in seconds to wait for a complete incoming data line, as a FixNum.
-
#io_waitreadable_sleep ⇒ Object
readonly
Time in seconds to sleep on IO::WaitReadable exception.
-
#logger ⇒ Object
readonly
logging object, may be overridden by special loggers like YELL or others.
-
#max_connections ⇒ Object
readonly
Maximum number of allowed connections, this does limit the TCP connections, as a FixNum.
-
#max_processings ⇒ Object
readonly
Maximum number of simultaneous processed connections, this does not limit the TCP connections itself, as a FixNum.
-
#pipelining_extension ⇒ Object
readonly
handle SMTP PIPELINING extension.
-
#proxy_extension ⇒ Object
readonly
handle PROXY connections.
Instance Method Summary collapse
-
#addresses ⇒ Object
Array of ip_address:port which get bound and build up from given hosts and ports.
-
#authenticated?(ctx) ⇒ Boolean
check the status of authentication for a given context.
-
#connections ⇒ Object
Return the current number of connected clients.
-
#connections? ⇒ Boolean
Return if has active connected clients.
-
#encrypted?(ctx) ⇒ Boolean
check the status of encryption for a given context.
-
#hosts ⇒ Object
Array of hosts / ip_addresses on which to bind, set as string separated by commata like ‘name.domain.com, 127.0.0.1, ::1’.
-
#initialize(ports: DEFAULT_SMTPD_PORT, hosts: DEFAULT_SMTPD_HOST, pre_fork: DEFAULT_SMTPD_PRE_FORK, max_processings: DEFAULT_SMTPD_MAX_PROCESSINGS, max_connections: nil, crlf_mode: nil, do_dns_reverse_lookup: nil, io_waitreadable_sleep: nil, io_cmd_timeout: nil, io_buffer_chunk_size: nil, io_buffer_max_size: nil, pipelining_extension: nil, internationalization_extensions: nil, proxy_extension: nil, auth_mode: nil, tls_mode: nil, tls_cert_path: nil, tls_key_path: nil, tls_ciphers: nil, tls_methods: nil, tls_cert_cn: nil, tls_cert_san: nil, logger: nil, logger_severity: nil) ⇒ Smtpd
constructor
Initialize SMTP Server class.
-
#join(sleep_seconds_before_join: 1) ⇒ Object
Join with the server thread(s) before joining the server threads, check and wait optionally a few seconds to let the service(s) come up.
-
#master? ⇒ Boolean
Return if this is the master process.
-
#on_auth_event(ctx, authorization_id, authentication_id, authentication) ⇒ Object
check the authentication on AUTH if any value returned, that will be used for ongoing processing otherwise the original value will be used for authorization_id.
- #on_connect_event(ctx) ⇒ Object
-
#on_disconnect_event(ctx) ⇒ Object
event before DISCONNECT.
-
#on_helo_event(ctx, helo_data) ⇒ Object
event on HELO/EHLO you may change the ctx[:helo_response] in here so that this will be used as greeting string the value is not allowed to return CR nor LF chars and will be stripped.
-
#on_logging_event(_ctx, severity, msg, err: nil) ⇒ Object
event on LOGGING the exposed logger property is from class MidiSmtpServer::ForwardingLogger and pushes any logging message to this on_logging_event.
-
#on_mail_from_event(ctx, mail_from_data) ⇒ Object
get address send in MAIL FROM if any value returned, that will be used for ongoing processing otherwise the original value will be used.
-
#on_message_data_event(ctx) ⇒ Object
get each message after DATA <message>.
-
#on_message_data_headers_event(ctx) ⇒ Object
event when headers are received while receiving message DATA.
-
#on_message_data_receiving_event(ctx) ⇒ Object
event while receiving message DATA.
-
#on_message_data_start_event(ctx) ⇒ Object
event when beginning with message DATA.
-
#on_process_line_unknown_event(_ctx, _line) ⇒ Object
event when process_line identifies an unknown command line allows to abort sessions for a series of unknown activities to prevent denial of service attacks etc.
-
#on_proxy_event(ctx, proxy_data) ⇒ Object
event on PROXY you may raise an exception if you want to block some addresses you also may change or add any value of the hash: source_ip, source_host, source_port, dest_ip, dest_host, dest_port a returned value hash is set as ctx[:proxy].
-
#on_rcpt_to_event(ctx, rcpt_to_data) ⇒ Object
get each address send in RCPT TO if any value returned, that will be used for ongoing processing otherwise the original value will be used.
-
#ports ⇒ Object
Array of ports on which to bind, set as string separated by commata like ‘2525, 3535’ or ‘2525:3535, 2525’.
-
#pre_fork? ⇒ Boolean
Return if in pre-fork mode.
-
#processings ⇒ Object
Return the current number of processed clients.
-
#processings? ⇒ Boolean
Return if has active processed clients.
-
#shutdown ⇒ Object
Schedule a shutdown for the server.
-
#shutdown? ⇒ Boolean
test for shutdown state.
-
#ssl_context ⇒ Object
Current TLS OpenSSL::SSL::SSLContext when initialized by :TLS_OPTIONAL, :TLS_REQUIRED.
-
#start ⇒ Object
Create the server.
-
#stop(wait_seconds_before_close: 2, gracefully: true) ⇒ Object
Stop the server.
-
#stopped? ⇒ Boolean
Returns true if the server has stopped.
-
#worker? ⇒ Boolean
Return if this is a forked worker process.
-
#workers ⇒ Object
Return number of forked worker processes.
-
#workers? ⇒ Boolean
Return if has active forked worker processes.
Constructor Details
#initialize(ports: DEFAULT_SMTPD_PORT, hosts: DEFAULT_SMTPD_HOST, pre_fork: DEFAULT_SMTPD_PRE_FORK, max_processings: DEFAULT_SMTPD_MAX_PROCESSINGS, max_connections: nil, crlf_mode: nil, do_dns_reverse_lookup: nil, io_waitreadable_sleep: nil, io_cmd_timeout: nil, io_buffer_chunk_size: nil, io_buffer_max_size: nil, pipelining_extension: nil, internationalization_extensions: nil, proxy_extension: nil, auth_mode: nil, tls_mode: nil, tls_cert_path: nil, tls_key_path: nil, tls_ciphers: nil, tls_methods: nil, tls_cert_cn: nil, tls_cert_san: nil, logger: nil, logger_severity: nil) ⇒ Smtpd
Initialize SMTP Server class
ports
-
ports to listen on. Allows multiple ports like “2525, 3535” or “2525:3535, 2525”. Default value = DEFAULT_SMTPD_PORT
hosts
-
interface ip or hostname to listen on or “*” to listen on all interfaces, allows multiple hostnames and ip_addresses like “name.domain.com, 127.0.0.1, ::1”. Default value = DEFAULT_SMTPD_HOST
pre_fork
-
number of processes to pre-fork to enable load balancing over multiple cores. Default value = DEFAULT_SMTPD_PRE_FORK
max_processings
-
maximum number of simultaneous processed connections, this does not limit the number of concurrent TCP connections. Default value = DEFAULT_SMTPD_MAX_PROCESSINGS
max_connections
-
maximum number of connections, this does limit the number of concurrent TCP connections (not set or nil => unlimited)
crlf_mode
-
CRLF handling support (:CRLF_ENSURE [default], :CRLF_LEAVE, :CRLF_STRICT)
do_dns_reverse_lookup
-
flag if this smtp server should do reverse DNS lookups on incoming connections
io_waitreadable_sleep
-
seconds to sleep in loop when no input data is available (DEFAULT_IO_WAITREADABLE_SLEEP)
io_cmd_timeout
-
time in seconds to wait until complete line of data is expected (DEFAULT_IO_CMD_TIMEOUT, nil => disabled test)
io_buffer_chunk_size
-
size of chunks (bytes) to read non-blocking from socket (DEFAULT_IO_BUFFER_CHUNK_SIZE)
io_buffer_max_size
-
max size of buffer (max line length) until lf ist expected (DEFAULT_IO_BUFFER_MAX_SIZE, nil => disabled test)
pipelining_extension
-
set to true for support of SMTP PIPELINING extension (DEFAULT_PIPELINING_EXTENSION_ENABLED)
internationalization_extensions
-
set to true for support of SMTP 8BITMIME and SMTPUTF8 extensions (DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED)
proxy_extension
-
set to true for supporting PROXY connections (DEFAULT_PROXY_EXTENSION_ENABLED)
auth_mode
-
enable builtin authentication support (:AUTH_FORBIDDEN [default], :AUTH_OPTIONAL, :AUTH_REQUIRED)
tls_mode
-
enable builtin TLS support (:TLS_FORBIDDEN [default], :TLS_OPTIONAL, :TLS_REQUIRED)
tls_cert_path
-
path to tls certificate chain file
tls_key_path
-
path to tls key file
tls_ciphers
-
allowed ciphers for connection
tls_methods
-
allowed methods for protocol
tls_cert_cn
-
set subject (CN) for self signed certificate “cn.domain.com”
tls_cert_san
-
set subject alternative (SAN) for self signed certificate, allows multiple names like “alt1.domain.com, alt2.domain.com”
logger
-
own logger class, otherwise default logger is created (DEPRECATED: use on_logging_event for special needs on logging)
logger_severity
-
set or override the logger level if given
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 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 320 321 322 323 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 377 378 379 380 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 407 408 409 410 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 |
# File 'lib/midi-smtp-server.rb', line 250 def initialize( ports: DEFAULT_SMTPD_PORT, hosts: DEFAULT_SMTPD_HOST, pre_fork: DEFAULT_SMTPD_PRE_FORK, max_processings: DEFAULT_SMTPD_MAX_PROCESSINGS, max_connections: nil, crlf_mode: nil, do_dns_reverse_lookup: nil, io_waitreadable_sleep: nil, io_cmd_timeout: nil, io_buffer_chunk_size: nil, io_buffer_max_size: nil, pipelining_extension: nil, internationalization_extensions: nil, proxy_extension: nil, auth_mode: nil, tls_mode: nil, tls_cert_path: nil, tls_key_path: nil, tls_ciphers: nil, tls_methods: nil, tls_cert_cn: nil, tls_cert_san: nil, logger: nil, logger_severity: nil ) # create an exposed logger to forward logging to the on_logging_event @logger = MidiSmtpServer::ForwardingLogger.new(method(:on_logging_event)) # external logging if logger.nil? @logger_protected = Logger.new($stdout) @logger_protected.datetime_format = '%Y-%m-%d %H:%M:%S' @logger_protected.formatter = proc { |severity, datetime, _progname, msg| "#{datetime}: [#{severity}] #{msg.chomp}\n" } @logger_protected.level = logger_severity.nil? ? Logger::DEBUG : logger_severity else @logger_protected = logger @logger_protected.level = logger_severity unless logger_severity.nil? logger.warn('Deprecated: "logger" was set on new! Please use "on_logging_event" instead.') end # initialize as master process @is_forked = false # no forked worker processes @workers = [] # list of TCPServers @tcp_servers = [] # list of running threads @tcp_server_threads = [] # lists for connections and thread management @connections = [] @processings = [] @connections_mutex = Mutex.new @connections_cv = ConditionVariable.new # settings # build array of ports # split string into array to instantiate multiple servers @ports = ports.to_s.delete(' ').split(',') # check for at least one port specification raise 'Missing port(s) to bind service(s) to!' if @ports.empty? # check that not also a '' empty item for port is added to the list raise 'Do not use empty value "" for port(s). Please use specific port(s)!' if @ports.include?('') # build array of hosts # split string into array to instantiate multiple servers @hosts = hosts.to_s.delete(' ').split(',') # # Check source of TCPServer.c at https://github.com/ruby/ruby/blob/trunk/ext/socket/tcpserver.c#L25-L31 # * Internally, TCPServer.new calls getaddrinfo() function to obtain ip_addresses. # * If getaddrinfo() returns multiple ip_addresses, # * TCPServer.new TRIES to create a server socket for EACH address and RETURNS FIRST one that is SUCCESSFUL. # # So for that it was a small portion of luck which address had been used then. # We won't support that magic anymore. If wish to bind on all local ip_addresses # and interfaces, use new "*" wildcard, otherwise specify ip_addresses and / or hostnames # # raise exception when found empty or inner empty hosts specification like "" or "a.b.c.d,,e.f.g.h", guess miss-coding raise 'No hosts defined! Please use specific hostnames and / or ip_addresses or "*" for wildcard!' if @hosts.empty? raise 'Detected an empty identifier in given hosts! Please use specific hostnames and / or ip_addresses or "*" for wildcard!' if @hosts.include?('') # build array of addresses for ip_addresses and ports to use @addresses = [] @hosts.each_with_index do |host, index| # resolv ip_addresses for host if not wildcard / all hosts # if host is "*" wildcard (all) interfaces are used # otherwise it will be bind to the found host ip_addresses if host == '*' ip_addresses_for_host = [] Socket.ip_address_list.each do |a| # test for all local valid ipv4 and ipv6 ip_addresses # check question on stackoverflow for details # https://stackoverflow.com/questions/59770803/identify-all-relevant-ip-addresses-from-ruby-socket-ip-address-list ip_addresses_for_host << a.ip_address if \ (a.ipv4? && (a.ipv4_loopback? || a.ipv4_private? || !(a.ipv4_loopback? || a.ipv4_private? || a.ipv4_multicast?) ) ) || (a.ipv6? && (a.ipv6_loopback? || a.ipv6_unique_local? || !(a.ipv6_loopback? || a.ipv6_unique_local? || a.ipv6_linklocal? || a.ipv6_multicast? || a.ipv6_sitelocal? || a.ipv6_mc_global? || a.ipv6_mc_linklocal? || a.ipv6_mc_nodelocal? || a.ipv6_mc_orglocal? || a.ipv6_mc_sitelocal? || a.ipv6_v4compat? || a.ipv6_v4mapped? || a.ipv6_unspecified?) ) ) end else ip_addresses_for_host = Resolv.new.getaddresses(host).uniq end # get ports for that host entry # if ports at index are not specified, use last item # of ports array. if multiple ports specified by # item like 2525:3535:4545, then all ports will be instantiated ports_for_host = (index < @ports.length ? @ports[index] : @ports.last).to_s.split(':') # append combination of ip_address and ports to the list of serving addresses ip_addresses_for_host.each do |ip_address| ports_for_host.each do |port| @addresses << "#{ip_address}:#{port}" end end end # read pre_fork @pre_fork = pre_fork raise 'Number of processes to pre-fork (pre_fork) must be zero or a positive integer greater than 1!' unless @pre_fork.is_a?(Integer) && (@pre_fork.zero? || pre_fork?) # read max_processings @max_processings = max_processings raise 'Number of simultaneous processings (max_processings) must be a positive integer!' unless @max_processings.is_a?(Integer) && @max_processings.positive? # check max_connections @max_connections = max_connections raise 'Number of concurrent connections (max_connections) must be nil or a positive integer!' unless @max_connections.nil? || (@max_connections.is_a?(Integer) && @max_connections.positive?) raise 'Number of concurrent connections (max_connections) is lower than number of simultaneous processings (max_processings)!' if !@max_connections.nil? && @max_connections < @max_processings # check for crlf mode @crlf_mode = crlf_mode.nil? ? DEFAULT_CRLF_MODE : crlf_mode raise "Unknown CRLF mode #{@crlf_mode} was given!" unless CRLF_MODES.include?(@crlf_mode) # always prevent auto resolving hostnames to prevent a delay on socket connect BasicSocket.do_not_reverse_lookup = true # do reverse lookups manually if enabled by io.addr and io.peeraddr @do_dns_reverse_lookup = do_dns_reverse_lookup.nil? ? true : do_dns_reverse_lookup # io and buffer settings @io_waitreadable_sleep = io_waitreadable_sleep.nil? ? DEFAULT_IO_WAITREADABLE_SLEEP : io_waitreadable_sleep @io_cmd_timeout = io_cmd_timeout.nil? ? DEFAULT_IO_CMD_TIMEOUT : io_cmd_timeout @io_buffer_chunk_size = io_buffer_chunk_size.nil? ? DEFAULT_IO_BUFFER_CHUNK_SIZE : io_buffer_chunk_size @io_buffer_max_size = io_buffer_max_size.nil? ? DEFAULT_IO_BUFFER_MAX_SIZE : io_buffer_max_size # smtp extensions @pipelining_extension = pipelining_extension.nil? ? DEFAULT_PIPELINING_EXTENSION_ENABLED : pipelining_extension @internationalization_extensions = internationalization_extensions.nil? ? DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED : internationalization_extensions @proxy_extension = proxy_extension.nil? ? DEFAULT_PROXY_EXTENSION_ENABLED : proxy_extension require 'ipaddr' if @proxy_extension # check for authentication @auth_mode = auth_mode.nil? ? DEFAULT_AUTH_MODE : auth_mode raise "Unknown authentication mode #{@auth_mode} was given!" unless AUTH_MODES.include?(@auth_mode) # check for encryption @encrypt_mode = tls_mode.nil? ? DEFAULT_ENCRYPT_MODE : tls_mode raise "Unknown encryption mode #{@encrypt_mode} was given!" unless ENCRYPT_MODES.include?(@encrypt_mode) # SSL transport layer for STARTTLS if @encrypt_mode == :TLS_FORBIDDEN @tls = nil else # try to load openssl gem now begin require 'openssl' rescue LoadError end # check for openssl gem when enabling TLS / SSL raise 'The openssl library gem is not installed!' unless defined?(OpenSSL) # check for given CN and SAN if tls_cert_cn.nil? # build generic set of "valid" self signed certificate CN and SAN # using all given hosts and detected ip_addresses but not "*" wildcard tls_cert_san = ([] + @hosts + @addresses.map { |address| address.rpartition(':').first }).uniq tls_cert_san.delete('*') # build generic CN based on first SAN if tls_cert_san.first.match?(/^(127\.0?0?0\.0?0?0\.0?0?1|::1|localhost)$/i) # used generic localhost.local tls_cert_cn = 'localhost.local' else # use first element from detected hosts and ip_addresses # drop that element from SAN tls_cert_cn = tls_cert_san.first tls_cert_san.slice!(0) end else tls_cert_cn = tls_cert_cn.to_s.strip tls_cert_san = tls_cert_san.to_s.delete(' ').split(',') end # create ssl transport service @tls = TlsTransport.new(tls_cert_path, tls_key_path, tls_ciphers, tls_methods, tls_cert_cn, tls_cert_san, @logger) end end |
Instance Attribute Details
#auth_mode ⇒ Object (readonly)
Authentication mode
211 212 213 |
# File 'lib/midi-smtp-server.rb', line 211 def auth_mode @auth_mode end |
#crlf_mode ⇒ Object (readonly)
CRLF handling based on conformity to RFC(2)822
199 200 201 |
# File 'lib/midi-smtp-server.rb', line 199 def crlf_mode @crlf_mode end |
#do_dns_reverse_lookup ⇒ Object (readonly)
Flag if should do reverse DNS lookups on incoming connections
209 210 211 |
# File 'lib/midi-smtp-server.rb', line 209 def do_dns_reverse_lookup @do_dns_reverse_lookup end |
#encrypt_mode ⇒ Object (readonly)
Encryption mode
213 214 215 |
# File 'lib/midi-smtp-server.rb', line 213 def encrypt_mode @encrypt_mode end |
#internationalization_extensions ⇒ Object (readonly)
handle SMTP 8BITMIME and SMTPUTF8 extension
217 218 219 |
# File 'lib/midi-smtp-server.rb', line 217 def internationalization_extensions @internationalization_extensions end |
#io_buffer_chunk_size ⇒ Object (readonly)
Bytes to read non-blocking from socket into buffer, as a FixNum
205 206 207 |
# File 'lib/midi-smtp-server.rb', line 205 def io_buffer_chunk_size @io_buffer_chunk_size end |
#io_buffer_max_size ⇒ Object (readonly)
Maximum bytes to read as buffer before expecting completed incoming data line, as a FixNum
207 208 209 |
# File 'lib/midi-smtp-server.rb', line 207 def io_buffer_max_size @io_buffer_max_size end |
#io_cmd_timeout ⇒ Object (readonly)
Maximum time in seconds to wait for a complete incoming data line, as a FixNum
203 204 205 |
# File 'lib/midi-smtp-server.rb', line 203 def io_cmd_timeout @io_cmd_timeout end |
#io_waitreadable_sleep ⇒ Object (readonly)
Time in seconds to sleep on IO::WaitReadable exception
201 202 203 |
# File 'lib/midi-smtp-server.rb', line 201 def io_waitreadable_sleep @io_waitreadable_sleep end |
#logger ⇒ Object (readonly)
logging object, may be overridden by special loggers like YELL or others
222 223 224 |
# File 'lib/midi-smtp-server.rb', line 222 def logger @logger end |
#max_connections ⇒ Object (readonly)
Maximum number of allowed connections, this does limit the TCP connections, as a FixNum
197 198 199 |
# File 'lib/midi-smtp-server.rb', line 197 def max_connections @max_connections end |
#max_processings ⇒ Object (readonly)
Maximum number of simultaneous processed connections, this does not limit the TCP connections itself, as a FixNum
195 196 197 |
# File 'lib/midi-smtp-server.rb', line 195 def max_processings @max_processings end |
#pipelining_extension ⇒ Object (readonly)
handle SMTP PIPELINING extension
215 216 217 |
# File 'lib/midi-smtp-server.rb', line 215 def pipelining_extension @pipelining_extension end |
#proxy_extension ⇒ Object (readonly)
handle PROXY connections
219 220 221 |
# File 'lib/midi-smtp-server.rb', line 219 def proxy_extension @proxy_extension end |
Instance Method Details
#addresses ⇒ Object
Array of ip_address:port which get bound and build up from given hosts and ports
184 185 186 187 |
# File 'lib/midi-smtp-server.rb', line 184 def addresses # prevent original array from being changed @addresses.dup end |
#authenticated?(ctx) ⇒ Boolean
check the status of authentication for a given context
511 512 513 |
# File 'lib/midi-smtp-server.rb', line 511 def authenticated?(ctx) ctx[:server][:authenticated] && !ctx[:server][:authenticated].to_s.empty? end |
#connections ⇒ Object
Return the current number of connected clients
98 99 100 |
# File 'lib/midi-smtp-server.rb', line 98 def connections @connections.size end |
#connections? ⇒ Boolean
Return if has active connected clients
103 104 105 |
# File 'lib/midi-smtp-server.rb', line 103 def connections? @connections.any? end |
#encrypted?(ctx) ⇒ Boolean
check the status of encryption for a given context
516 517 518 |
# File 'lib/midi-smtp-server.rb', line 516 def encrypted?(ctx) ctx[:server][:encrypted] && !ctx[:server][:encrypted].to_s.empty? end |
#hosts ⇒ Object
Array of hosts / ip_addresses on which to bind, set as string separated by commata like ‘name.domain.com, 127.0.0.1, ::1’
178 179 180 181 |
# File 'lib/midi-smtp-server.rb', line 178 def hosts # prevent original array from being changed @hosts.dup end |
#join(sleep_seconds_before_join: 1) ⇒ Object
Join with the server thread(s) before joining the server threads, check and wait optionally a few seconds to let the service(s) come up
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/midi-smtp-server.rb', line 145 def join(sleep_seconds_before_join: 1) # check already existing TCPServers return if @tcp_servers.empty? # check number of processes to pre-fork if pre_fork? # create a number of pre-fork processes and attach and join threads within workers @pre_fork.times do # append worker pid to list of workers @workers << fork do # set state for a forked process @is_forked = true # just attach and join the threads to forked worker process attach_threads join_threads(sleep_seconds_before_join: sleep_seconds_before_join) end end # Blocking wait until each worker process has been finished @workers.each { |pid| Process.waitpid(pid) } else # just join the threads to running single master process (default) join_threads(sleep_seconds_before_join: sleep_seconds_before_join) end end |
#master? ⇒ Boolean
Return if this is the master process
123 124 125 |
# File 'lib/midi-smtp-server.rb', line 123 def master? !@is_forked end |
#on_auth_event(ctx, authorization_id, authentication_id, authentication) ⇒ Object
check the authentication on AUTH if any value returned, that will be used for ongoing processing otherwise the original value will be used for authorization_id
502 503 504 505 506 507 508 |
# File 'lib/midi-smtp-server.rb', line 502 def on_auth_event(ctx, , authentication_id, authentication) # if authentication is used, override this event # and implement your own user management. # otherwise all authentications are blocked per default on_logging_event(ctx, Logger::DEBUG, "Deny access from #{ctx[:server][:remote_ip]}:#{ctx[:server][:remote_port]} for #{authentication_id}" + ( == '' ? '' : "/#{}") + " with #{authentication}") raise Smtpd535Exception end |
#on_connect_event(ctx) ⇒ Object
477 478 479 |
# File 'lib/midi-smtp-server.rb', line 477 def on_connect_event(ctx) on_logging_event(ctx, Logger::DEBUG, "Client connect from #{ctx[:server][:remote_ip]}:#{ctx[:server][:remote_port]} to #{ctx[:server][:local_ip]}:#{ctx[:server][:local_port]}") end |
#on_disconnect_event(ctx) ⇒ Object
event before DISCONNECT
482 483 484 |
# File 'lib/midi-smtp-server.rb', line 482 def on_disconnect_event(ctx) on_logging_event(ctx, Logger::DEBUG, "Client disconnect from #{ctx[:server][:remote_ip]}:#{ctx[:server][:remote_port]} on #{ctx[:server][:local_ip]}:#{ctx[:server][:local_port]}") end |
#on_helo_event(ctx, helo_data) ⇒ Object
event on HELO/EHLO you may change the ctx[:helo_response] in here so that this will be used as greeting string the value is not allowed to return CR nor LF chars and will be stripped
490 |
# File 'lib/midi-smtp-server.rb', line 490 def on_helo_event(ctx, helo_data) end |
#on_logging_event(_ctx, severity, msg, err: nil) ⇒ Object
event on LOGGING the exposed logger property is from class MidiSmtpServer::ForwardingLogger and pushes any logging message to this on_logging_event. if logging occurs from inside session, the _ctx should be not nil if logging occurs from an error, the err object should be filled
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
# File 'lib/midi-smtp-server.rb', line 456 def on_logging_event(_ctx, severity, msg, err: nil) case severity when Logger::INFO @logger_protected.info(msg) when Logger::WARN @logger_protected.warn(msg) when Logger::ERROR @logger_protected.error(msg) when Logger::FATAL @logger_protected.fatal(msg) err.backtrace.each { |log| @logger_protected.fatal("#{log}") } else @logger_protected.debug(msg) end end |
#on_mail_from_event(ctx, mail_from_data) ⇒ Object
get address send in MAIL FROM if any value returned, that will be used for ongoing processing otherwise the original value will be used
523 |
# File 'lib/midi-smtp-server.rb', line 523 def on_mail_from_event(ctx, mail_from_data) end |
#on_message_data_event(ctx) ⇒ Object
get each message after DATA <message>
540 |
# File 'lib/midi-smtp-server.rb', line 540 def (ctx) end |
#on_message_data_headers_event(ctx) ⇒ Object
event when headers are received while receiving message DATA
537 |
# File 'lib/midi-smtp-server.rb', line 537 def (ctx) end |
#on_message_data_receiving_event(ctx) ⇒ Object
event while receiving message DATA
534 |
# File 'lib/midi-smtp-server.rb', line 534 def (ctx) end |
#on_message_data_start_event(ctx) ⇒ Object
event when beginning with message DATA
531 |
# File 'lib/midi-smtp-server.rb', line 531 def (ctx) end |
#on_process_line_unknown_event(_ctx, _line) ⇒ Object
event when process_line identifies an unknown command line allows to abort sessions for a series of unknown activities to prevent denial of service attacks etc.
545 546 547 548 |
# File 'lib/midi-smtp-server.rb', line 545 def on_process_line_unknown_event(_ctx, _line) # per default we encounter an error raise Smtpd500Exception end |
#on_proxy_event(ctx, proxy_data) ⇒ Object
event on PROXY you may raise an exception if you want to block some addresses you also may change or add any value of the hash: source_ip, source_host, source_port, dest_ip, dest_host, dest_port a returned value hash is set as ctx[:proxy]
497 |
# File 'lib/midi-smtp-server.rb', line 497 def on_proxy_event(ctx, proxy_data) end |
#on_rcpt_to_event(ctx, rcpt_to_data) ⇒ Object
get each address send in RCPT TO if any value returned, that will be used for ongoing processing otherwise the original value will be used
528 |
# File 'lib/midi-smtp-server.rb', line 528 def on_rcpt_to_event(ctx, rcpt_to_data) end |
#ports ⇒ Object
Array of ports on which to bind, set as string separated by commata like ‘2525, 3535’ or ‘2525:3535, 2525’
172 173 174 175 |
# File 'lib/midi-smtp-server.rb', line 172 def ports # prevent original array from being changed @ports.dup end |
#pre_fork? ⇒ Boolean
Return if in pre-fork mode
118 119 120 |
# File 'lib/midi-smtp-server.rb', line 118 def pre_fork? @pre_fork > 1 end |
#processings ⇒ Object
Return the current number of processed clients
108 109 110 |
# File 'lib/midi-smtp-server.rb', line 108 def processings @processings.size end |
#processings? ⇒ Boolean
Return if has active processed clients
113 114 115 |
# File 'lib/midi-smtp-server.rb', line 113 def processings? @processings.any? end |
#shutdown ⇒ Object
Schedule a shutdown for the server
88 89 90 |
# File 'lib/midi-smtp-server.rb', line 88 def shutdown @shutdown = true end |
#shutdown? ⇒ Boolean
test for shutdown state
93 94 95 |
# File 'lib/midi-smtp-server.rb', line 93 def shutdown? @shutdown end |
#ssl_context ⇒ Object
Current TLS OpenSSL::SSL::SSLContext when initialized by :TLS_OPTIONAL, :TLS_REQUIRED
190 191 192 |
# File 'lib/midi-smtp-server.rb', line 190 def ssl_context @tls&.ssl_context end |
#start ⇒ Object
Create the server
50 51 52 53 54 |
# File 'lib/midi-smtp-server.rb', line 50 def start create_service # immediately attach the threads to running single master process (default) attach_threads unless pre_fork? end |
#stop(wait_seconds_before_close: 2, gracefully: true) ⇒ Object
Stop the server
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/midi-smtp-server.rb', line 57 def stop(wait_seconds_before_close: 2, gracefully: true) begin # signal pre_forked workers to stop @workers.each { |worker_pid| Process.kill(:TERM, worker_pid) } if pre_fork? && master? # always signal shutdown shutdown if gracefully # wait if some connection(s) need(s) more time to handle shutdown sleep wait_seconds_before_close if connections? # drop tcp_servers while raising SmtpdStopServiceException @connections_mutex.synchronize do @tcp_server_threads.each do |tcp_server_thread| # use safe navigation (&.) to make sure that obj exists like ... if tcp_server_thread tcp_server_thread&.raise SmtpdStopServiceException end end ensure # check for removing TCPServers @tcp_servers.each { |tcp_server| remove_tcp_server(tcp_server) } if master? end # wait if some connection(s) still need(s) more time to come down sleep wait_seconds_before_close if connections? || !stopped? end |
#stopped? ⇒ Boolean
Returns true if the server has stopped.
83 84 85 |
# File 'lib/midi-smtp-server.rb', line 83 def stopped? master? ? @workers.empty? && @tcp_server_threads.empty? && @tcp_servers.empty? : @tcp_server_threads.empty? end |
#worker? ⇒ Boolean
Return if this is a forked worker process
128 129 130 |
# File 'lib/midi-smtp-server.rb', line 128 def worker? @is_forked end |
#workers ⇒ Object
Return number of forked worker processes
133 134 135 |
# File 'lib/midi-smtp-server.rb', line 133 def workers @workers.size end |
#workers? ⇒ Boolean
Return if has active forked worker processes
138 139 140 |
# File 'lib/midi-smtp-server.rb', line 138 def workers? @workers.any? end |