Class: BasicSocket

Inherits:
IO
  • Object
show all
Defined in:
lib/socket/mri.rb,
lib/socket/basic_socket.rb

Direct Known Subclasses

IPSocket, Socket, UNIXSocket

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.do_not_reverse_lookupObject



15
16
17
18
# File 'lib/socket/basic_socket.rb', line 15

def self.do_not_reverse_lookup
  @no_reverse_lookup = true unless defined?(@no_reverse_lookup)
  @no_reverse_lookup
end

.do_not_reverse_lookup=(setting) ⇒ Object



11
12
13
# File 'lib/socket/basic_socket.rb', line 11

def self.do_not_reverse_lookup=(setting)
  @no_reverse_lookup = setting
end

.for_fd(fixnum) ⇒ Object



2
3
4
5
6
7
8
9
# File 'lib/socket/basic_socket.rb', line 2

def self.for_fd(fixnum)
  sock = allocate

  IO.setup(sock, fixnum, nil, true)
  sock.binmode

  sock
end

Instance Method Details

#close_readObject



234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/socket/basic_socket.rb', line 234

def close_read
  ensure_open

  if mode_read_only?
    return close
  end

  RubySL::Socket::Foreign.shutdown(descriptor, 0)

  force_write_only

  nil
end

#close_writeObject



248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/socket/basic_socket.rb', line 248

def close_write
  ensure_open

  if mode_write_only?
    return close
  end

  RubySL::Socket::Foreign.shutdown(descriptor, 1)

  force_read_only

  nil
end

#connect_addressObject

Returns an address of the socket suitable for connect in the local machine.

This method returns self.local_address, except following condition.

  • IPv4 unspecified address (0.0.0.0) is replaced by IPv4 loopback address (127.0.0.1).

  • IPv6 unspecified address (::) is replaced by IPv6 loopback address (::1).

If the local address is not suitable for connect, SocketError is raised. IPv4 and IPv6 address which port is 0 is not suitable for connect. Unix domain socket which has no path is not suitable for connect.

Addrinfo.tcp("0.0.0.0", 0).listen {|serv|
  p serv.connect_address #=> #<Addrinfo: 127.0.0.1:53660 TCP>
  serv.connect_address.connect {|c|
    s, _ = serv.accept
    p [c, s] #=> [#<Socket:fd 4>, #<Socket:fd 6>]
  }
}


314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/socket/mri.rb', line 314

def connect_address
  addr = local_address
  afamily = addr.afamily
  if afamily == Socket::AF_INET
    raise SocketError, "unbound IPv4 socket" if addr.ip_port == 0
    if addr.ip_address == "0.0.0.0"
      addr = Addrinfo.new(["AF_INET", addr.ip_port, nil, "127.0.0.1"], addr.pfamily, addr.socktype, addr.protocol)
    end
  elsif defined?(Socket::AF_INET6) && afamily == Socket::AF_INET6
    raise SocketError, "unbound IPv6 socket" if addr.ip_port == 0
    if addr.ip_address == "::"
      addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
    elsif addr.ip_address == "0.0.0.0" # MacOS X 10.4 returns "a.b.c.d" for IPv4-mapped IPv6 address.
      addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
    elsif addr.ip_address == "::ffff:0.0.0.0" # MacOS X 10.6 returns "::ffff:a.b.c.d" for IPv4-mapped IPv6 address.
      addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
    end
  elsif defined?(Socket::AF_UNIX) && afamily == Socket::AF_UNIX
    raise SocketError, "unbound Unix socket" if addr.unix_path == ""
  end
  addr
end

#do_not_reverse_lookupObject



24
25
26
# File 'lib/socket/basic_socket.rb', line 24

def do_not_reverse_lookup
  @no_reverse_lookup
end

#do_not_reverse_lookup=(setting) ⇒ Object



20
21
22
# File 'lib/socket/basic_socket.rb', line 20

def do_not_reverse_lookup=(setting)
  @no_reverse_lookup = setting
end

#getpeereidObject



293
294
295
# File 'lib/socket/basic_socket.rb', line 293

def getpeereid
  RubySL::Socket::Foreign.getpeereid(descriptor)
end

#getpeernameObject



93
94
95
# File 'lib/socket/basic_socket.rb', line 93

def getpeername
  RubySL::Socket::Foreign.getpeername(descriptor)
end

#getsocknameObject



89
90
91
# File 'lib/socket/basic_socket.rb', line 89

def getsockname
  RubySL::Socket::Foreign.getsockname(descriptor)
