[#37674] [Bug:trunk] Socket.getaddrinfo("www.ruby-lang.org", 80) returns only UDP information — Tanaka Akira <akr@...>
1.9 と 1.8 で Socket.getaddrinfo("www.ruby-lang.org", 80) の返り値が違います。
4 messages
2009/01/01
[#37677] [BUG:trunk] Re: [ruby-cvs:28403] Ruby:r21185 (trunk): * thread.c (rb_thread_blocking_region): add a comment. — "U.Nakamura" <usa@...>
あけましておめでとうございます、なかむら(う)です。
4 messages
2009/01/02
[#37679] [FEATURE:trunk] EncDet again — "Yugui (Yuki Sonoda)" <yugui@...>
Yuguiです。
23 messages
2009/01/03
[#39781] [Feature #973] EncDet again
— Yui NARUSE <redmine@...>
2009/11/25
チケット #973 が更新されました。 (by Yui NARUSE)
[#39782] Re: [Feature #973] EncDet again
— Kouhei Sutou <kou@...>
2009/11/25
須藤です。
[#37680] Re: [ruby-cvs:28500] Ruby:r21282 (trunk): * enc/trans/euc-cn.c: renemed from gb2312.c because GB2312 is — Martin Duerst <duerst@...>
At 15:29 09/01/03, you wrote:
3 messages
2009/01/03
[#37681] Re: [ruby-cvs:28501] Ruby:r21283 (trunk): * enc/trans/chinese.trans: added for transcoding EUC-CN and GB12345. — Martin Duerst <duerst@...>
At 15:31 09/01/03, you wrote:
5 messages
2009/01/03
[#37684] Re: [ruby-cvs:28501] Ruby:r21283 (trunk): * enc/trans/chinese.trans: added for transcoding EUC-CN and GB12345.
— "NARUSE, Yui" <naruse@...>
2009/01/03
成瀬です。
[#37694] Re: [ruby-cvs:28501] Ruby:r21283 (trunk): *enc/trans/chinese.trans: added for transcoding EUC-CN and GB12345.
— Martin Duerst <duerst@...>
2009/01/04
At 23:18 09/01/03, NARUSE, Yui wrote:
[#37686] IRB doesn't prompt for MSWin32 — arton <artonx@...>
artonです。
11 messages
2009/01/03
[#37687] Re: IRB doesn't prompt for MSWin32
— "Ayumu Aizawa" <ayumu.aizawa@...>
2009/01/03
ruby-devにははじめてメールします、あいざわです。
[#37688] [Bug #974] Range#max で終了しないことがある — tadayoshi funaba <redmine@...>
Bug #974: Range#max で終了しないことがある
6 messages
2009/01/03
[#37689] Re: [Bug #974] Range#max で終了しないことがある
— Yukihiro Matsumoto <matz@...>
2009/01/03
まつもと ゆきひろです
[#37692] AddrInfo — Tanaka Akira <akr@...>
提案なのですが、ext/socket で AddrInfo クラスを新設するのはどうでしょうか。
8 messages
2009/01/04
[#37737] Encoding.locale_charmap — Hidetoshi NAGAI <nagai@...>
永井@知能.九工大です.
13 messages
2009/01/08
[#37738] Re: Encoding.locale_charmap
— Yukihiro Matsumoto <matz@...>
2009/01/08
まつもと ゆきひろです
[#37748] $LOAD_PATHとバージョンの運用の関係 — akira yamada / やまだあきら <akira@...>
1.9系でのバージョンの運用と$LOAD_PATHの値について質問です。
12 messages
2009/01/09
[#37758] Re: $LOAD_PATHとバージョンの運用の関係
— "NARUSE, Yui" <naruse@...>
2009/01/11
成瀬です。
[#37889] Re: $LOAD_PATHとバージョンの運用の関係
— Takao Kouji <kouji@...7.net>
2009/02/03
高尾宏治です。
[#37890] Re: $LOAD_PATHとバージョンの運用の関係
— Tanaka Akira <akr@...>
2009/02/04
In article <1A717293-1636-4FD3-87FE-388EF5FFF560@takao7.net>,
[#37897] Re: $LOAD_PATHとバージョンの運用の関係
— "U.Nakamura" <usa@...>
2009/02/04
こんにちは、なかむら(う)です。
[#37749] $LOAD_PATHのconfigureによる指定 — akira yamada / やまだあきら <akira@...>
Ruby 1.9.1RC1では$LOAD_PATHの中の
6 messages
2009/01/09
[#37765] AddrInfo.list_ipaddr — Tanaka Akira <akr@...>
提案なんですが、ホストが持っている IP アドレスのリストを返す
4 messages
2009/01/13
[#37783] [Feature:1.9] backlog option for TCPServer and UNIXServer — Nobuyoshi Nakada <nobu@...>
なかだです。
5 messages
2009/01/18
[#37785] Re: [Feature:1.9] backlog option for TCPServer and UNIXServer
— Tanaka Akira <akr@...>
2009/01/19
In article <20090118200251.12428E0D32@mail.bc9.jp>,
[#37823] [Feature #1038] Signal.trapのcommandにSymbol — Kazuhiro NISHIYAMA <redmine@...>
Feature #1038: Signal.trapのcommandにSymbol
4 messages
2009/01/22
[ruby-dev:37765] AddrInfo.list_ipaddr
From:
Tanaka Akira <akr@...>
Date:
2009-01-13 07:37:59 UTC
List:
ruby-dev #37765
提案なんですが、ホストが持っている IP アドレスのリストを返す
メソッドを用意するのはどうでしょうか。
% ./ruby -rsocket -rpp -e 'pp AddrInfo.list_ipaddr'
[#<AddrInfo: 127.0.0.1>,
#<AddrInfo: 150.82.175.199>,
#<AddrInfo: ::1>,
#<AddrInfo: fe80::20b:97ff:fe2b:8539%eth0>]
これがなぜ必要かというと、まず、IPv6 が利用できないホストを
判断したいからです。上記のリストでは ::1 と
fe80::20b:97ff:fe2b:8539%eth0 というふたつの IPv6 アドレスが
ありますが、これらは loopback address と link local address
というもので、グローバルには使えないものです。この判断を行う
ことにより、resolv.rb で使えもしない IPv6 アドレスを問い合わ
せたり返したりすることを防ぐことができます。
また、IP アドレスのリストは UDP でサーバを作る場合にも重要で
す。UDP のサーバは到着したパケットの宛先から返事を送ることが
望まれます。[RFC 1123] まぁ、送ったところ以外から返すと、ク
ライアントは関係ないところから (あるいは危ないひとから) 来た
パケットだと思って無視するかもしれないわけです。
外につながっている IP アドレスがひとつであれば問題にはならな
いんですが、複数の IP アドレスを持つマルチホームな環境で問題
になります。
では、パケットを受け取るときに宛先を得れば、と思うわけですが、
残念なことに recvfrom では宛先は得られません。recvfrom で得
られるアドレスはパケットの送信元であって宛先ではないからです。
また、同様に sendto でもサーバ側での送信元を指定することはで
きません。
これを解決するひとつの方法は、ソケットをあらかじめ各アドレス
ごとにつくって bind しておくというものですが、そこで IP アド
レスのリストが必要になります。
それ以外の方法としては、IPv6 では IPV6_RECVPKTINFO とか
setsockopt すると recvmsg/sendmsg で可能なようです。[RFC 3542]
が、IPv4 では標準化されたあるいは広く使えるものはないようで
す。(とくに送信元を指定する方法が)
この要求は過去にも何回があがっています。
[ruby-talk:162689], [ruby-talk:260514], [ruby-talk:324043]
たいてい、Socket.gethostname の結果を逆引きする、という話に
なるんですが、これは /etc/hosts や DNS に依存するなど、確実
に使えるというものではありません。やはりネットワークインター
フェースの情報が欲しければ、 インターフェース自身に尋ねるべ
きだと思います。
実際に尋ねる方法は、残念なことに IP アドレスのリストを得る方
法は標準化されていないようなので、以下を実装してあります。
* getifaddrs:
BSD/OS, FreeBSD, NetBSD, OpenBSD, DragonFly BSD, MirOS BSD,
GNU/Linux, MacOS X, AIX
* SIOCGLIFCONF: Solaris
* SIOCGIFCONF: 4.3BSD
どうやら、getifaddrs は普及が進んでいるようです。
4.3BSD の SIOCGIFCONF を使うものを実装してあるので、そこに由
来するものはだいたい動くんじゃないかと思います。ただ、その場
合は IPv4 限定になりますが。
(なお、4.4BSD の SIOCGIFCONF は拡張されて IPv6 も扱えて、い
ちおうその場合も動くようにしてありますが、4.4BSD で
getifaddrs がない環境は残っていないように思えます。)
なお、ここで抜けているものとして HP-UX がありますが、これに
は SIOCGLIFCONF があるようです。ただし Solaris とは構造体が
違うようで、テスト環境がないので実装していません。実機があれ
ばそんなに難しくないと思います。
というわけでどうでしょうか。
% svn diff --diff-cmd diff -x '-u -p'
Index: ext/socket/socket.c
===================================================================
--- ext/socket/socket.c (revision 21486)
+++ ext/socket/socket.c (working copy)
@@ -67,6 +67,20 @@
#include <fcntl.h>
#endif
#endif
+
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
#ifndef EWOULDBLOCK
#define EWOULDBLOCK EAGAIN
#endif
@@ -5403,6 +5417,235 @@ addrinfo_s_unix(VALUE self, VALUE path)
#endif
+#if defined(HAVE_GETIFADDRS) || defined(SIOCGLIFCONF) || defined(SIOCGIFCONF)
+static VALUE
+sockaddr_obj(struct sockaddr *addr)
+{
+ socklen_t len;
+
+ if (addr == NULL)
+ return Qnil;
+
+ switch (addr->sa_family) {
+#ifdef HAVE_SYS_UN_H
+ case AF_UNIX:
+ len = sizeof(struct sockaddr_un);
+ break;
+#endif
+
+ case AF_INET:
+ len = sizeof(struct sockaddr_in);
+ break;
+
+#ifdef AF_INET6
+ case AF_INET6:
+ len = sizeof(struct sockaddr_in6);
+ break;
+#endif
+
+ default:
+ len = sizeof(struct sockaddr_in);
+ break;
+ }
+#ifdef SA_LEN
+ if (len < SA_LEN(addr))
+ len = SA_LEN(addr);
+#endif
+
+ return addrinfo_new(addr, len, 0, 0, 0, Qnil, Qnil);
+}
+#endif
+
+/*
+ * call-seq:
+ * AddrInfo.list_ipaddr => array
+ *
+ * Returns local IP addresses as an array.
+ *
+ * The array contains AddrInfo objects.
+ *
+ * pp AddrInfo.list_ipaddr
+ * #=> [#<AddrInfo: 127.0.0.1>,
+ * #<AddrInfo: 192.168.0.128>,
+ * #<AddrInfo: ::1>,
+ * ...]
+ *
+ */
+static VALUE
+addrinfo_s_list_ipaddr(VALUE self)
+{
+#if defined(HAVE_GETIFADDRS)
+ struct ifaddrs *ifp = NULL;
+ struct ifaddrs *p;
+ int ret;
+ VALUE list;
+
+ ret = getifaddrs(&ifp);
+ if (ret == -1) {
+ rb_sys_fail("getifaddrs");
+ }
+
+ list = rb_ary_new();
+ for (p = ifp; p; p = p->ifa_next) {
+ if (IS_IP_FAMILY(p->ifa_addr->sa_family)) {
+ rb_ary_push(list, sockaddr_obj(p->ifa_addr));
+ }
+ }
+
+ freeifaddrs(ifp);
+
+ return list;
+#elif defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM)
+ /* Solaris if_tcp(7P) */
+ int fd = -1;
+ int ret;
+ struct lifnum ln;
+ struct lifconf lc;
+ char *reason = NULL;
+ int save_errno;
+ int i;
+ VALUE list;
+
+ lc.lifc_buf = NULL;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd == -1)
+ rb_sys_fail("socket");
+
+ memset(&ln, 0, sizeof(ln));
+ ln.lifn_family = AF_UNSPEC;
+
+ ret = ioctl(fd, SIOCGLIFNUM, &ln);
+ if (ret == -1) {
+ reason = "SIOCGLIFNUM";
+ goto finish;
+ }
+
+ memset(&lc, 0, sizeof(lc));
+ lc.lifc_family = AF_UNSPEC;
+ lc.lifc_flags = 0;
+ lc.lifc_len = sizeof(struct lifreq) * ln.lifn_count;
+ lc.lifc_req = xmalloc(lc.lifc_len);
+
+ ret = ioctl(fd, SIOCGLIFCONF, &lc);
+ if (ret == -1) {
+ reason = "SIOCGLIFCONF";
+ goto finish;
+ }
+
+ close(fd);
+ fd = -1;
+
+ list = rb_ary_new();
+ for (i = 0; i < ln.lifn_count; i++) {
+ struct lifreq *req = &lc.lifc_req[i];
+ if (IS_IP_FAMILY(req->lifr_addr.ss_family)) {
+ rb_ary_push(list, sockaddr_obj((struct sockaddr *)&req->lifr_addr));
+ }
+ }
+
+ finish:
+ save_errno = errno;
+ if (lc.lifc_buf != NULL)
+ xfree(lc.lifc_req);
+ if (fd != -1)
+ close(fd);
+ errno = save_errno;
+
+ if (reason)
+ rb_sys_fail(reason);
+ return list;
+
+#elif defined(SIOCGIFCONF)
+ int fd = -1;
+ int ret;
+#define EXTRA_SPACE (sizeof(struct ifconf) + sizeof(struct sockaddr_storage))
+ char initbuf[4096+EXTRA_SPACE];
+ char *buf = initbuf;
+ int bufsize;
+ struct ifconf conf;
+ struct ifreq *req;
+ VALUE list = Qnil;
+ char *reason = NULL;
+ int save_errno;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd == -1)
+ rb_sys_fail("socket");
+
+ bufsize = sizeof(initbuf);
+ buf = initbuf;
+
+ retry:
+ conf.ifc_len = bufsize;
+ conf.ifc_req = (struct ifreq *)buf;
+
+ /* fprintf(stderr, "bufsize: %d\n", bufsize); */
+
+ ret = ioctl(fd, SIOCGIFCONF, &conf);
+ if (ret == -1) {
+ reason = "SIOCGIFCONF";
+ goto finish;
+ }
+
+ /* fprintf(stderr, "conf.ifc_len: %d\n", conf.ifc_len); */
+
+ if (bufsize - EXTRA_SPACE < conf.ifc_len) {
+ if (bufsize < conf.ifc_len) {
+ /* NetBSD returns required size for all interfaces. */
+ bufsize = conf.ifc_len + EXTRA_SPACE;
+ }
+ else {
+ bufsize = bufsize << 1;
+ }
+ if (buf == initbuf)
+ buf = NULL;
+ buf = xrealloc(buf, bufsize);
+ goto retry;
+ }
+
+ close(fd);
+ fd = -1;
+
+ list = rb_ary_new();
+ req = conf.ifc_req;
+ while ((char*)req < (char*)conf.ifc_req + conf.ifc_len) {
+ struct sockaddr *addr = &req->ifr_addr;
+ if (IS_IP_FAMILY(addr->sa_family)) {
+ rb_ary_push(list, sockaddr_obj(addr));
+ }
+#ifdef HAVE_SA_LEN
+# ifndef _SIZEOF_ADDR_IFREQ
+# define _SIZEOF_ADDR_IFREQ(r) (sizeof(struct ifreq) + \
+ (sizeof(struct sockaddr) < (r).ifr_addr.sa_len ? \
+ (r).ifr_addr.sa_len - sizeof(struct sockaddr) : \
+ 0))
+# endif
+ req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
+#else
+ req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
+#endif
+ }
+
+ finish:
+
+ save_errno = errno;
+ if (buf != initbuf)
+ xfree(buf);
+ if (fd != -1)
+ close(fd);
+ errno = save_errno;
+
+ if (reason)
+ rb_sys_fail(reason);
+ return list;
+
+#undef EXTRA_SPACE
+#else
+ rb_notimplement();
+#endif
+}
+
static VALUE
sockaddr_string_value(volatile VALUE *v)
{
@@ -5605,6 +5848,8 @@ Init_socket()
rb_define_method(rb_cAddrInfo, "getnameinfo", addrinfo_getnameinfo, -1);
+ rb_define_singleton_method(rb_cAddrInfo, "list_ipaddr", addrinfo_s_list_ipaddr, 0);
+
/* constants */
mConst = rb_define_module_under(rb_cSocket, "Constants");
init_constants(mConst);
Index: ext/socket/extconf.rb
===================================================================
--- ext/socket/extconf.rb (revision 21486)
+++ ext/socket/extconf.rb (working copy)
@@ -258,6 +258,12 @@ unless getaddr_info_ok and have_func("ge
have_header("resolv.h")
end
+have_header("ifaddrs.h")
+have_func("getifaddrs")
+have_header("sys/ioctl.h")
+have_header("sys/sockio.h")
+have_header("net/if.h")
+
unless have_type("socklen_t", headers)
$defs << "-Dsocklen_t=int"
end
Index: test/socket/test_addrinfo.rb
===================================================================
--- test/socket/test_addrinfo.rb (revision 21486)
+++ test/socket/test_addrinfo.rb (working copy)
@@ -39,6 +39,17 @@ class TestSocketAddrInfo < Test::Unit::T
assert_equal(0, ai.protocol)
end
+ def test_list_ipaddr
+ begin
+ list = AddrInfo.list_ipaddr
+ rescue NotImplementedError
+ return
+ end
+ list.each {|ai|
+ assert_instance_of(AddrInfo, ai)
+ }
+ end
+
def test_addrinfo_predicates
ipv4_ai = AddrInfo.new(Socket.sockaddr_in(80, "192.168.0.1"))
assert(ipv4_ai.ip?)
--
[田中 哲][たなか あきら][Tanaka Akira]