2013-03-18 19:20:17 -04:00
|
|
|
module Puma
|
|
|
|
module MiniSSL
|
|
|
|
class Socket
|
|
|
|
def initialize(socket, engine)
|
|
|
|
@socket = socket
|
|
|
|
@engine = engine
|
2015-01-13 23:11:26 -05:00
|
|
|
@peercert = nil
|
2013-03-18 19:20:17 -04:00
|
|
|
end
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
def to_io
|
|
|
|
@socket
|
|
|
|
end
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
def readpartial(size)
|
|
|
|
while true
|
|
|
|
output = @engine.read
|
|
|
|
return output if output
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
data = @socket.readpartial(size)
|
|
|
|
@engine.inject(data)
|
|
|
|
output = @engine.read
|
2012-08-23 01:34:10 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
return output if output
|
2012-08-23 01:34:10 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
while neg_data = @engine.extract
|
|
|
|
@socket.write neg_data
|
|
|
|
end
|
2012-08-23 01:34:10 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-11-24 22:57:12 -05:00
|
|
|
def engine_read_all
|
|
|
|
output = @engine.read
|
|
|
|
while output and additional_output = @engine.read
|
|
|
|
output << additional_output
|
|
|
|
end
|
|
|
|
output
|
|
|
|
end
|
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
def read_nonblock(size)
|
|
|
|
while true
|
2013-11-24 22:57:12 -05:00
|
|
|
output = engine_read_all
|
2013-03-18 19:20:17 -04:00
|
|
|
return output if output
|
2012-08-23 01:34:10 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
data = @socket.read_nonblock(size)
|
2012-08-23 01:34:10 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
@engine.inject(data)
|
2013-11-24 22:57:12 -05:00
|
|
|
output = engine_read_all
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
return output if output
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
while neg_data = @engine.extract
|
|
|
|
@socket.write neg_data
|
|
|
|
end
|
2012-08-22 19:53:25 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
def write(data)
|
2013-05-06 14:32:03 -04:00
|
|
|
need = data.bytesize
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
while true
|
|
|
|
wrote = @engine.write data
|
|
|
|
enc = @engine.extract
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-06-18 02:20:54 -04:00
|
|
|
while enc
|
|
|
|
@socket.write enc
|
|
|
|
enc = @engine.extract
|
2013-03-18 19:20:17 -04:00
|
|
|
end
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
need -= wrote
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-05-06 14:32:03 -04:00
|
|
|
return data.bytesize if need == 0
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2014-05-05 17:30:15 -04:00
|
|
|
data = data[wrote..-1]
|
2013-03-18 19:20:17 -04:00
|
|
|
end
|
2012-08-22 19:53:25 -04:00
|
|
|
end
|
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
alias_method :syswrite, :write
|
2012-09-10 11:50:43 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
def flush
|
|
|
|
@socket.flush
|
|
|
|
end
|
2012-08-23 00:43:40 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
def close
|
2016-07-25 20:20:17 -04:00
|
|
|
begin
|
|
|
|
# Try to setup (so that we can then close them) any
|
|
|
|
# partially initialized sockets.
|
|
|
|
while @engine.init?
|
|
|
|
# Don't let this socket hold this loop forever.
|
|
|
|
# If it can't send more packets within 1s, then
|
|
|
|
# give up.
|
|
|
|
return unless IO.select([@socket], nil, nil, 1)
|
|
|
|
begin
|
|
|
|
read_nonblock(1024)
|
|
|
|
rescue Errno::EAGAIN
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
done = @engine.shutdown
|
|
|
|
|
|
|
|
while true
|
|
|
|
enc = @engine.extract
|
|
|
|
@socket.write enc
|
|
|
|
|
|
|
|
notify = @socket.sysread(1024)
|
|
|
|
|
|
|
|
@engine.inject notify
|
|
|
|
done = @engine.shutdown
|
|
|
|
|
|
|
|
break if done
|
|
|
|
end
|
|
|
|
rescue IOError, SystemCallError
|
|
|
|
# nothing
|
|
|
|
ensure
|
|
|
|
@socket.close
|
|
|
|
end
|
2013-03-18 19:20:17 -04:00
|
|
|
end
|
2012-08-23 00:43:40 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
def peeraddr
|
|
|
|
@socket.peeraddr
|
|
|
|
end
|
2015-01-13 23:11:26 -05:00
|
|
|
|
|
|
|
def peercert
|
|
|
|
return @peercert if @peercert
|
|
|
|
|
|
|
|
raw = @engine.peercert
|
|
|
|
return nil unless raw
|
|
|
|
|
|
|
|
@peercert = OpenSSL::X509::Certificate.new raw
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if defined?(JRUBY_VERSION)
|
|
|
|
class SSLError < StandardError
|
|
|
|
# Define this for jruby even though it isn't used.
|
|
|
|
end
|
2015-09-18 12:43:51 -04:00
|
|
|
|
|
|
|
def self.check; end
|
2012-08-23 00:43:40 -04:00
|
|
|
end
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
class Context
|
|
|
|
attr_accessor :verify_mode
|
2012-11-30 07:47:47 -05:00
|
|
|
|
2014-05-05 17:30:15 -04:00
|
|
|
if defined?(JRUBY_VERSION)
|
|
|
|
# jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
|
|
|
|
attr_reader :keystore
|
|
|
|
attr_accessor :keystore_pass
|
2012-11-30 07:47:47 -05:00
|
|
|
|
2014-05-05 17:30:15 -04:00
|
|
|
def keystore=(keystore)
|
|
|
|
raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore
|
|
|
|
@keystore = keystore
|
|
|
|
end
|
2016-07-24 17:29:23 -04:00
|
|
|
|
|
|
|
def check
|
|
|
|
raise "Keystore not configured" unless @keystore
|
|
|
|
end
|
|
|
|
|
2014-05-05 17:30:15 -04:00
|
|
|
else
|
|
|
|
# non-jruby Context properties
|
|
|
|
attr_reader :key
|
|
|
|
attr_reader :cert
|
2015-01-13 23:11:26 -05:00
|
|
|
attr_reader :ca
|
2014-05-05 17:30:15 -04:00
|
|
|
|
|
|
|
def key=(key)
|
|
|
|
raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
|
|
|
|
@key = key
|
|
|
|
end
|
2012-11-30 07:47:47 -05:00
|
|
|
|
2014-05-05 17:30:15 -04:00
|
|
|
def cert=(cert)
|
|
|
|
raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
|
|
|
|
@cert = cert
|
|
|
|
end
|
2015-01-13 23:11:26 -05:00
|
|
|
|
|
|
|
def ca=(ca)
|
|
|
|
raise ArgumentError, "No such ca file '#{ca}'" unless File.exist? ca
|
|
|
|
@ca = ca
|
|
|
|
end
|
2016-07-24 17:29:23 -04:00
|
|
|
|
|
|
|
def check
|
|
|
|
raise "Key not configured" unless @key
|
|
|
|
raise "Cert not configured" unless @cert
|
|
|
|
end
|
2013-03-18 19:20:17 -04:00
|
|
|
end
|
2012-11-30 07:47:47 -05:00
|
|
|
end
|
2012-08-23 00:43:40 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
VERIFY_NONE = 0
|
|
|
|
VERIFY_PEER = 1
|
2015-01-13 23:11:26 -05:00
|
|
|
VERIFY_FAIL_IF_NO_PEER_CERT = 2
|
2012-08-23 00:43:40 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
class Server
|
|
|
|
def initialize(socket, ctx)
|
|
|
|
@socket = socket
|
|
|
|
@ctx = ctx
|
|
|
|
end
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
def to_io
|
|
|
|
@socket
|
|
|
|
end
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
def accept
|
2016-07-24 17:29:23 -04:00
|
|
|
@ctx.check
|
2013-03-18 19:20:17 -04:00
|
|
|
io = @socket.accept
|
2014-05-05 17:30:15 -04:00
|
|
|
engine = Engine.server @ctx
|
2012-08-22 19:53:25 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
Socket.new io, engine
|
|
|
|
end
|
2012-08-23 00:43:40 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
def accept_nonblock
|
2016-07-24 17:29:23 -04:00
|
|
|
@ctx.check
|
2013-03-18 19:20:17 -04:00
|
|
|
io = @socket.accept_nonblock
|
2014-05-05 17:30:15 -04:00
|
|
|
engine = Engine.server @ctx
|
2012-09-10 11:50:43 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
Socket.new io, engine
|
|
|
|
end
|
2012-09-10 11:50:43 -04:00
|
|
|
|
2013-03-18 19:20:17 -04:00
|
|
|
def close
|
|
|
|
@socket.close
|
|
|
|
end
|
2012-08-23 00:43:40 -04:00
|
|
|
end
|
2012-08-22 19:53:25 -04:00
|
|
|
end
|
|
|
|
end
|