Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Crystal::System::Addrinfo #14957

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/crystal/system/addrinfo.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Crystal::System::Addrinfo
# alias Handle

# protected def initialize(addrinfo : Handle)

# def system_ip_address : ::Socket::IPAddress

# only used by `#system_ip_address`?
# def to_unsafe
straight-shoota marked this conversation as resolved.
Show resolved Hide resolved

# def self.getaddrinfo(domain, service, family, type, protocol, timeout) : Handle

# def self.next_addrinfo(addrinfo : Handle) : Handle

# def self.free_addrinfo(addrinfo : Handle)
end

{% if flag?(:wasi) %}
require "./wasi/addrinfo"
{% elsif flag?(:unix) %}
require "./unix/addrinfo"
{% elsif flag?(:win32) %}
require "./win32/addrinfo"
{% else %}
{% raise "No Crystal::System::Addrinfo implementation available" %}
{% end %}
71 changes: 71 additions & 0 deletions src/crystal/system/unix/addrinfo.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
module Crystal::System::Addrinfo
alias Handle = LibC::Addrinfo*

@addr : LibC::SockaddrIn6

protected def initialize(addrinfo : Handle)
@family = ::Socket::Family.from_value(addrinfo.value.ai_family)
@type = ::Socket::Type.from_value(addrinfo.value.ai_socktype)
@protocol = ::Socket::Protocol.from_value(addrinfo.value.ai_protocol)
@size = addrinfo.value.ai_addrlen.to_i

@addr = uninitialized LibC::SockaddrIn6

case @family
when ::Socket::Family::INET6
addrinfo.value.ai_addr.as(LibC::SockaddrIn6*).copy_to(pointerof(@addr).as(LibC::SockaddrIn6*), 1)
when ::Socket::Family::INET
addrinfo.value.ai_addr.as(LibC::SockaddrIn*).copy_to(pointerof(@addr).as(LibC::SockaddrIn*), 1)
else
# TODO: (asterite) UNSPEC and UNIX unsupported?
end
end

def system_ip_address : ::Socket::IPAddress
::Socket::IPAddress.from(to_unsafe, size)
end

def to_unsafe
pointerof(@addr).as(LibC::Sockaddr*)
end

def self.getaddrinfo(domain, service, family, type, protocol, timeout) : Handle
hints = LibC::Addrinfo.new
hints.ai_family = (family || ::Socket::Family::UNSPEC).to_i32
hints.ai_socktype = type
hints.ai_protocol = protocol
hints.ai_flags = 0

if service.is_a?(Int)
hints.ai_flags |= LibC::AI_NUMERICSERV
end

# On OS X < 10.12, the libsystem implementation of getaddrinfo segfaults
# if AI_NUMERICSERV is set, and servname is NULL or 0.
{% if flag?(:darwin) %}
if service.in?(0, nil) && (hints.ai_flags & LibC::AI_NUMERICSERV)
hints.ai_flags |= LibC::AI_NUMERICSERV
service = "00"
end
{% end %}

ret = LibC.getaddrinfo(domain, service.to_s, pointerof(hints), out ptr)
unless ret.zero?
if ret == LibC::EAI_SYSTEM
raise ::Socket::Addrinfo::Error.from_os_error nil, Errno.value, domain: domain
end

error = Errno.new(ret)
raise ::Socket::Addrinfo::Error.from_os_error(nil, error, domain: domain, type: type, protocol: protocol, service: service)
end
ptr
end

def self.next_addrinfo(addrinfo : Handle) : Handle
addrinfo.value.ai_next
end

def self.free_addrinfo(addrinfo : Handle)
LibC.freeaddrinfo(addrinfo)
end
end
27 changes: 27 additions & 0 deletions src/crystal/system/wasi/addrinfo.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module Crystal::System::Addrinfo
alias Handle = NoReturn

protected def initialize(addrinfo : Handle)
raise NotImplementedError.new("Crystal::System::Addrinfo#initialize")
end

def system_ip_address : ::Socket::IPAddress
raise NotImplementedError.new("Crystal::System::Addrinfo#system_ip_address")
end

def to_unsafe
raise NotImplementedError.new("Crystal::System::Addrinfo#to_unsafe")
end

def self.getaddrinfo(domain, service, family, type, protocol, timeout) : Handle
raise NotImplementedError.new("Crystal::System::Addrinfo.getaddrinfo")
end

def self.next_addrinfo(addrinfo : Handle) : Handle
raise NotImplementedError.new("Crystal::System::Addrinfo.next_addrinfo")
end

def self.free_addrinfo(addrinfo : Handle)
raise NotImplementedError.new("Crystal::System::Addrinfo.free_addrinfo")
end
end
61 changes: 61 additions & 0 deletions src/crystal/system/win32/addrinfo.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module Crystal::System::Addrinfo
alias Handle = LibC::Addrinfo*

@addr : LibC::SockaddrIn6

protected def initialize(addrinfo : Handle)
@family = ::Socket::Family.from_value(addrinfo.value.ai_family)
@type = ::Socket::Type.from_value(addrinfo.value.ai_socktype)
@protocol = ::Socket::Protocol.from_value(addrinfo.value.ai_protocol)
@size = addrinfo.value.ai_addrlen.to_i

@addr = uninitialized LibC::SockaddrIn6

case @family
when ::Socket::Family::INET6
addrinfo.value.ai_addr.as(LibC::SockaddrIn6*).copy_to(pointerof(@addr).as(LibC::SockaddrIn6*), 1)
when ::Socket::Family::INET
addrinfo.value.ai_addr.as(LibC::SockaddrIn*).copy_to(pointerof(@addr).as(LibC::SockaddrIn*), 1)
else
# TODO: (asterite) UNSPEC and UNIX unsupported?
end
end

def system_ip_address : ::Socket::IPAddress
::Socket::IPAddress.from(to_unsafe, size)
end

def to_unsafe
pointerof(@addr).as(LibC::Sockaddr*)
end

def self.getaddrinfo(domain, service, family, type, protocol, timeout) : Handle
hints = LibC::Addrinfo.new
hints.ai_family = (family || ::Socket::Family::UNSPEC).to_i32
hints.ai_socktype = type
hints.ai_protocol = protocol
hints.ai_flags = 0

if service.is_a?(Int)
hints.ai_flags |= LibC::AI_NUMERICSERV
if service < 0
raise ::Socket::Addrinfo::Error.from_os_error(nil, WinError::WSATYPE_NOT_FOUND, domain: domain, type: type, protocol: protocol, service: service)
end
end

ret = LibC.getaddrinfo(domain, service.to_s, pointerof(hints), out ptr)
unless ret.zero?
error = WinError.new(ret.to_u32!)
raise ::Socket::Addrinfo::Error.from_os_error(nil, error, domain: domain, type: type, protocol: protocol, service: service)
end
ptr
end

def self.next_addrinfo(addrinfo : Handle) : Handle
addrinfo.value.ai_next
end

def self.free_addrinfo(addrinfo : Handle)
LibC.freeaddrinfo(addrinfo)
end
end
Loading
Loading