Index: ext/socket/udpsocket.c =================================================================== --- ext/socket/udpsocket.c (revision 36037) +++ ext/socket/udpsocket.c (working copy) @@ -112,6 +112,7 @@ udp_connect(VALUE sock, VALUE host, VALU static VALUE udp_bind(VALUE sock, VALUE host, VALUE port) { + VALUE mesg; rb_io_t *fptr; struct addrinfo *res0, *res; @@ -126,7 +127,13 @@ udp_bind(VALUE sock, VALUE host, VALUE p return INT2FIX(0); } freeaddrinfo(res0); - rb_sys_fail("bind(2)"); + + port = rb_String(port); + mesg = rb_sprintf("bind(2) for \"%s\" port %s", + StringValueCStr(host), + StringValueCStr(port)); + rb_sys_fail_str(mesg); + return INT2FIX(0); } Index: ext/socket/ipsocket.c =================================================================== --- ext/socket/ipsocket.c (revision 36037) +++ ext/socket/ipsocket.c (working copy) @@ -43,7 +43,7 @@ init_inetsock_internal(struct inetsock_a { int type = arg->type; struct addrinfo *res; - int fd, status = 0; + int fd, status = 0, local = 0; const char *syscall = 0; arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv, SOCK_STREAM, @@ -81,6 +81,7 @@ init_inetsock_internal(struct inetsock_a else { if (arg->local.res) { status = bind(fd, arg->local.res->ai_addr, arg->local.res->ai_addrlen); + local = status; syscall = "bind(2)"; } @@ -99,7 +100,24 @@ init_inetsock_internal(struct inetsock_a break; } if (status < 0) { - rb_sys_fail(syscall); + VALUE host, port, mesg; + const char * local_mesg; + + if (local < 0) { + host = arg->local.host; + port = rb_String(arg->local.serv); + local_mesg = " (local address)"; + } else { + host = arg->remote.host; + port = rb_String(arg->remote.serv); + local_mesg = ""; + } + + mesg = rb_sprintf("%s for \"%s\" port %s%s", + syscall, StringValuePtr(host), StringValuePtr(port), + local_mesg); + + rb_sys_fail_str(mesg); } arg->fd = -1; Index: ext/socket/socket.c =================================================================== --- ext/socket/socket.c (revision 36037) +++ ext/socket/socket.c (working copy) @@ -10,6 +10,8 @@ #include "rubysocket.h" +static VALUE sock_s_unpack_sockaddr_in(VALUE, VALUE); + static void setup_domain_and_type(VALUE domain, int *dv, VALUE type, int *tv) { @@ -467,8 +469,14 @@ sock_bind(VALUE sock, VALUE addr) SockAddrStringValue(addr); GetOpenFile(sock, fptr); - if (bind(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_LENINT(addr)) < 0) - rb_sys_fail("bind(2)"); + if (bind(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_LENINT(addr)) < 0) { + VALUE host_port = sock_s_unpack_sockaddr_in(sock, addr); + VALUE mesg = rb_sprintf("bind(2) for \"%s\" port %d", + StringValueCStr(RARRAY_PTR(host_port)[1]), + NUM2INT(RARRAY_PTR(host_port)[0])); + + rb_sys_fail_str(mesg); + } return INT2FIX(0); } Index: test/socket/test_tcp.rb =================================================================== --- test/socket/test_tcp.rb (revision 36037) +++ test/socket/test_tcp.rb (working copy) @@ -6,6 +6,23 @@ end class TestSocket_TCPSocket < Test::Unit::TestCase + def test_initialize_failure + s = TCPServer.new("localhost", nil) + server_port = s.addr[1] + + c = TCPSocket.new("localhost", server_port) + client_port = c.addr[1] + + begin + # TCPServer.new uses SO_REUSEADDR so we must create a failure on the + # local address. + TCPSocket.new("localhost", server_port, "localhost", client_port) + flunk "expected SystemCallError" + rescue SystemCallError => e + assert_match "for \"localhost\" port #{client_port}", e.message + end + end + def test_recvfrom svr = TCPServer.new("localhost", 0) th = Thread.new { Index: test/socket/test_socket.rb =================================================================== --- test/socket/test_socket.rb (revision 36037) +++ test/socket/test_socket.rb (working copy) @@ -70,6 +70,22 @@ class TestSocket < Test::Unit::TestCase } end + def test_bind + Socket.open(Socket::AF_INET, Socket::SOCK_STREAM, 0) {|bound| + bound.bind(Socket.sockaddr_in(0, "127.0.0.1")) + addr = bound.getsockname + port, = Socket.unpack_sockaddr_in(addr) + + Socket.open(Socket::AF_INET, Socket::SOCK_STREAM, 0) {|s| + e = assert_raises(Errno::EADDRINUSE) do + s.bind(Socket.sockaddr_in(port, "127.0.0.1")) + end + + assert_match "bind(2) for \"127.0.0.1\" port #{port}", e.message + } + } + end + def test_getaddrinfo # This should not send a DNS query because AF_UNIX. assert_raise(SocketError) { Socket.getaddrinfo("www.kame.net", 80, "AF_UNIX") } Index: test/socket/test_udp.rb =================================================================== --- test/socket/test_udp.rb (revision 36037) +++ test/socket/test_udp.rb (working copy) @@ -36,4 +36,20 @@ class TestSocket_UDPSocket < Test::Unit: s.bind(host, 2000) } end + + def test_bind_addrinuse + host = "127.0.0.1" + port = 2001 + + in_use = UDPSocket.new + in_use.bind(host, port) + + s = UDPSocket.new + + e = assert_raises(Errno::EADDRINUSE) do + s.bind(host, port) + end + + assert_match "bind(2) for \"#{host}\" port #{port}", e.message + end end if defined?(UDPSocket)