Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 21 additions & 0 deletions spec/std/socket/address_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,27 @@ describe Socket::IPAddress do
end
end
end

it "#loopback?" do
Socket::IPAddress.new("127.0.0.1", 0).loopback?.should be_true
Socket::IPAddress.new("127.255.255.254", 0).loopback?.should be_true
Socket::IPAddress.new("128.0.0.1", 0).loopback?.should be_false
Socket::IPAddress.new("0.0.0.0", 0).loopback?.should be_false
Socket::IPAddress.new("::1", 0).loopback?.should be_true
Socket::IPAddress.new("0000:0000:0000:0000:0000:0000:0000:0001", 0).loopback?.should be_true
Socket::IPAddress.new("::2", 0).loopback?.should be_false
Socket::IPAddress.new(Socket::IPAddress::LOOPBACK, 0).loopback?.should be_true
Socket::IPAddress.new(Socket::IPAddress::LOOPBACK6, 0).loopback?.should be_true
end

it "#unspecified?" do
Socket::IPAddress.new("0.0.0.0", 0).unspecified?.should be_true
Socket::IPAddress.new("127.0.0.1", 0).unspecified?.should be_false
Socket::IPAddress.new("::", 0).unspecified?.should be_true
Socket::IPAddress.new("0000:0000:0000:0000:0000:0000:0000:0000", 0).unspecified?.should be_true
Socket::IPAddress.new(Socket::IPAddress::UNSPECIFIED, 0).unspecified?.should be_true
Socket::IPAddress.new(Socket::IPAddress::UNSPECIFIED6, 0).unspecified?.should be_true
end
end

describe Socket::UNIXAddress do
Expand Down
11 changes: 3 additions & 8 deletions src/compiler/crystal/tools/playground/server.cr
Original file line number Diff line number Diff line change
Expand Up @@ -528,17 +528,12 @@ module Crystal::Playground

server = HTTP::Server.new handlers

host = @host
if host
address = server.bind_tcp host, @port
else
address = server.bind_tcp @port
end
address = server.bind_tcp @host || Socket::IPAddress::LOOPBACK, @port
@port = address.port

puts "Listening on http://#{address}"
if host == "0.0.0.0"
puts "WARNING running playground with 0.0.0.0 is unsecure."
if address.unspecified?
Copy link
Copy Markdown
Member

@RX14 RX14 Oct 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a later PR to make this have better behaviour: test for not-loopback not 0.0.0.0 specifically

puts "WARNING running playground on #{address.address} is insecure."
end

begin
Expand Down
4 changes: 2 additions & 2 deletions src/http/server.cr
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class HTTP::Server
# If *reuse_port* is `true`, it enables the `SO_REUSEPORT` socket option,
# which allows multiple processes to bind to the same port.
def bind_tcp(port : Int32, reuse_port : Bool = false) : Socket::IPAddress
bind_tcp "127.0.0.1", port, reuse_port
bind_tcp Socket::IPAddress::LOOPBACK, port, reuse_port
end

# Creates a `TCPServer` listenting on *address* and adds it as a socket, returning the local address
Expand All @@ -180,7 +180,7 @@ class HTTP::Server
# server = HTTP::Server.new { }
# server.bind_unused_port # => Socket::IPAddress.new("127.0.0.1", 12345)
# ```
def bind_unused_port(host : String = "127.0.0.1", reuse_port : Bool = false) : Socket::IPAddress
def bind_unused_port(host : String = Socket::IPAddress::LOOPBACK, reuse_port : Bool = false) : Socket::IPAddress
bind_tcp host, 0, reuse_port
end

Expand Down
44 changes: 44 additions & 0 deletions src/socket/address.cr
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ class Socket
# resolve an IP, or don't know whether a `String` constains an IP or a domain
# name, you should use `Addrinfo.resolve` instead.
struct IPAddress < Address
UNSPECIFIED = "0.0.0.0"
UNSPECIFIED6 = "::"
LOOPBACK = "127.0.0.1"
LOOPBACK6 = "::1"
BROADCAST = "255.255.255.255"
BROADCAST6 = "ff0X::1"

getter port : Int32

@address : String?
Expand Down Expand Up @@ -190,6 +197,43 @@ class Socket
end
end

# Returns `true` if this IP is a loopback address.
#
# In the IPv4 family, loopback addresses are all addresses in the subnet
# `127.0.0.0/24`. In IPv6 `::1` is the loopback address.
def loopback? : Bool
if addr = @addr4
addr.s_addr & 0x00000000ff_u32 == 0x0000007f_u32
elsif addr = @addr6
ipv6_addr8(addr) == StaticArray[0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 1_u8]
else
raise "unreachable!"
end
end

# Returns `true` if this IP is an unspecified address, either the IPv4 address `0.0.0.0` or the IPv6 address `::`.
def unspecified? : Bool
if addr = @addr4
addr.s_addr == 0_u32
elsif addr = @addr6
ipv6_addr8(addr) == StaticArray[0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8]
else
raise "unreachable!"
end
end

private def ipv6_addr8(addr : LibC::In6Addr)
{% if flag?(:darwin) || flag?(:openbsd) || flag?(:freebsd) %}
addr.__u6_addr.__u6_addr8
{% elsif flag?(:linux) && flag?(:musl) %}
addr.__in6_union.__s6_addr
{% elsif flag?(:linux) %}
addr.__in6_u.__u6_addr8
{% else %}
{% raise "Unsupported platform" %}
{% end %}
end

def ==(other : IPAddress)
family == other.family &&
port == other.port &&
Expand Down