Project

General

Profile

Feature #15553 ยป patch.diff

Glass_saga (Masaki Matsushita), 01/21/2019 03:58 AM

View differences:

ext/socket/extconf.rb
444 444
  test_func = "socket(0,0,0)"
445 445
  have_library("nsl", 't_open("", 0, (struct t_info *)NULL)', headers) # SunOS
446 446
  have_library("socket", "socket(0,0,0)", headers) # SunOS
447
  have_library("anl", 'getaddrinfo_a', headers)
447 448
end
448 449

  
449 450
if have_func(test_func, headers)
......
505 506
  unless have_func("gethostname((char *)0, 0)", headers)
506 507
    have_func("uname((struct utsname *)NULL)", headers)
507 508
  end
509
  have_func("getaddrinfo_a", headers)
508 510

  
509 511
  ipv6 = false
510 512
  default_ipv6 = /haiku/ !~ RUBY_PLATFORM
ext/socket/lib/socket.rb
215 215
    end
216 216
  end
217 217

  
218
  def self.getaddrinfo(nodename, servname, family=nil, socktype=nil, protocol=nil, flags=nil, timeout: nil)
219
    if timeout
220
      if defined?(_getaddrinfo_a)
221
        _getaddrinfo_a(nodename, servname, family, socktype, protocol, flags, timeout: timeout)
222
      else
223
        require "timeout"
224

  
225
        Timeout.timeout(timeout, SocketError) do
226
          _getaddrinfo(nodename, servname, family, socktype, protocol, flags)
227
        end
228
      end
229
    else
230
      _getaddrinfo(nodename, servname, family, socktype, protocol, flags)
231
    end
232
  end
233

  
218 234
  # iterates over the list of Addrinfo objects obtained by Addrinfo.getaddrinfo.
219 235
  #
220 236
  #   Addrinfo.foreach(nil, 80) {|x| p x }
......
223 239
  #   #   #<Addrinfo: [::1]:80 TCP (:80)>
224 240
  #   #   #<Addrinfo: [::1]:80 UDP (:80)>
225 241
  #
226
  def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, &block)
227
    Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags).each(&block)
242
  def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, timeout: nil, &block)
243
    Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags, timeout: timeout).each(&block)
228 244
  end
229 245
end
230 246

  
......
606 622
  # _opts_ may have following options:
607 623
  #
608 624
  # [:connect_timeout] specify the timeout in seconds.
625
  # [:resolv_timeout] specify the name resolution timeout in seconds.
609 626
  #
610 627
  # If a block is given, the block is called with the socket.
611 628
  # The value of the block is returned.
......
619 636
  #     puts sock.read
620 637
  #   }
621 638
  #
622
  def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil) # :yield: socket
639
  def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil) # :yield: socket
623 640
    last_error = nil
624 641
    ret = nil
625 642

  
......
628 645
      local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
629 646
    end
630 647

  
631
    Addrinfo.foreach(host, port, nil, :STREAM) {|ai|
648
    Addrinfo.foreach(host, port, nil, :STREAM, timeout: resolv_timeout) {|ai|
632 649
      if local_addr_list
633 650
        local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily }
634 651
        next unless local_addr
ext/socket/raddrinfo.c
9 9
************************************************/
10 10

  
11 11
#include "rubysocket.h"
12
#include "hrtime.h"
12 13

  
13 14
#if defined(INET6) && (defined(LOOKUP_ORDER_HACK_INET) || defined(LOOKUP_ORDER_HACK_INET6))
14 15
#define LOOKUP_ORDERS (sizeof(lookup_order_table) / sizeof(lookup_order_table[0]))
......
199 200
}
200 201
#endif
201 202

  
203
#ifdef HAVE_GETADDRINFO_A
204
struct gai_suspend_arg
205
{
206
    struct gaicb *req;
207
    struct timespec *timeout;
208
};
209

  
210
static void *
211
nogvl_gai_suspend(void *arg)
212
{
213
    int ret;
214
    struct gai_suspend_arg *ptr = arg;
215
    struct gaicb const *wait_reqs[1];
216

  
217
    wait_reqs[0] = ptr->req;
218
    ret = gai_suspend(wait_reqs, 1, ptr->timeout);
219

  
220
    return (void *)(VALUE)ret;
221
}
222
#endif
223

  
202 224
static int
203 225
numeric_getaddrinfo(const char *node, const char *service,
204 226
        const struct addrinfo *hints,
......
318 340
    return ret;
319 341
}
320 342

  
343
#ifdef HAVE_GETADDRINFO_A
344
int
345
rb_getaddrinfo_a(const char *node, const char *service,
346
               const struct addrinfo *hints,
347
               struct rb_addrinfo **res, struct timespec *timeout)
