Class: LDAP::Server
- Inherits:
-
Object
- Object
- LDAP::Server
- Defined in:
- lib/ldap/server/version.rb,
lib/ldap/server/util.rb,
lib/ldap/server/match.rb,
lib/ldap/server/filter.rb,
lib/ldap/server/schema.rb,
lib/ldap/server/server.rb,
lib/ldap/server/syntax.rb,
lib/ldap/server/operation.rb,
lib/ldap/server/tcpserver.rb,
lib/ldap/server/connection.rb,
lib/ldap/server/preforkserver.rb
Overview
:nodoc:
Defined Under Namespace
Classes: Connection, Filter, MatchingRule, Operation, Schema, Syntax
Constant Summary collapse
- DEFAULT_OPT =
{ :port=>389, :nodelay=>true, }
- VERSION =
'0.5.2'
- BaseObject =
Scope
0
- SingleLevel =
1
- WholeSubtree =
2
- NeverDerefAliases =
DerefAliases
0
- DerefInSearching =
1
- DerefFindingBaseObj =
2
- DerefAlways =
3
Instance Attribute Summary collapse
-
#logger ⇒ Object
readonly
Create a new server.
-
#root_dse ⇒ Object
Returns the value of attribute root_dse.
Class Method Summary collapse
-
.preforkserver(opt, &blk) ⇒ Object
Accept connections on a port, and for each one run the given block in one of N pre-forked children.
-
.ssl_prepare(opt) ⇒ Object
create opt from the other ssl options.
-
.tcpserver(opt, &blk) ⇒ Object
Accept connections on a port, and for each one start a new thread and run the given block.
Instance Method Summary collapse
-
#initialize(opt = DEFAULT_OPT) ⇒ Server
constructor
A new instance of Server.
- #join ⇒ Object
- #run_prefork ⇒ Object
- #run_tcpserver ⇒ Object
- #stop ⇒ Object
Constructor Details
#initialize(opt = DEFAULT_OPT) ⇒ Server
Returns a new instance of Server.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/ldap/server/server.rb', line 27 def initialize(opt = DEFAULT_OPT) @opt = opt @opt[:server] = self @opt[:operation_class] ||= LDAP::Server::Operation @opt[:operation_args] ||= [] unless @opt[:logger] @opt[:logger] ||= Logger.new($stderr) @opt[:logger].level = Logger::INFO end @logger = @opt[:logger] LDAP::Server.ssl_prepare(@opt) @schema = opt[:schema] # may be nil @root_dse = Hash.new { |h,k| h[k] = [] }.merge({ 'objectClass' => ['top','openLDAProotDSE','extensibleObject'], 'supportedLDAPVersion' => ['3'], #'altServer' => #'supportedExtension' => #'supportedControl' => #'supportedSASLMechanisms' => }) @root_dse['subschemaSubentry'] = [@schema.subschema_dn] if @schema @root_dse['namingContexts'] = opt[:namingContexts] if opt[:namingContexts] end |
Instance Attribute Details
#logger ⇒ Object (readonly)
Create a new server. Options include all those to tcpserver/preforkserver plus:
:operation_class=>Class - set Operation handler class
:operation_args=>[...] - args to Operation.new
:ssl_key_file=>pem, :ssl_cert_file=>pem - enable SSL
:ssl_ca_path=>directory - verify peer certificates
:schema=>Schema - Schema object
:namingContexts=>[dn, ...] - base DN(s) we answer
25 26 27 |
# File 'lib/ldap/server/server.rb', line 25 def logger @logger end |
#root_dse ⇒ Object
Returns the value of attribute root_dse.
9 10 11 |
# File 'lib/ldap/server/server.rb', line 9 def root_dse @root_dse end |
Class Method Details
.preforkserver(opt, &blk) ⇒ Object
Accept connections on a port, and for each one run the given block in one of N pre-forked children. Returns a Thread object for the listener.
Options:
:port=>port number [required]
:bindaddr=>"IP address"
:user=>"username" - drop privileges after bind
:group=>"groupname" - ditto
:logger=>object - implements << method
:listen=>number - listen queue depth
:nodelay=>true - set TCP_NODELAY option
:min_servers=>N - prefork parameters
:max_servers=>N
:max_requests_per_child=>N
:max_idle=>N - seconds
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/ldap/server/preforkserver.rb', line 24 def self.preforkserver(opt, &blk) server = PreFork.new(opt[:bindaddr] || "0.0.0.0", opt[:port]) # Drop privileges if requested if opt[:group] or opt[:user] require 'etc' gid = Etc.getgrnam(opt[:group]).gid if opt[:group] uid = Etc.getpwnam(opt[:user]).uid if opt[:user] File.chown(uid, gid, server.instance_eval {@lockf}) Process.gid = Process.egid = gid if gid Process.uid = Process.euid = uid if uid end # Typically the O/S will buffer response data for 100ms before sending. # If the response is sent as a single write() then there's no need for it. if opt[:nodelay] begin server.sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) rescue Exception end end # set queue size for incoming connections (default is 5) server.sock.listen(opt[:listen]) if opt[:listen] # Set prefork server parameters server.min_servers = opt[:min_servers] if opt[:min_servers] server.max_servers = opt[:max_servers] if opt[:max_servers] server.max_request_per_child = opt[:max_request_per_child] if opt[:max_request_per_child] server.max_idle = opt[:max_idle] if opt[:max_idle] Thread.new do server.start do |s| begin s.instance_eval(&blk) rescue Interrupt # This exception can be raised to shut the server down server.stop rescue Exception => e opt[:logger].error(s.peeraddr[3]) { "#{e}: #{e.backtrace[0]}" } ensure s.close end end end end |
.ssl_prepare(opt) ⇒ Object
create opt from the other ssl options
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/ldap/server/server.rb', line 53 def self.ssl_prepare(opt) # :nodoc: if opt[:ssl_key_file] and opt[:ssl_cert_file] ctx = OpenSSL::SSL::SSLContext.new ctx.key = OpenSSL::PKey::RSA.new(File::read(opt[:ssl_key_file])) ctx.cert = OpenSSL::X509::Certificate.new(File::read(opt[:ssl_cert_file])) if opt[:ssl_dhparams] ctx.tmp_dh_callback = proc { |*args| OpenSSL::PKey::DH.new( File.read(opt[:ssl_dhparams]) ) } end if opt[:ssl_ca_path] ctx.ca_path = opt[:ssl_ca_path] ctx.verify_mode = opt[:ssl_verify_mode] || OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT elsif opt[:ssl_verify_mode] != 0 $stderr.puts "Warning: No ssl_ca_path, peer certificate won't be verified" end opt[:ssl_ctx] = ctx end end |
.tcpserver(opt, &blk) ⇒ Object
Accept connections on a port, and for each one start a new thread and run the given block. Returns the Thread object for the listener.
FIXME:
-
have a limit on total number of concurrent connects
-
have a limit on connections from a single IP, or from a /24 (to avoid the trivial DoS that the first limit creates)
-
ACL using source IP address (or perhaps that belongs in application)
Options:
:port=>port number [required]
:bindaddr=>"IP address"
:user=>"username" - drop privileges after bind
:group=>"groupname" - ditto
:logger=>object - implements << method
:listen=>number - listen queue depth
:nodelay=>true - set TCP_NODELAY option
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/ldap/server/tcpserver.rb', line 24 def self.tcpserver(opt, &blk) server = TCPServer.new(opt[:bindaddr] || "0.0.0.0", opt[:port]) # Drop privileges if requested require 'etc' if opt[:group] or opt[:user] Process.gid = Process.egid = Etc.getgrnam(opt[:group]).gid if opt[:group] Process.uid = Process.euid = Etc.getpwnam(opt[:user]).uid if opt[:user] # Typically the O/S will buffer response data for 100ms before sending. # If the response is sent as a single write() then there's no need for it. if opt[:nodelay] begin server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) rescue Exception end end # set queue size for incoming connections (default is 5) server.listen(opt[:listen]) if opt[:listen] Thread.new do while true begin session = server.accept # subtlety: copy 'session' into a block-local variable because # it will change when the next session is accepted Thread.new(session) do |s| begin s.instance_eval(&blk) rescue Exception => e opt[:logger].error(s.peeraddr[3]) {"#{e}: #{e.backtrace[0]}"} ensure s.close end end rescue Interrupt # This exception can be raised to shut the server down server.close if server and not server.closed? break end end end end |
Instance Method Details
#join ⇒ Object
94 95 96 |
# File 'lib/ldap/server/server.rb', line 94 def join @thread.join end |
#run_prefork ⇒ Object
85 86 87 88 89 90 91 92 |
# File 'lib/ldap/server/server.rb', line 85 def run_prefork require 'ldap/server/preforkserver' opt = @opt @thread = LDAP::Server.preforkserver(@opt) do LDAP::Server::Connection::new(self,opt).handle_requests end end |
#run_tcpserver ⇒ Object
76 77 78 79 80 81 82 83 |
# File 'lib/ldap/server/server.rb', line 76 def run_tcpserver require 'ldap/server/tcpserver' opt = @opt @thread = LDAP::Server.tcpserver(@opt) do LDAP::Server::Connection::new(self,opt).handle_requests end end |
#stop ⇒ Object
98 99 100 101 |
# File 'lib/ldap/server/server.rb', line 98 def stop @thread.raise Interrupt, "" # <= temporary fix for 1.8.6 @thread.join end |