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



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

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



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

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

.for_fd(fixnum) ⇒ Object



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

def self.for_fd(fixnum)
  sock = allocate

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

  sock
end

Instance Method Details

#close_readObject



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

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



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

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



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

def do_not_reverse_lookup
  @no_reverse_lookup
end

#do_not_reverse_lookup=(setting) ⇒ Object



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

def do_not_reverse_lookup=(setting)
  @no_reverse_lookup = setting
end

#getpeereidObject



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

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

#getpeernameObject



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

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

#getsocknameObject



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

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

#getsockopt(level, optname) ⇒ Object



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

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)


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

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

#recv(bytes_to_read, flags = 0) ⇒ Object



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

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

#recv_nonblock(bytes_to_read, flags = 0) ⇒ Object



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

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



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

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



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

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)


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

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

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



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

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



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

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



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

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



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

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



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

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