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
- Write =
Proc.new do |ctx, sess, buf, len| addrinfo = Session.from_ptr(sess).addrinfo 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.from_ptr(sess).addrinfo # 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, nil, :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) ⇒ Object
- #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
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 |
# File 'lib/tinydtls/udpsocket.rb', line 39 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 id = object_id CONTEXT_MAP[id] = TinyDTLS::Context.new(@sendfn, @queue, @secconf) cptr = Wrapper::dtls_new_context(FFI::Pointer.new(id)) @ctx = Wrapper::DTLSContextStruct.new(cptr) if timeout.nil? @sessions = SessionManager.new(@ctx) else @sessions = SessionManager.new(@ctx, timeout) end @handler = Wrapper::DTLSHandlerStruct.new @handler[:write] = UDPSocket::Write @handler[:read] = UDPSocket::Read @handler[:get_psk_info] = SecurityConfig::GetPSKInfo Wrapper::dtls_set_handler(@ctx, @handler) end |
Instance Method Details
#add_client(id, key) ⇒ Object
68 69 70 |
# File 'lib/tinydtls/udpsocket.rb', line 68 def add_client(id, key) @secconf.add_client(id, key) end |
#bind(host, port) ⇒ Object
72 73 74 75 |
# File 'lib/tinydtls/udpsocket.rb', line 72 def bind(host, port) super(host, port) start_thread end |
#close ⇒ Object
TODO: close_read,write
79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/tinydtls/udpsocket.rb', line 79 def close @sessions.destroy! @thread.kill unless @thread.nil? # DTLS free context sends messages to peers so we need to # call it before we actually close the underlying socket. Wrapper::dtls_free_context(@ctx) 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(object_id) end |
#connect(host, port) ⇒ Object
94 95 96 97 |
# File 'lib/tinydtls/udpsocket.rb', line 94 def connect(host, port) @defhost = host @defport = port end |
#recvfrom(len = -1,, flags = 0) ⇒ Object
99 100 101 102 |
# File 'lib/tinydtls/udpsocket.rb', line 99 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
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/tinydtls/udpsocket.rb', line 104 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.
128 129 130 131 |
# File 'lib/tinydtls/udpsocket.rb', line 128 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
133 134 135 136 |
# File 'lib/tinydtls/udpsocket.rb', line 133 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
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/tinydtls/udpsocket.rb', line 138 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, nil, :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` until it # succeeds which is suboptimal because it doesn't take into # account that the handshake may fail. until (res = dtls_send(addr, mesg)) > 0 sleep 1 end return res end |