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_wait_available ⇒ Object
readonly
Time in fraction of a second to wait on IO::WaitReadable exception.
-
#io_wait_mode ⇒ Object
readonly
how to wait 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_wait_mode: nil, io_wait_available: 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
. -
#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_wait_mode: nil, io_wait_available: 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
portsports to listen on. Allows multiple ports like "2525, 3535" or "2525:3535, 2525". Default value = DEFAULT_SMTPD_PORT
hostsinterface 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_forknumber of processes to pre-fork to enable load balancing over multiple cores. Default value = DEFAULT_SMTPD_PRE_FORK
max_processingsmaximum number of simultaneous processed connections, this does not limit the number of concurrent TCP connections. Default value = DEFAULT_SMTPD_MAX_PROCESSINGS
max_connectionsmaximum number of connections, this does limit the number of concurrent TCP connections (not set or nil => unlimited)
crlf_modeCRLF handling support (:CRLF_ENSURE [default], :CRLF_LEAVE, :CRLF_STRICT)
do_dns_reverse_lookupflag if this smtp server should do reverse DNS lookups on incoming connections
io_wait_modehow to wait when no input data is available (DEFAULT_IO_WAIT_MODE)
io_wait_availablefraction of a second to wait in loop when no input data is available (DEFAULT_IO_WAIT_AVAILABLE)
io_cmd_timeouttime in seconds to wait until complete line of data is expected (DEFAULT_IO_CMD_TIMEOUT, nil => disabled test)
io_buffer_chunk_sizesize of chunks (bytes) to read non-blocking from socket (DEFAULT_IO_BUFFER_CHUNK_SIZE)
io_buffer_max_sizemax size of buffer (max line length) until \lf ist expected (DEFAULT_IO_BUFFER_MAX_SIZE, nil => disabled test)
pipelining_extensionset to true for support of SMTP PIPELINING extension (DEFAULT_PIPELINING_EXTENSION_ENABLED)
internationalization_extensionsset to true for support of SMTP 8BITMIME and SMTPUTF8 extensions (DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED)
proxy_extensionset to true for supporting PROXY connections (DEFAULT_PROXY_EXTENSION_ENABLED)
auth_modeenable builtin authentication support (:AUTH_FORBIDDEN [default], :AUTH_OPTIONAL, :AUTH_REQUIRED)
tls_modeenable builtin TLS support (:TLS_FORBIDDEN [default], :TLS_OPTIONAL, :TLS_REQUIRED)
tls_cert_pathpath to tls certificate chain file
tls_key_pathpath to tls key file
tls_ciphersallowed ciphers for connection
tls_methodsallowed methods for protocol
tls_cert_cnset subject (CN) for self signed certificate "cn.domain.com"
tls_cert_sanset subject alternative (SAN) for self signed certificate, allows multiple names like "alt1.domain.com, alt2.domain.com"
loggerown logger class, otherwise default logger is created (DEPRECATED: use on_logging_event for special needs on logging)
logger_severityset or override the logger level if given
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 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
# File 'lib/midi-smtp-server.rb', line 256 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_wait_mode: nil, io_wait_available: nil, io_waitreadable_sleep: nil, # deprecated, but for compatibility 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 # rubocop: disable-next Style/RedundantCondition @do_dns_reverse_lookup = do_dns_reverse_lookup.nil? ? true : do_dns_reverse_lookup # deprecated compatibility raise 'Not allowed to use io_wait_available and deprecated io_waitreadable_sleep at the same time!' unless io_waitreadable_sleep.nil? || io_wait_available.nil? unless io_waitreadable_sleep.nil? io_wait_available = io_waitreadable_sleep io_wait_mode = :IO_WAIT_SLEEP @logger_protected.warn('Deprecated: "io_waitreadable_sleep" was replaced! Please use "io_wait_available" and "io_wait_mode" instead.') end # io and buffer settings @io_wait_mode = io_wait_mode.nil? ? DEFAULT_IO_WAIT_MODE : io_wait_mode raise "Unknown io wait mode #{@io_wait_mode} was given!" unless IO_WAIT_MODES.include?(@io_wait_mode) @io_wait_available = io_wait_available.nil? ? DEFAULT_IO_WAIT_AVAILABLE : io_wait_available raise 'Value of time must be positive (io_wait_available)' unless @io_wait_available.positive? @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
216 217 218 |
# File 'lib/midi-smtp-server.rb', line 216 def auth_mode @auth_mode end |
#crlf_mode ⇒ Object (readonly)
CRLF handling based on conformity to RFC(2)822
202 203 204 |
# File 'lib/midi-smtp-server.rb', line 202 def crlf_mode @crlf_mode end |
#do_dns_reverse_lookup ⇒ Object (readonly)
Flag if should do reverse DNS lookups on incoming connections
214 215 216 |
# File 'lib/midi-smtp-server.rb', line 214 def do_dns_reverse_lookup @do_dns_reverse_lookup end |
#encrypt_mode ⇒ Object (readonly)
Encryption mode
218 219 220 |
# File 'lib/midi-smtp-server.rb', line 218 def encrypt_mode @encrypt_mode end |
#internationalization_extensions ⇒ Object (readonly)
handle SMTP 8BITMIME and SMTPUTF8 extension
222 223 224 |
# File 'lib/midi-smtp-server.rb', line 222 def internationalization_extensions @internationalization_extensions end |
#io_buffer_chunk_size ⇒ Object (readonly)
Bytes to read non-blocking from socket into buffer, as a FixNum
210 211 212 |
# File 'lib/midi-smtp-server.rb', line 210 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
212 213 214 |
# File 'lib/midi-smtp-server.rb', line 212 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
208 209 210 |
# File 'lib/midi-smtp-server.rb', line 208 def io_cmd_timeout @io_cmd_timeout end |
#io_wait_available ⇒ Object (readonly)
Time in fraction of a second to wait on IO::WaitReadable exception
206 207 208 |
# File 'lib/midi-smtp-server.rb', line 206 def io_wait_available @io_wait_available end |
#io_wait_mode ⇒ Object (readonly)
how to wait on IO::WaitReadable exception
204 205 206 |
# File 'lib/midi-smtp-server.rb', line 204 def io_wait_mode @io_wait_mode end |
#logger ⇒ Object (readonly)
logging object, may be overridden by special loggers like YELL or others
227 228 229 |
# File 'lib/midi-smtp-server.rb', line 227 def logger @logger end |
#max_connections ⇒ Object (readonly)
Maximum number of allowed connections, this does limit the TCP connections, as a FixNum
200 201 202 |
# File 'lib/midi-smtp-server.rb', line 200 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
198 199 200 |
# File 'lib/midi-smtp-server.rb', line 198 def max_processings @max_processings end |
#pipelining_extension ⇒ Object (readonly)
handle SMTP PIPELINING extension
220 221 222 |
# File 'lib/midi-smtp-server.rb', line 220 def pipelining_extension @pipelining_extension end |
#proxy_extension ⇒ Object (readonly)
handle PROXY connections
224 225 226 |
# File 'lib/midi-smtp-server.rb', line 224 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
187 188 189 190 |
# File 'lib/midi-smtp-server.rb', line 187 def addresses # prevent original array from being changed @addresses.dup end |
#authenticated?(ctx) ⇒ Boolean
check the status of authentication for a given context
530 531 532 |
# File 'lib/midi-smtp-server.rb', line 530 def authenticated?(ctx) ctx[:server][:authenticated] && !ctx[:server][:authenticated].to_s.empty? end |
#connections ⇒ Object
Return the current number of connected clients
101 102 103 |
# File 'lib/midi-smtp-server.rb', line 101 def connections @connections.size end |
#connections? ⇒ Boolean
Return if has active connected clients
106 107 108 |
# File 'lib/midi-smtp-server.rb', line 106 def connections? @connections.any? end |
#encrypted?(ctx) ⇒ Boolean
check the status of encryption for a given context
535 536 537 |
# File 'lib/midi-smtp-server.rb', line 535 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'
181 182 183 184 |
# File 'lib/midi-smtp-server.rb', line 181 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
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/midi-smtp-server.rb', line 148 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
126 127 128 |
# File 'lib/midi-smtp-server.rb', line 126 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
521 522 523 524 525 526 527 |
# File 'lib/midi-smtp-server.rb', line 521 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
496 497 498 |
# File 'lib/midi-smtp-server.rb', line 496 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
501 502 503 |
# File 'lib/midi-smtp-server.rb', line 501 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
509 |
# File 'lib/midi-smtp-server.rb', line 509 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
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 |
# File 'lib/midi-smtp-server.rb', line 475 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
542 |
# File 'lib/midi-smtp-server.rb', line 542 def on_mail_from_event(ctx, mail_from_data) end |
#on_message_data_event(ctx) ⇒ Object
get each message after DATA
559 |
# File 'lib/midi-smtp-server.rb', line 559 def (ctx) end |
#on_message_data_headers_event(ctx) ⇒ Object
event when headers are received while receiving message DATA
556 |
# File 'lib/midi-smtp-server.rb', line 556 def (ctx) end |
#on_message_data_receiving_event(ctx) ⇒ Object
event while receiving message DATA
553 |
# File 'lib/midi-smtp-server.rb', line 553 def (ctx) end |
#on_message_data_start_event(ctx) ⇒ Object
event when beginning with message DATA
550 |
# File 'lib/midi-smtp-server.rb', line 550 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.
564 565 566 567 |
# File 'lib/midi-smtp-server.rb', line 564 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]
516 |
# File 'lib/midi-smtp-server.rb', line 516 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
547 |
# File 'lib/midi-smtp-server.rb', line 547 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'
175 176 177 178 |
# File 'lib/midi-smtp-server.rb', line 175 def ports # prevent original array from being changed @ports.dup end |
#pre_fork? ⇒ Boolean
Return if in pre-fork mode
121 122 123 |
# File 'lib/midi-smtp-server.rb', line 121 def pre_fork? @pre_fork > 1 end |
#processings ⇒ Object
Return the current number of processed clients
111 112 113 |
# File 'lib/midi-smtp-server.rb', line 111 def processings @processings.size end |
#processings? ⇒ Boolean
Return if has active processed clients
116 117 118 |
# File 'lib/midi-smtp-server.rb', line 116 def processings? @processings.any? end |
#shutdown ⇒ Object
Schedule a shutdown for the server
91 92 93 |
# File 'lib/midi-smtp-server.rb', line 91 def shutdown @shutdown = true end |
#shutdown? ⇒ Boolean
test for shutdown state
96 97 98 |
# File 'lib/midi-smtp-server.rb', line 96 def shutdown? @shutdown end |
#ssl_context ⇒ Object
Current TLS OpenSSL::SSL::SSLContext when initialized by :TLS_OPTIONAL, :TLS_REQUIRED
193 194 195 |
# File 'lib/midi-smtp-server.rb', line 193 def ssl_context @tls&.ssl_context end |
#start ⇒ Object
Create the server
53 54 55 56 57 |
# File 'lib/midi-smtp-server.rb', line 53 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
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/midi-smtp-server.rb', line 60 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.
86 87 88 |
# File 'lib/midi-smtp-server.rb', line 86 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
131 132 133 |
# File 'lib/midi-smtp-server.rb', line 131 def worker? @is_forked end |
#workers ⇒ Object
Return number of forked worker processes
136 137 138 |
# File 'lib/midi-smtp-server.rb', line 136 def workers @workers.size end |
#workers? ⇒ Boolean
Return if has active forked worker processes
141 142 143 |
# File 'lib/midi-smtp-server.rb', line 141 def workers? @workers.any? end |