Class: Socket

Inherits:
BasicSocket show all
Includes:
ListenAndAccept
Defined in:
lib/rubysl/socket/socket.rb

Defined Under Namespace

Modules: Constants, Foreign, ListenAndAccept Classes: Servent, SockAddr_In, SockAddr_Un

Constant Summary collapse

FFI =
Rubinius::FFI

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ListenAndAccept

#accept, #accept_nonblock, #accept_nonblock2, #listen

Methods inherited from BasicSocket

#close_read, #close_write, do_not_reverse_lookup, do_not_reverse_lookup=, for_fd, from_descriptor, #from_descriptor, #getpeername, #getsockname, #getsockopt, #recv, #recv_nonblock, #recvfrom, #send, #setsockopt, #shutdown

Constructor Details

#initialize(family, socket_type, protocol) ⇒ Socket

Returns a new instance of Socket.



733
734
735
736
737
738
739
740
741
# File 'lib/rubysl/socket/socket.rb', line 733

def initialize(family, socket_type, protocol)
  family = self.class.get_protocol_family family
  socket_type = self.class.get_socket_type socket_type
  descriptor  = Socket::Foreign.socket family, socket_type, protocol

  Errno.handle 'socket(2)' if descriptor < 0

  IO.setup self, descriptor, nil, true
end

Class Method Details

.get_protocol_family(family) ⇒ Object



781
782
783
784
785
786
787
788
789
790
791
792
# File 'lib/rubysl/socket/socket.rb', line 781

def self.get_protocol_family(family)
  case family
  when Fixnum
    return family
  when String
    # do nothing
  else
    family = StringValue(family)
  end

  return Socket::Constants.const_get family
end

.get_socket_type(type) ⇒ Object



794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
# File 'lib/rubysl/socket/socket.rb', line 794

def self.get_socket_type(type)
  if type.kind_of? String
    if type.prefix? "SOCK_"
      begin
        type = Socket::Constants.const_get(type)
      rescue NameError
        raise SocketError, "unknown socket type #{type}"
      end
    else
      raise SocketError, "unknown socket type #{type}"
    end
  elsif !type.kind_of? Integer
    raise Errno::EPROTONOSUPPORT, type.inspect
  end

  type
end

.getaddrinfo(host, service, family = 0, socktype = 0, protocol = 0, flags = 0) ⇒ Object



540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
# File 'lib/rubysl/socket/socket.rb', line 540

def self.getaddrinfo(host, service, family = 0, socktype = 0,
                     protocol = 0, flags = 0)
  if service
    if service.kind_of? Fixnum
      service = service.to_s
    else
      service = StringValue(service)
    end
  end

  addrinfos = Socket::Foreign.getaddrinfo(host, service, family, socktype,
                                          protocol, flags)

  addrinfos.map do |ai|
    addrinfo = []
    addrinfo << Socket::Constants::AF_TO_FAMILY[ai[1]]

    sockaddr = Foreign.unpack_sockaddr_in ai[4], !BasicSocket.do_not_reverse_lookup

    addrinfo << sockaddr.pop # port
    addrinfo.concat sockaddr # hosts
    addrinfo << ai[1]
    addrinfo << ai[2]
    addrinfo << ai[3]
    addrinfo
  end
end

.gethostbyname(hostname) ⇒ Object



604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
# File 'lib/rubysl/socket/socket.rb', line 604

def self.gethostbyname(hostname)
  addrinfos = Socket.getaddrinfo(hostname, nil)

  hostname     = addrinfos.first[2]
  family       = addrinfos.first[4]
  addresses    = []
  alternatives = []
  addrinfos.each do |a|
    alternatives << a[2] unless a[2] == hostname
    # transform addresses to packed strings
    if a[4] == family
      sockaddr = Socket.sockaddr_in(1, a[3])
      if family == AF_INET
        # IPv4 address
        offset = FFI.config("sockaddr_in.sin_addr.offset")
        size = FFI.config("sockaddr_in.sin_addr.size")
        addresses << sockaddr.byteslice(offset, size)
      elsif family == AF_INET6
        # Ipv6 address
        offset = FFI.config("sockaddr_in6.sin6_addr.offset")
        size = FFI.config("sockaddr_in6.sin6_addr.size")
        addresses << sockaddr.byteslice(offset, size)
      else
        addresses << a[3]
      end
    end
  end

  [hostname, alternatives.uniq, family] + addresses.uniq
end

.gethostnameObject



597
598
599
600
601
602
# File 'lib/rubysl/socket/socket.rb', line 597

def self.gethostname
  FFI::MemoryPointer.new :char, 1024 do |mp|  #magic number 1024 comes from MRI
    Socket::Foreign.gethostname(mp, 1024) # same here
    return mp.read_string
  end
end

.getnameinfo(sockaddr, flags = 0) ⇒ Object



568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
# File 'lib/rubysl/socket/socket.rb', line 568

