summaryrefslogtreecommitdiff
path: root/ext/openssl/lib/openssl/ssl.rb
blob: 9c31fa73cdf96d221591dfee43967acd279196bf (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
=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"

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

    class SSLSocket
      include Buffering
      include SocketForwarder

      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
        @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