348
{
349
    struct addrinfo *ai;
350
    int ret;
351
    int allocated_by_malloc = 0;
352

  
353
    ret = numeric_getaddrinfo(node, service, hints, &ai);
354
    if (ret == 0)
355
        allocated_by_malloc = 1;
356
    else {
357
	struct gai_suspend_arg arg;
358
	struct gaicb *reqs[1];
359
	struct gaicb req;
360

  
361
	req.ar_name = node;
362
	req.ar_service = service;
363
	req.ar_request = hints;
364

  
365
	reqs[0] = &req;
366
	ret = getaddrinfo_a(GAI_NOWAIT, reqs, 1, NULL);
367
	if (ret) return ret;
368

  
369
	arg.req = &req;
370
	arg.timeout = timeout;
371

  
372
	ret = (int)(VALUE)rb_thread_call_without_gvl(nogvl_gai_suspend, &arg, RUBY_UBF_IO, 0);
373

  
374
	if (ret) {
375
	    /* on Ubuntu 18.04 (or other systems), gai_suspend(3) returns EAI_SYSTEM/ENOENT on timeout */
376
	    if (ret == EAI_SYSTEM && errno == ENOENT) {
377
		return EAI_AGAIN;
378
	    } else {
379
		return ret;
380
	    }
381
	}
382

  
383
	ret = gai_error(reqs[0]);
384
	ai = reqs[0]->ar_result;
385
    }
386

  
387
    if (ret == 0) {
388
        *res = (struct rb_addrinfo *)xmalloc(sizeof(struct rb_addrinfo));
389
        (*res)->allocated_by_malloc = allocated_by_malloc;
390
        (*res)->ai = ai;
391
    }
392
    return ret;
393
}
394
#endif
395

  
321 396
void
322 397
rb_freeaddrinfo(struct rb_addrinfo *ai)
323 398
{
......
530 605
    return res;
531 606
}
532 607

  
608
#ifdef HAVE_GETADDRINFO_A
609
static struct rb_addrinfo*
610
rsock_getaddrinfo_a(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack, VALUE timeout)
611
{
612
    struct rb_addrinfo* res = NULL;
613
    char *hostp, *portp;
614
    int error;
615
    char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
616
    int additional_flags = 0;
617

  
618
    hostp = host_str(host, hbuf, sizeof(hbuf), &additional_flags);
619
    portp = port_str(port, pbuf, sizeof(pbuf), &additional_flags);
620

  
621
    if (socktype_hack && hints->ai_socktype == 0 && str_is_number(portp)) {
622
       hints->ai_socktype = SOCK_DGRAM;
623
    }
624
    hints->ai_flags |= additional_flags;
625

  
626
    if (NIL_P(timeout)) {
627
	error = rb_getaddrinfo(hostp, portp, hints, &res);
628
    } else {
629
	struct timespec _timeout = rb_time_timespec_interval(timeout);
630
	error = rb_getaddrinfo_a(hostp, portp, hints, &res, &_timeout);
631
    }
632

  
633
    if (error) {
634
        if (hostp && hostp[strlen(hostp)-1] == '\n') {
635
            rb_raise(rb_eSocket, "newline at the end of hostname");
636
        }
637
        rsock_raise_socket_error("getaddrinfo_a", error);
638
    }
639

  
640
    return res;
641
}
642
#endif
643

  
533 644
int
534 645
rsock_fd_family(int fd)
535 646
{
......
811 922
static struct rb_addrinfo *
812 923
call_getaddrinfo(VALUE node, VALUE service,
813 924
                 VALUE family, VALUE socktype, VALUE protocol, VALUE flags,
814
                 int socktype_hack)
925
                 int socktype_hack, VALUE timeout)
815 926
{
816 927
    struct addrinfo hints;
817 928
    struct rb_addrinfo *res;
......
828 939
    if (!NIL_P(flags)) {
829 940
	hints.ai_flags = NUM2INT(flags);
830 941
    }
831
    res = rsock_getaddrinfo(node, service, &hints, socktype_hack);
942

  
943
    if (NIL_P(timeout)) {
944
	res = rsock_getaddrinfo(node, service, &hints, socktype_hack);
945
    } else {
946
#ifdef HAVE_GETADDRINFO_A
947
	res = rsock_getaddrinfo_a(node, service, &hints, socktype_hack, timeout);
948
#else
949
	rb_f_notimplement();
950
#endif
951
    }
832 952

  
833 953
    if (res == NULL)
834 954
	rb_raise(rb_eSocket, "host not found");
......
842 962
                          VALUE family, VALUE socktype, VALUE protocol, VALUE flags,
843 963
                          VALUE inspectnode, VALUE inspectservice)
844 964
{
845
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 1);
965
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 1, Qnil);
846 966
    VALUE canonname;
847 967
    VALUE inspectname = rb_str_equal(node, inspectnode) ? Qnil : make_inspectname(inspectnode, inspectservice, res->ai);
848 968

  
......
912 1032
    VALUE canonname;
913 1033
    VALUE inspectname;
914 1034

  
915
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0);
1035
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0, Qnil);
916 1036

  
917 1037
    inspectname = make_inspectname(node, service, res->ai);
