summaryrefslogtreecommitdiff
path: root/ext/openssl/lib/openssl/ssl.rb
blob: 9b287fc939c7c40f407084574bcce18dc3de0af4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
69
70
71
72
73
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
103
104
105
106
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
=begin
= $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL

= Info
  'OpenSSL for Ruby 2' project
  Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
  All rights reserved.

= Licence
  This program is licenced under the same licence as Ruby.
  (See the file 'LICENCE'.)

= Version
  $Id$
=end

require "openssl"
require "openssl/buffering"
require "fcntl"

module OpenSSL
  module SSL
    module SocketForwarder
      def addr
        to_io.addr
      end

      def peeraddr
        to_io.peeraddr
      end

      def getsockopt(level, optname, optval)
        to_io.setsockopt(level, optname, optval)
      end

      def setsockopt(level, optname)
        to_io.setsockopt(level, optname)
      end

      def fcntl(*args)
        to_io.fcntl(*args)
      end

      def closed?
        to_io.closed?
      end

      def do_not_reverse_lookup=(flag)
        to_io.do_not_reverse_lookup = flag
      end
    end

    module Nonblock
      def initialize(*args)
        flag = File::NONBLOCK
        flag |= @io.fcntl(Fcntl::F_GETFL) if defined?(Fcntl::F_GETFL)
        @io.fcntl(Fcntl::F_SETFL, flag)
        super
      end
    end

    class SSLSocket
      include Buffering
      include SocketForwarder
      include Nonblock

      def post_connection_check(hostname)
        check_common_name = true
        cert = peer_cert
        cert.extensions.each{|ext|
          next if ext.oid != "subjectAltName"
          ext.value.split(/,\s+/).each{|general_name|
            if /\ADNS:(.*)/ =~ general_name
              check_common_name = false
              reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
              return true if /\A#{reg}\z/i =~ hostname
            elsif /\AIP Address:(.*)/ =~ general_name
              check_common_name = false
              return true if $1 == hostname
            end
          }
        }
        if check_common_name
          cert.subject.to_a.each{|oid, value|
            if oid == "CN" && value.casecmp(hostname) == 0
              return true
            end
          }
        end
        raise SSLError, "hostname not match"
      end
    end

    class SSLServer
      include SocketForwarder
      attr_accessor :start_immediately

      def initialize(svr, ctx)
        @svr = svr
        @ctx = ctx
        unless ctx.session_id_context
          session_id = OpenSSL::Digest::MD5.hexdigest($0)
          @ctx.session_id_context = session_id
        end
        @start_immediately = true
      end

      def to_io
        @svr
      end

      def listen(backlog=5)
        @svr.listen(backlog)
      end

      def accept
        sock = @svr.accept
        begin
          ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
          ssl.sync_close = true
          ssl.accept if @start_immediately
          ssl
        rescue SSLError => ex
          sock.close
          raise ex
        end
      end

      def close
        @svr.close
      end
    end
  end
end