Class: TinyDTLS::UDPSocket
- Inherits:
-
UDPSocket
- Object
- UDPSocket
- TinyDTLS::UDPSocket
- Defined in:
- lib/tinydtls/udpsocket.rb
Overview
This class implements a DTLS socket on top of a ruby UDPSocket. It isn’t currently nowhere near being API compatible with the ruby UDPSocket. Being 100% backwards compatible with the ruby UDPSocket is not possible to to tinydtls internals. For instance we can’t properly implement IO#select. It should thus be considered if it is really a good idea to extend the ruby UDPSocket in the long run.
Basic send and receive methods are implemented and should work.
Constant Summary collapse
- MAX_RETRY =
Maximum of times a dtls_send is retried.
5.freeze
- Write =
Proc.new do |ctx, sess, buf, len| addrinfo = Session.addr_from_ptr(sess) ctxobj = TinyDTLS::Context.from_ptr(ctx) ctxobj.sendfn.call(buf.read_string(len), Socket::MSG_DONTWAIT, addrinfo.ip_address, addrinfo.ip_port) end
- Read =
Proc.new do |ctx, sess, buf, len| addrinfo = Session.addr_from_ptr(sess) # We need to perform a reverse lookup here because # the #recvfrom function needs to return the DNS # hostname. sender = Socket.getaddrinfo(addrinfo.ip_address, addrinfo.ip_port, addrinfo.afamily, :DGRAM, 0, 0, true).first ctxobj = TinyDTLS::Context.from_ptr(ctx) ctxobj.queue.push([buf.read_string(len), sender]) # It is unclear to me why this callback even needs a return value, # the `tests/dtls-client.c` program in the tinydtls repository # simply uses 0 as a return value, so let's do that as well. 0 end
Instance Method Summary collapse
-
#add_client(id, key, default = false) ⇒ Object
Adds a new identity/key pair to the underlying TinyDTLS::SessionManager.
- #bind(host, port) ⇒ Object
-
#close ⇒ Object
TODO: close_read,write.
- #connect(host, port) ⇒ Object
-
#initialize(address_family = Socket::AF_INET, timeout = nil) ⇒ UDPSocket
constructor
A new instance of UDPSocket.
- #recvfrom(len = -1,, flags = 0) ⇒ Object
- #recvfrom_nonblock(len = -1,, flag = 0, outbuf = nil, exception: true) ⇒ Object
-
#recvmsg(maxmesglen = nil, flags = 0, maxcontrollen = nil, opts = {}) ⇒ Object
TODO: The recvmsg functions only implement a subset of the functionallity of the UDP socket class, e.g.
- #recvmsg_nonblock(maxdatalen = nil, flags = 0, maxcontrollen = nil, opts = {}) ⇒ Object
- #send(mesg, flags, host = nil, port = nil) ⇒ Object
Constructor Details
#initialize(address_family = Socket::AF_INET, timeout = nil) ⇒ UDPSocket
Returns a new instance of UDPSocket.
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 |
# File 'lib/tinydtls/udpsocket.rb', line 43 def initialize(address_family = Socket::AF_INET, timeout = nil) super(address_family) Wrapper::dtls_init @timeout = timeout.freeze @queue = Queue.new @family = address_family @sendfn = method(:send).super_method @secconf = SecurityConfig.new @context = TinyDTLS::Context.new(@sendfn, @queue, @secconf) CONTEXT_MAP[@context.key] = @context if timeout.nil? @sessions = SessionManager.new(@context) else @sessions = SessionManager.new(@context, timeout) end @handler = Wrapper::DTLSHandlerStruct.new @handler[:write] = UDPSocket::Write @handler[:read] = UDPSocket::Read @handler[:get_psk_info] = SecurityConfig::GetPSKInfo Wrapper::dtls_set_handler(@context.to_ffi, @handler) end |
Instance Method Details
#add_client(id, key, default = false) ⇒ Object
Adds a new identity/key pair to the underlying TinyDTLS::SessionManager. By default the first pair added to the store will be used for establishing new handshakes, this behaviour can be changed using the optional default argument.
73 74 75 76 77 78 79 80 |
# File 'lib/tinydtls/udpsocket.rb', line 73 def add_client(id, key, default = false) @secconf.add_client(id, key) if default @secconf.default_id = id @secconf.default_key = key end end |
#bind(host, port) ⇒ Object
82 83 84 85 |
# File 'lib/tinydtls/udpsocket.rb', line 82 def bind(host, port) super(host, port) start_thread end |
#close ⇒ Object
TODO: close_read,write
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/tinydtls/udpsocket.rb', line 89 def close # This method can't be called twice since we actually free memory # allocated by ruby-ffi in this function. Since we can't free it # twice we ensure that this function is only called once. return unless CONTEXT_MAP.has_key? @context.key @sessions.close unless @thread.nil? @thread.kill while @thread.alive? @thread.join end end # dtls_free_context sends messages to peers so we need to # explicitly free the dtls_context_t before closing the socket. Wrapper::dtls_free_context(@context.to_ffi) super # Assuming the @thread is already stopped at this point # we can safely access the CONTEXT_MAP without running # into any kind of concurrency problems. CONTEXT_MAP.delete(@context.key) end |
#connect(host, port) ⇒ Object
114 115 116 117 |
# File 'lib/tinydtls/udpsocket.rb', line 114 def connect(host, port) @defhost = host @defport = port end |
#recvfrom(len = -1,, flags = 0) ⇒ Object
119 120 121 122 |
# File 'lib/tinydtls/udpsocket.rb', line 119 def recvfrom(len = -1, flags = 0) ary = @queue.pop return [byteslice(ary.first, len), ary.last] end |
#recvfrom_nonblock(len = -1,, flag = 0, outbuf = nil, exception: true) ⇒ Object
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/tinydtls/udpsocket.rb', line 124 def recvfrom_nonblock(len = -1, flag = 0, outbuf = nil, exception: true) ary = nil begin ary = @queue.pop(true) rescue ThreadError if exception raise IO::EAGAINWaitReadable else return :wait_readable end end pay = byteslice(ary.first, len) unless outbuf.nil? outbuf << pay end return [pay, ary.last] end |
#recvmsg(maxmesglen = nil, flags = 0, maxcontrollen = nil, opts = {}) ⇒ Object
TODO: The recvmsg functions only implement a subset of the functionallity of the UDP socket class, e.g. they don’t return ancillary data.
148 149 150 151 |
# File 'lib/tinydtls/udpsocket.rb', line 148 def recvmsg(maxmesglen = nil, flags = 0, maxcontrollen = nil, opts = {}) mesg, sender = recvfrom(maxmesglen.nil? ? -1 : maxmesglen, flags) return [mesg, to_addrinfo(*sender), 0, nil] end |
#recvmsg_nonblock(maxdatalen = nil, flags = 0, maxcontrollen = nil, opts = {}) ⇒ Object
153 154 155 156 |
# File 'lib/tinydtls/udpsocket.rb', line 153 def recvmsg_nonblock(maxdatalen = nil, flags = 0, maxcontrollen = nil, opts = {}) mesg, sender = recvfrom_nonblock(maxdatalen.nil? ? -1 : maxdatalen, flags) return [mesg, to_addrinfo(*sender), 0, nil] end |
#send(mesg, flags, host = nil, port = nil) ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/tinydtls/udpsocket.rb', line 158 def send(mesg, flags, host = nil, port = nil) start_thread if host.nil? and port.nil? if @defport.nil? or @defhost.nil? raise Errno::EDESTADDRREQ end host = @defhost port = @defport elsif port.nil? # host is not nil and must be a sockaddr_to port, host = Socket.unpack_sockaddr_in(host) end addr = Addrinfo.getaddrinfo(host, port, @family, :DGRAM).first # If a new thread has been started above a new handshake needs to # be performed by it. We need to block here until the handshake # was completed. # # The current approach is calling `Wrapper::dtls_write` up to # MAX_RETRY times. If we didn't manage to send our data to the # peer after MAX_RETRY times an exception is raised. MAX_RETRY.times do res = dtls_send(addr, mesg) if res > 0 return res end sleep 1 end raise Errno::ECONNREFUSED.new("DTLS handshake failed") end |