918 1038

  
......
932 1052
}
933 1053

  
934 1054
static VALUE
935
addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE protocol, VALUE flags)
1055
addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE protocol, VALUE flags, VALUE timeout)
936 1056
{
937 1057
    VALUE ret;
938 1058
    struct addrinfo *r;
939 1059
    VALUE inspectname;
940 1060

  
941
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0);
1061
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0, timeout);
942 1062

  
943 1063
    inspectname = make_inspectname(node, service, res->ai);
944 1064

  
......
1691 1811
#endif
1692 1812
        res = call_getaddrinfo(rb_ary_entry(pair, 0), rb_ary_entry(pair, 1),
1693 1813
                               INT2NUM(pfamily), INT2NUM(socktype), INT2NUM(protocol),
1694
                               INT2NUM(flags), 1);
1814
                               INT2NUM(flags), 1, Qnil);
1695 1815

  
1696 1816
        len = res->ai->ai_addrlen;
1697 1817
        memcpy(&ss, res->ai->ai_addr, res->ai->ai_addrlen);
......
2373 2493
 *
2374 2494
 */
2375 2495
static VALUE
2376
addrinfo_s_getaddrinfo(int argc, VALUE *argv, VALUE self)
2496
addrinfo_private_getaddrinfo(int argc, VALUE *argv, VALUE self)
2377 2497
{
2378 2498
    VALUE node, service, family, socktype, protocol, flags;
2379 2499

  
2380 2500
    rb_scan_args(argc, argv, "24", &node, &service, &family, &socktype, &protocol, &flags);
2381
    return addrinfo_list_new(node, service, family, socktype, protocol, flags);
2501
    return addrinfo_list_new(node, service, family, socktype, protocol, flags, Qnil);
2502
}
2503

  
2504
#ifdef HAVE_GETADDRINFO_A
2505
static ID id_timeout;
2506

  
2507
static VALUE
2508
addrinfo_private_getaddrinfo_a(int argc, VALUE *argv, VALUE self)
2509
{
2510
    VALUE node, service, family, socktype, protocol, flags, opts, timeout;
2511

  
2512
    rb_scan_args(argc, argv, "24:", &node, &service, &family, &socktype,
2513
		 &protocol, &flags, &opts);
2514
    rb_get_kwargs(opts, &id_timeout, 1, 0, &timeout);
2515
    return addrinfo_list_new(node, service, family, socktype, protocol, flags, timeout);
2382 2516
}
2517
#endif
2383 2518

  
2384 2519
/*
2385 2520
 * call-seq:
......
2568 2703
    rb_define_method(rb_cAddrinfo, "initialize", addrinfo_initialize, -1);
2569 2704
    rb_define_method(rb_cAddrinfo, "inspect", addrinfo_inspect, 0);
2570 2705
    rb_define_method(rb_cAddrinfo, "inspect_sockaddr", rsock_addrinfo_inspect_sockaddr, 0);
2571
    rb_define_singleton_method(rb_cAddrinfo, "getaddrinfo", addrinfo_s_getaddrinfo, -1);
2706

  
2707
    rb_define_private_method(CLASS_OF(rb_cAddrinfo), "_getaddrinfo",
2708
			     addrinfo_private_getaddrinfo, -1);
2709
#ifdef HAVE_GETADDRINFO_A
2710
    id_timeout = rb_intern("timeout");
2711
    rb_define_private_method(CLASS_OF(rb_cAddrinfo), "_getaddrinfo_a",
2712
			     addrinfo_private_getaddrinfo_a, -1);
2713
#endif
2572 2714
    rb_define_singleton_method(rb_cAddrinfo, "ip", addrinfo_s_ip, 1);
2573 2715
    rb_define_singleton_method(rb_cAddrinfo, "tcp", addrinfo_s_tcp, 2);
2574 2716
    rb_define_singleton_method(rb_cAddrinfo, "udp", addrinfo_s_udp, 2);
include/ruby/intern.h
933 933
struct timeval rb_time_interval(VALUE num);
934 934
struct timeval rb_time_timeval(VALUE time);
935 935
struct timespec rb_time_timespec(VALUE time);
936
struct timespec rb_time_timespec_interval(VALUE num);
936 937
VALUE rb_time_utc_offset(VALUE time);
937 938
/* variable.c */
938 939
VALUE rb_mod_name(VALUE);
test/socket/test_addrinfo.rb
688 688
      assert_equal(ai1.canonname, ai2.canonname)
689 689
    end
690 690

  
691
    def test_addrinfo_timeout
692
      ai = Addrinfo.getaddrinfo("localhost.localdomain", "http", Socket::PF_INET, Socket::SOCK_STREAM, timeout: 1).fetch(0)
693
      assert_equal(6, ai.protocol)
694
      assert_equal(80, ai.ip_port)
695
    end
691 696
  end
692 697
end
time.c
2671 2671
    return time_timespec(time, FALSE);
2672 2672
}
2673 2673

  
2674
struct timespec
2675
rb_time_timespec_interval(VALUE num)
2676
{
2677
    return time_timespec(num, TRUE);
2678
}
2679

  
2674 2680
/*
2675 2681
 *  call-seq:
2676 2682
 *     Time.now -> time