# frozen_string_literal: false require 'mkmf' AF_INET6_SOCKET_CREATION_TEST = < #ifndef _WIN32 #include #endif int main(void) { socket(AF_INET6, SOCK_STREAM, 0); return 0; } EOF GETADDRINFO_GETNAMEINFO_TEST = < #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif #ifndef EXIT_FAILURE #define EXIT_FAILURE 1 #endif #ifndef AF_LOCAL #define AF_LOCAL AF_UNIX #endif int main(void) { int passive, gaierr, inet4 = 0, inet6 = 0; struct addrinfo hints, *ai, *aitop; char straddr[INET6_ADDRSTRLEN], strport[16]; #ifdef _WIN32 WSADATA retdata; WSAStartup(MAKEWORD(2, 0), &retdata); #endif for (passive = 0; passive <= 1; passive++) { memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = passive ? AI_PASSIVE : 0; hints.ai_socktype = SOCK_STREAM; if ((gaierr = getaddrinfo(NULL, "54321", &hints, &aitop)) != 0) { (void)gai_strerror(gaierr); goto bad; } for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family == AF_LOCAL) continue; if (ai->ai_addr == NULL) goto bad; #if defined(_AIX) if (ai->ai_family == AF_INET6 && passive) { inet6++; continue; } ai->ai_addr->sa_len = ai->ai_addrlen; ai->ai_addr->sa_family = ai->ai_family; #endif if (ai->ai_addrlen == 0 || getnameinfo(ai->ai_addr, ai->ai_addrlen, straddr, sizeof(straddr), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { goto bad; } if (strcmp(strport, "54321") != 0) { goto bad; } switch (ai->ai_family) { case AF_INET: if (passive) { if (strcmp(straddr, "0.0.0.0") != 0) { goto bad; } } else { if (strcmp(straddr, "127.0.0.1") != 0) { goto bad; } } inet4++; break; case AF_INET6: if (passive) { if (strcmp(straddr, "::") != 0) { goto bad; } } else { if (strcmp(straddr, "::1") != 0) { goto bad; } } inet6++; break; case AF_UNSPEC: goto bad; break; default: /* another family support? */ break; } } } if (!(inet4 == 0 || inet4 == 2)) goto bad; if (!(inet6 == 0 || inet6 == 2)) goto bad; if (aitop) freeaddrinfo(aitop); return EXIT_SUCCESS; bad: if (aitop) freeaddrinfo(aitop); return EXIT_FAILURE; } EOF RECVMSG_WITH_MSG_PEEK_ALLOCATE_FD_TEST = <<'EOF' #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { int ps[2], sv[2]; int ret; ssize_t ss; int s_fd, r_fd; struct msghdr s_msg, r_msg; union { struct cmsghdr hdr; char dummy[CMSG_SPACE(sizeof(int))]; } s_cmsg, r_cmsg; struct iovec s_iov, r_iov; char s_buf[1], r_buf[1]; struct stat s_statbuf, r_statbuf; ret = pipe(ps); if (ret == -1) { perror("pipe"); exit(EXIT_FAILURE); } s_fd = ps[0]; ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, sv); if (ret == -1) { perror("socketpair"); exit(EXIT_FAILURE); } s_msg.msg_name = NULL; s_msg.msg_namelen = 0; s_msg.msg_iov = &s_iov; s_msg.msg_iovlen = 1; s_msg.msg_control = &s_cmsg; s_msg.msg_controllen = CMSG_SPACE(sizeof(int));; s_msg.msg_flags = 0; s_iov.iov_base = &s_buf; s_iov.iov_len = sizeof(s_buf); s_buf[0] = 'a'; s_cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int)); s_cmsg.hdr.cmsg_level = SOL_SOCKET; s_cmsg.hdr.cmsg_type = SCM_RIGHTS; memcpy(CMSG_DATA(&s_cmsg.hdr), (char *)&s_fd, sizeof(int)); ss = sendmsg(sv[0], &s_msg, 0); if (ss == -1) { perror("sendmsg"); exit(EXIT_FAILURE); } r_msg.msg_name = NULL; r_msg.msg_namelen = 0; r_msg.msg_iov = &r_iov; r_msg.msg_iovlen = 1; r_msg.msg_control = &r_cmsg; r_msg.msg_controllen = CMSG_SPACE(sizeof(int)); r_msg.msg_flags = 0; r_iov.iov_base = &r_buf; r_iov.iov_len = sizeof(r_buf); r_buf[0] = '0'; memset(&r_cmsg, 0xff, CMSG_SPACE(sizeof(int))); ss = recvmsg(sv[1], &r_msg, MSG_PEEK); if (ss == -1) { perror("recvmsg"); exit(EXIT_FAILURE); } if (ss != 1) { fprintf(stderr, "unexpected return value from recvmsg: %ld\n", (long)ss); exit(EXIT_FAILURE); } if (r_buf[0] != 'a') { fprintf(stderr, "unexpected return data from recvmsg: 0x%02x\n", r_buf[0]); exit(EXIT_FAILURE); } if (r_msg.msg_controllen < CMSG_LEN(sizeof(int))) { fprintf(stderr, "unexpected: r_msg.msg_controllen < CMSG_LEN(sizeof(int)) not hold: %ld\n", (long)r_msg.msg_controllen); exit(EXIT_FAILURE); } if (r_cmsg.hdr.cmsg_len < CMSG_LEN(sizeof(int))) { fprintf(stderr, "unexpected: r_cmsg.hdr.cmsg_len < CMSG_LEN(sizeof(int)) not hold: %ld\n", (long)r_cmsg.hdr.cmsg_len); exit(EXIT_FAILURE); } memcpy((char *)&r_fd, CMSG_DATA(&r_cmsg.hdr), sizeof(int)); if (r_fd < 0) { fprintf(stderr, "negative r_fd: %d\n", r_fd); exit(EXIT_FAILURE); } if (r_fd == s_fd) { fprintf(stderr, "r_fd and s_fd is same: %d\n", r_fd); exit(EXIT_FAILURE); } ret = fstat(s_fd, &s_statbuf); if (ret == -1) { perror("fstat(s_fd)"); exit(EXIT_FAILURE); } ret = fstat(r_fd, &r_statbuf); if (ret == -1) { perror("fstat(r_fd)"); exit(EXIT_FAILURE); } if (s_statbuf.st_dev != r_statbuf.st_dev || s_statbuf.st_ino != r_statbuf.st_ino) { fprintf(stderr, "dev/ino doesn't match: s_fd:%ld/%ld r_fd:%ld/%ld\n", (long)s_statbuf.st_dev, (long)s_statbuf.st_ino, (long)r_statbuf.st_dev, (long)r_statbuf.st_ino); exit(EXIT_FAILURE); } return EXIT_SUCCESS; } EOF def test_recvmsg_with_msg_peek_creates_fds(headers) case RUBY_PLATFORM when /linux/ # Linux 2.6.38 allocate fds by recvmsg with MSG_PEEK. close_fds = true when /bsd|darwin/ # FreeBSD 8.2.0, NetBSD 5 and MacOS X Snow Leopard doesn't # allocate fds by recvmsg with MSG_PEEK. # [ruby-dev:44189] # http://bugs.ruby-lang.org/issues/5075 close_fds = false when /cygwin/ # Cygwin doesn't support fd passing. # http://cygwin.com/ml/cygwin/2003-09/msg01808.html close_fds = false else close_fds = nil end if !CROSS_COMPILING if checking_for("recvmsg() with MSG_PEEK allocate file descriptors") { try_run(cpp_include(headers) + RECVMSG_WITH_MSG_PEEK_ALLOCATE_FD_TEST) } if close_fds == false warn "unexpected fd-passing recvmsg() with MSG_PEEK behavor on #{RUBY_PLATFORM}: fd allocation unexpected." elsif close_fds == nil puts "info: #{RUBY_PLATFORM} recvmsg() with MSG_PEEK allocates fds on fd-passing." end close_fds = true else if close_fds == true warn "unexpected fd-passing recvmsg() with MSG_PEEK behavor on #{RUBY_PLATFORM}: fd allocation expected." elsif close_fds == nil puts "info: #{RUBY_PLATFORM}: recvmsg() with MSG_PEEK doesn't allocates fds on fd-passing." end close_fds = false end end if close_fds == nil abort < end %w[ sys/uio.h xti.h netinet/in_systm.h netinet/tcp.h netinet/tcp_fsm.h netinet/udp.h arpa/inet.h netpacket/packet.h net/ethernet.h sys/un.h ifaddrs.h sys/ioctl.h sys/sockio.h net/if.h sys/param.h sys/ucred.h ucred.h net/if_dl.h arpa/nameser.h resolv.h ].each {|h| if have_header(h, headers) headers << h end } have_struct_member("struct sockaddr", "sa_len", headers) # 4.4BSD have_struct_member("struct sockaddr_in", "sin_len", headers) # 4.4BSD have_struct_member("struct sockaddr_in6", "sin6_len", headers) # 4.4BSD if have_type("struct sockaddr_un", headers) # POSIX have_struct_member("struct sockaddr_un", "sun_len", headers) # 4.4BSD end have_type("struct sockaddr_dl", headers) # AF_LINK address. 4.4BSD since Net2 have_type("struct sockaddr_storage", headers) have_type("struct addrinfo", headers) if have_type("socklen_t", headers) if try_static_assert("sizeof(socklen_t) >= sizeof(long)", headers) $defs << "-DRSTRING_SOCKLEN=(socklen_t)RSTRING_LEN" end end have_type("struct in_pktinfo", headers) {|src| src.sub(%r'^/\*top\*/', '\&'"\n#if defined(IPPROTO_IP) && defined(IP_PKTINFO)") << "#else\n" << "#error\n" << ">>>>>> no in_pktinfo <<<<<<\n" << "#endif\n" } and have_struct_member("struct in_pktinfo", "ipi_spec_dst", headers) have_type("struct in6_pktinfo", headers) {|src| src.sub(%r'^/\*top\*/', '\&'"\n#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO)") << "#else\n" << "#error\n" << ">>>>>> no in6_pktinfo <<<<<<\n" << "#endif\n" } have_type("struct sockcred", headers) have_type("struct cmsgcred", headers) have_type("struct ip_mreq", headers) # 4.4BSD have_type("struct ip_mreqn", headers) # Linux 2.4 have_type("struct ipv6_mreq", headers) # RFC 3493 have_msg_control = nil have_msg_control = have_struct_member('struct msghdr', 'msg_control', headers) unless $mswin or $mingw have_struct_member('struct msghdr', 'msg_accrights', headers) if have_type("struct tcp_info", headers) have_const("TCP_ESTABLISHED", headers) have_const("TCP_SYN_SENT", headers) have_const("TCP_SYN_RECV", headers) have_const("TCP_FIN_WAIT1", headers) have_const("TCP_FIN_WAIT2", headers) have_const("TCP_TIME_WAIT", headers) have_const("TCP_CLOSE", headers) have_const("TCP_CLOSE_WAIT", headers) have_const("TCP_LAST_ACK", headers) have_const("TCP_LISTEN", headers) have_const("TCP_CLOSING", headers) have_struct_member('struct tcp_info', 'tcpi_state', headers) if /solaris/ !~ RUBY_PLATFORM have_struct_member('struct tcp_info', 'tcpi_ca_state', headers) end have_struct_member('struct tcp_info', 'tcpi_retransmits', headers) have_struct_member('struct tcp_info', 'tcpi_probes', headers) have_struct_member('struct tcp_info', 'tcpi_backoff', headers) have_struct_member('struct tcp_info', 'tcpi_options', headers) have_struct_member('struct tcp_info', 'tcpi_snd_wscale', headers) have_struct_member('struct tcp_info', 'tcpi_rcv_wscale', headers) have_struct_member('struct tcp_info', 'tcpi_rto', headers) have_struct_member('struct tcp_info', 'tcpi_ato', headers) have_struct_member('struct tcp_info', 'tcpi_snd_mss', headers) have_struct_member('struct tcp_info', 'tcpi_rcv_mss', headers) have_struct_member('struct tcp_info', 'tcpi_unacked', headers) have_struct_member('struct tcp_info', 'tcpi_sacked', headers) have_struct_member('struct tcp_info', 'tcpi_lost', headers) have_struct_member('struct tcp_info', 'tcpi_retrans', headers) have_struct_member('struct tcp_info', 'tcpi_fackets', headers) have_struct_member('struct tcp_info', 'tcpi_last_data_sent', headers) have_struct_member('struct tcp_info', 'tcpi_last_ack_sent', headers) have_struct_member('struct tcp_info', 'tcpi_last_data_recv', headers) have_struct_member('struct tcp_info', 'tcpi_last_ack_recv', headers) have_struct_member('struct tcp_info', 'tcpi_pmtu', headers) have_struct_member('struct tcp_info', 'tcpi_rcv_ssthresh', headers) have_struct_member('struct tcp_info', 'tcpi_rtt', headers) have_struct_member('struct tcp_info', 'tcpi_rttvar', headers) have_struct_member('struct tcp_info', 'tcpi_snd_ssthresh', headers) have_struct_member('struct tcp_info', 'tcpi_snd_cwnd', headers) have_struct_member('struct tcp_info', 'tcpi_advmss', headers) have_struct_member('struct tcp_info', 'tcpi_reordering', headers) have_struct_member('struct tcp_info', 'tcpi_rcv_rtt', headers) have_struct_member('struct tcp_info', 'tcpi_rcv_space', headers) have_struct_member('struct tcp_info', 'tcpi_total_retrans', headers) # FreeBSD extension have_struct_member('struct tcp_info', 'tcpi_snd_wnd', headers) have_struct_member('struct tcp_info', 'tcpi_snd_bwnd', headers) have_struct_member('struct tcp_info', 'tcpi_snd_nxt', headers) have_struct_member('struct tcp_info', 'tcpi_rcv_nxt', headers) have_struct_member('struct tcp_info', 'tcpi_toe_tid', headers) have_struct_member('struct tcp_info', 'tcpi_snd_rexmitpack', headers) have_struct_member('struct tcp_info', 'tcpi_rcv_ooopack', headers) have_struct_member('struct tcp_info', 'tcpi_snd_zerowin', headers) end case RUBY_PLATFORM when /mswin(32|64)|mingw/ test_func = "WSACleanup" have_library("iphlpapi") have_library("ws2_32", "WSACleanup", headers) when /cygwin/ test_func = "socket(0,0,0)" when /haiku/ test_func = "socket(0,0,0)" have_library("network", "socket(0,0,0)", headers) else test_func = "socket(0,0,0)" have_library("nsl", 't_open("", 0, (struct t_info *)NULL)', headers) # SunOS have_library("socket", "socket(0,0,0)", headers) # SunOS end if have_func(test_func, headers) have_func("sendmsg(0, (struct msghdr *)NULL, 0)", headers) # POSIX have_recvmsg = have_func("recvmsg(0, (struct msghdr *)NULL, 0)", headers) # POSIX have_func("freehostent((struct hostent *)NULL)", headers) # RFC 2553 have_func("freeaddrinfo((struct addrinfo *)NULL)", headers) # RFC 2553 if /haiku/ !~ RUBY_PLATFORM and have_func("gai_strerror(0)", headers) # POSIX if checking_for("gai_strerror() returns const pointer") {!try_compile(< void conftest_gai_strerror_is_const() { *gai_strerror(0) = 0; } EOF $defs << "-DGAI_STRERROR_CONST" end end have_func("accept4", headers) have_func('inet_ntop(0, (const void *)0, (char *)0, 0)', headers) or have_func("inet_ntoa(*(struct in_addr *)NULL)", headers) have_func('inet_pton(0, "", (void *)0)', headers) or have_func('inet_aton("", (struct in_addr *)0)', headers) have_func('getservbyport(0, "")', headers) have_func("getifaddrs((struct ifaddrs **)NULL)", headers) have_struct_member("struct if_data", "ifi_vhid", headers) # FreeBSD have_func("getpeereid", headers) have_func("getpeerucred(0, (ucred_t **)NULL)", headers) # SunOS have_func_decl = proc do |name, headers| # check if there is a declaration of by trying to declare # both "int (void)" and "void (void)" # (at least one attempt should fail if there is a declaration) if !checking_for("declaration of #{name}()") {!%w[int void].all? {|ret| try_compile(<true)} and #include int t(struct in6_addr *addr) {return IN6_IS_ADDR_UNSPECIFIED(addr);} SRC checking_for("fixing apple's #{hdr}", "%s") do file = xpopen(%w"clang -include netinet/in.h -E -xc -", in: IO::NULL) do |f| re = %r[^# *\d+ *"(.*/netinet/in\.h)"] Logging.message " grep(#{re})\n" f.read[re, 1] end Logging.message "Substitute from #{file}\n" in6 = File.read(file) if in6.gsub!(/\*\(const\s+__uint32_t\s+\*\)\(const\s+void\s+\*\)\(&(\(\w+\))->s6_addr\[(\d+)\]\)/) do i, r = $2.to_i.divmod(4) if r.zero? "#$1->__u6_addr.__u6_addr32[#{i}]" else $& end end FileUtils.mkdir_p(File.dirname(hdr)) open(hdr, "w") {|f| f.write(in6)} $distcleanfiles << hdr $distcleandirs << File.dirname(hdr) "done" else "not needed" end end create_makefile("socket") end