end

#getsockopt(level, optname) ⇒ Object



28
29
30
31
32
33
34
35
36
# File 'lib/socket/basic_socket.rb', line 28

def getsockopt(level, optname)
  sockname = RubySL::Socket::Foreign.getsockname(descriptor)
  family   = RubySL::Socket.family_for_sockaddr_in(sockname)
  level    = RubySL::Socket::SocketOptions.socket_level(level, family)
  optname  = RubySL::Socket::SocketOptions.socket_option(level, optname)
  data     = RubySL::Socket::Foreign.getsockopt(descriptor, level, optname)

  Socket::Option.new(family, level, optname, data)
end

#local_addressObject

MRI defines this method in BasicSocket and stuffs all logic in it. Since inheriting classes behave differently we overwrite this method in said classes. The method here exists so that code such as the following still works: BasicSocket.method_defined?(:local_address).

Raises:

  • (NotImplementedError)


283
284
285
286
# File 'lib/socket/basic_socket.rb', line 283

def local_address
  raise NotImplementedError,
    'This method must be implemented by classes inheriting from BasicSocket'
end

#recv(bytes_to_read, flags = 0) ⇒ Object



129
130
131
# File 'lib/socket/basic_socket.rb', line 129

def recv(bytes_to_read, flags = 0)
  return socket_recv(bytes_to_read, flags, 0)
end

#recv_nonblock(bytes_to_read, flags = 0) ⇒ Object



262
263
264
265
266
267
268
# File 'lib/socket/basic_socket.rb', line 262

def recv_nonblock(bytes_to_read, flags = 0)
  fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)

  RubySL::Socket::Error.wrap_read_nonblock do
    socket_recv(bytes_to_read, flags, 0)
  end
end

#recvmsg(max_msg_len = nil, flags = 0, *_) ⇒ Object



133
134
135
136
137
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/socket/basic_socket.rb', line 133

def recvmsg(max_msg_len = nil, flags = 0, *_)
  socket_type = getsockopt(:SOCKET, :TYPE).int

  if socket_type == Socket::SOCK_STREAM
    grow_msg = false
  else
    grow_msg = max_msg_len.nil?
  end

  flags |= Socket::MSG_PEEK if grow_msg

  msg_len = max_msg_len || 4096

  loop do
    msg_buffer = RubySL::Socket::Foreign.char_pointer(msg_len)
    address    = RubySL::Socket.sockaddr_class_for_socket(self).new
    io_vec     = RubySL::Socket::Foreign::Iovec.with_buffer(msg_buffer)
    header     = RubySL::Socket::Foreign::Msghdr.with_buffers(address, io_vec)

    begin
      need_more = false

      msg_size = RubySL::Socket::Foreign
        .recvmsg(descriptor, header.pointer, flags)

      RubySL::Socket::Error.read_error('recvmsg(2)', self) if msg_size < 0

      if grow_msg and header.message_truncated?
        need_more = true
        msg_len *= 2
      end

      next if need_more

      # When a socket is actually connected the address structure is not used.
      if header.address_size > 0
        addr = Addrinfo.new(address.to_s, address.family, socket_type)
      else
        addr = Addrinfo.new([Socket::AF_UNSPEC], nil, socket_type)
      end

      return msg_buffer.read_string(msg_size), addr, header.flags
    ensure
      msg_buffer.free
      address.free
      io_vec.free
      header.free
    end
  end

  nil
end

#recvmsg_nonblock(max_msg_len = nil, flags = 0, *_) ⇒ Object



186
187
188
189
190
# File 'lib/socket/basic_socket.rb', line 186

def recvmsg_nonblock(max_msg_len = nil, flags = 0, *_)
  fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)

  recvmsg(max_msg_len, flags | Socket::MSG_DONTWAIT)
end

#remote_addressObject

Raises:

  • (NotImplementedError)


288
289
290
291
# File 'lib/socket/basic_socket.rb', line 288

def remote_address
  raise NotImplementedError,
    'This method must be implemented by classes inheriting from BasicSocket'
end

#send(message, flags, dest_sockaddr = nil) ⇒ Object



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
# File 'lib/socket/basic_socket.rb', line 97