def self.getnameinfo(sockaddr, flags = 0)
  port   = nil
  host   = nil
  family = Socket::AF_UNSPEC
  if sockaddr.is_a?(Array)
    if sockaddr.size == 3
      af = sockaddr[0]
      port = sockaddr[1]
      host = sockaddr[2]
    elsif sockaddr.size == 4
      af = sockaddr[0]
      port = sockaddr[1]
      host = sockaddr[3] || sockaddr[2]
    else
      raise ArgumentError, "array size should be 3 or 4, #{sockaddr.size} given"
    end

    if family == "AF_INET"
      family = Socket::AF_INET
    elsif family == "AF_INET6"
      family = Socket::AF_INET6
    end
    sockaddr = Socket::Foreign.pack_sockaddr_in(host, port, family, Socket::SOCK_DGRAM, 0)
  end

  family, port, host, ip = Socket::Foreign.getnameinfo(sockaddr, flags)
  [host, port]
end

.getservbyname(service, proto = 'tcp') ⇒ Object



651
652
653
654
655
656
657
658
659
660
661
662
663
664
# File 'lib/rubysl/socket/socket.rb', line 651

def self.getservbyname(service, proto='tcp')
  FFI::MemoryPointer.new :char, service.length + 1 do |svc|
    FFI::MemoryPointer.new :char, proto.length + 1 do |prot|
      svc.write_string(service + "\0")
      prot.write_string(proto + "\0")
      fn = Socket::Foreign.getservbyname(svc, prot)

      raise SocketError, "no such service #{service}/#{proto}" if fn.nil?

      s = Servent.new(fn.read_string(Servent.size))
      return Socket::Foreign.ntohs(s[:s_port])
    end
  end
end

.pack_sockaddr_in(port, host, type = Socket::SOCK_DGRAM, flags = 0) ⇒ Object Also known as: sockaddr_in



666
667
668
# File 'lib/rubysl/socket/socket.rb', line 666

def self.pack_sockaddr_in(port, host, type = Socket::SOCK_DGRAM, flags = 0)
  Socket::Foreign.pack_sockaddr_in host, port, Socket::AF_UNSPEC, type, flags
end

.pack_sockaddr_un(file) ⇒ Object Also known as: sockaddr_un



712
713
714
# File 'lib/rubysl/socket/socket.rb', line 712

def self.pack_sockaddr_un(file)
  SockAddr_Un.new(file).to_s
end

.socketpair(domain, type, protocol, klass = self) ⇒ Object Also known as: pair



682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
# File 'lib/rubysl/socket/socket.rb', line 682

def self.socketpair(domain, type, protocol, klass=self)
  if domain.kind_of? String
    if domain.prefix? "AF_" or domain.prefix? "PF_"
      begin
        domain = Socket::Constants.const_get(domain)
      rescue NameError
        raise SocketError, "unknown socket domain #{domani}"
      end
    else
      raise SocketError, "unknown socket domain #{domani}"
    end
  end

  type = get_socket_type(type)

  FFI::MemoryPointer.new :int, 2 do |mp|
    Socket::Foreign.socketpair(domain, type, protocol, mp)
    fd0, fd1 = mp.read_array_of_int(2)

    [ klass.from_descriptor(fd0), klass.from_descriptor(fd1) ]
  end
end

.unpack_sockaddr_in(sockaddr) ⇒ Object



670
671
672
673
674
675
676
677
678
679
680
# File 'lib/rubysl/socket/socket.rb', line 670

def self.unpack_sockaddr_in(sockaddr)
  host, address, port = Socket::Foreign.unpack_sockaddr_in sockaddr, false

  return [port, address]
rescue SocketError => e
  if e.message =~ /ai_family not supported/ then # HACK platform specific?
    raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr'
  else
    raise e
  end
end

.unpack_sockaddr_un(addr) ⇒ Object



716
717
718
719
720
721
722
723
724
725
726
# File 'lib/rubysl/socket/socket.rb', line 716

def self.unpack_sockaddr_un(addr)

  if addr.length > FFI.config("sockaddr_un.sizeof")
    raise TypeError, "too long sockaddr_un - #{addr.length} longer than #{FFI.config("sockaddr_un.sizeof")}"
  end

  struct = SockAddr_Un.new
  struct.pointer.write_string(addr)

  struct[:sun_path]
end

Instance Method Details

#bind(server_sockaddr) ⇒ Object



743
744
745
746
747
# File 'lib/rubysl/socket/socket.rb', line 743

def bind(server_sockaddr)
  err = Socket::Foreign.bind(descriptor, server_sockaddr)
  Errno.handle 'bind(2)' unless err == 0
  err
end

#connect(sockaddr, extra = nil) ⇒ Object



750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
# File 'lib/rubysl/socket/socket.rb', line 750

def connect(sockaddr, extra=nil)
  if extra
    sockaddr = Socket.pack_sockaddr_in sockaddr, extra
  else
    sockaddr = StringValue(sockaddr)
  end

  status = Socket::Foreign.connect descriptor, sockaddr

  if status < 0
    begin
      Errno.handle "connect(2)"
    rescue Errno::EISCONN
      return 0
    end
  end

  return 0
end

#connect_nonblock(sockaddr) ⇒ Object



770
771
772
773
774
775
776
777
778
779
# File 'lib/rubysl/socket/socket.rb', line 770

def connect_nonblock(sockaddr)
  fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)

  status = Socket::Foreign.connect descriptor, StringValue(sockaddr)
  if status < 0
    Errno.handle "connect(2)"
  end

  return status
end