def send(message, flags, dest_sockaddr = nil)
  bytes      = message.bytesize
  bytes_sent = 0

  if dest_sockaddr.is_a?(Addrinfo)
    dest_sockaddr = dest_sockaddr.to_sockaddr
  end

  RubySL::Socket::Foreign.char_pointer(bytes) do |buffer|
    buffer.write_string(message)

    if dest_sockaddr.is_a?(String)
      addr = RubySL::Socket.sockaddr_class_for_socket(self)
        .with_sockaddr(dest_sockaddr)

      begin
        bytes_sent = RubySL::Socket::Foreign
          .sendto(descriptor, buffer, bytes, flags, addr, addr.size)
      ensure
        addr.free
      end
    else
      bytes_sent = RubySL::Socket::Foreign
        .send(descriptor, buffer, bytes, flags)
    end
  end

  RubySL::Socket::Error.write_error('send(2)', self) if bytes_sent < 0

  bytes_sent
end

#sendmsg(message, flags = 0, dest_sockaddr = nil, *_) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/socket/basic_socket.rb', line 192

def sendmsg(message, flags = 0, dest_sockaddr = nil, *_)
  msg_buffer = RubySL::Socket::Foreign.char_pointer(message.bytesize)
  io_vec     = RubySL::Socket::Foreign::Iovec.with_buffer(msg_buffer)
  header     = RubySL::Socket::Foreign::Msghdr.new
  address    = nil

  begin
    msg_buffer.write_string(message)

    header.message = io_vec

    if dest_sockaddr.is_a?(Addrinfo)
      dest_sockaddr = dest_sockaddr.to_sockaddr
    end

    if dest_sockaddr.is_a?(String)
      address = RubySL::Socket::Foreign::SockaddrIn
        .with_sockaddr(dest_sockaddr)

      header.address = address
    end

    num_bytes = RubySL::Socket::Foreign
      .sendmsg(descriptor, header.pointer, flags)

    RubySL::Socket::Error.read_error('sendmsg(2)', self) if num_bytes < 0

    num_bytes
  ensure
    address.free if address
    header.free
    io_vec.free
    msg_buffer.free
  end
end

#sendmsg_nonblock(message, flags = 0, dest_sockaddr = nil, *_) ⇒ Object



228
229
230
231
232
# File 'lib/socket/basic_socket.rb', line 228

def sendmsg_nonblock(message, flags = 0, dest_sockaddr = nil, *_)
  fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)

  sendmsg(message, flags | Socket::MSG_DONTWAIT, dest_sockaddr)
end

#setsockopt(level_or_option, optname = nil, optval = nil) ⇒ Object



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
# File 'lib/socket/basic_socket.rb', line 38

def setsockopt(level_or_option, optname = nil, optval = nil)
  if level_or_option and optname and optval
    if level_or_option.is_a?(Socket::Option)
      raise TypeError,
        'expected the first argument to be a Fixnum, Symbol, or String'
    end

    level = level_or_option
  elsif level_or_option.is_a?(Socket::Option)
    raise(ArgumentError, 'given 2, expected 1') if optname

    level   = level_or_option.level
    optname = level_or_option.optname
    optval  = level_or_option.data
  else
    raise TypeError,
      'expected the first argument to be a Fixnum, Symbol, String, or Socket::Option'
  end

  optval = 1 if optval == true
  optval = 0 if optval == false

  sockname = RubySL::Socket::Foreign.getsockname(descriptor)
  family   = RubySL::Socket.family_for_sockaddr_in(sockname)
  level    = RubySL::Socket::SocketOptions.socket_level(level, family)
  optname  = RubySL::Socket::SocketOptions.socket_option(level, optname)
  error    = 0

  if optval.is_a?(Fixnum)
    RubySL::Socket::Foreign.memory_pointer(:socklen_t) do |pointer|
      pointer.write_int(optval)

      error = RubySL::Socket::Foreign
        .setsockopt(descriptor, level, optname, pointer, pointer.total)
    end
  elsif optval.is_a?(String)
    RubySL::Socket::Foreign.memory_pointer(optval.bytesize) do |pointer|
      pointer.write_string(optval)

      error = RubySL::Socket::Foreign
        .setsockopt(descriptor, level, optname, pointer, optval.bytesize)
    end
  else
    raise TypeError, 'socket option should be a Fixnum, String, true, or false'
  end

  Errno.handle('unable to set socket option') if error < 0

  0
end

#shutdown(how = Socket::SHUT_RDWR) ⇒ Object



270
271
272
273
274
275
276
277
# File 'lib/socket/basic_socket.rb', line 270

def shutdown(how = Socket::SHUT_RDWR)
  how = RubySL::Socket.shutdown_option(how)
  err = RubySL::Socket::Foreign.shutdown(descriptor, how)

  Errno.handle('shutdown(2)') unless err == 0

  0
end