1require 'mkmf' 2 3$INCFLAGS << " -I$(topdir) -I$(top_srcdir)" 4 5case RUBY_PLATFORM 6when /(ms|bcc)win(32|64)|mingw/ 7 test_func = "WSACleanup" 8 have_library("ws2_32", "WSACleanup") 9when /cygwin/ 10 test_func = "socket" 11when /beos/ 12 test_func = "socket" 13 have_library("net", "socket") 14when /haiku/ 15 test_func = "socket" 16 have_library("network", "socket") 17when /i386-os2_emx/ 18 test_func = "socket" 19 have_library("socket", "socket") 20else 21 test_func = "socket" 22 have_library("nsl", "t_open") 23 have_library("socket", "socket") 24end 25 26if /darwin/ =~ RUBY_PLATFORM 27 # For IPv6 extension header access on OS X 10.7+ [Bug #8517] 28 $CFLAGS << " -D__APPLE_USE_RFC_3542" 29end 30 31headers = [] 32unless $mswin or $bccwin or $mingw 33 headers = %w<sys/types.h netdb.h string.h sys/socket.h netinet/in.h> 34end 35if /solaris/ =~ RUBY_PLATFORM and !try_compile("") 36 # bug of gcc 3.0 on Solaris 8 ? 37 headers << "sys/feature_tests.h" 38end 39if have_header("arpa/inet.h") 40 headers << "arpa/inet.h" 41end 42 43ipv6 = false 44default_ipv6 = /cygwin|beos|haiku/ !~ RUBY_PLATFORM 45if enable_config("ipv6", default_ipv6) 46 if checking_for("ipv6") {try_link(<<EOF)} 47#include <sys/types.h> 48#ifndef _WIN32 49#include <sys/socket.h> 50#endif 51int 52main(void) 53{ 54 socket(AF_INET6, SOCK_STREAM, 0); 55 return 0; 56} 57EOF 58 $defs << "-DENABLE_IPV6" << "-DINET6" 59 ipv6 = true 60 end 61end 62 63if ipv6 64 if $mingw 65 $CPPFLAGS << " -D_WIN32_WINNT=0x501" unless $CPPFLAGS.include?("_WIN32_WINNT") 66 end 67 ipv6lib = nil 68 class << (fmt = "unknown") 69 def %(s) s || self end 70 end 71 idirs, ldirs = dir_config("inet6", %w[/usr/inet6 /usr/local/v6].find {|d| File.directory?(d)}) 72 checking_for("ipv6 type", fmt) do 73 if have_macro("IPV6_INRIA_VERSION", "netinet/in.h") 74 "inria" 75 elsif have_macro("__KAME__", "netinet/in.h") 76 have_library(ipv6lib = "inet6") 77 "kame" 78 elsif have_macro("_TOSHIBA_INET6", "sys/param.h") 79 have_library(ipv6lib = "inet6") and "toshiba" 80 elsif have_macro("__V6D__", "sys/v6config.h") 81 have_library(ipv6lib = "v6") and "v6d" 82 elsif have_macro("_ZETA_MINAMI_INET6", "sys/param.h") 83 have_library(ipv6lib = "inet6") and "zeta" 84 elsif ipv6lib = with_config("ipv6-lib") 85 warn <<EOS 86--with-ipv6-lib and --with-ipv6-libdir option will be obsolete, use 87--with-inet6lib and --with-inet6-{include,lib} options instead. 88EOS 89 find_library(ipv6lib, nil, with_config("ipv6-libdir", ldirs)) and 90 ipv6lib 91 elsif have_library("inet6") 92 "inet6" 93 end 94 end or not ipv6lib or abort <<EOS 95 96Fatal: no #{ipv6lib} library found. cannot continue. 97You need to fetch lib#{ipv6lib}.a from appropriate 98ipv6 kit and compile beforehand. 99EOS 100end 101 102if have_struct_member("struct sockaddr_in", "sin_len", headers) 103 $defs[-1] = "-DHAVE_SIN_LEN" 104end 105 106# doug's fix, NOW add -Dss_family... only if required! 107doug = proc {have_struct_member("struct sockaddr_storage", "ss_family", headers)} 108if (doug[] or 109 with_cppflags($CPPFLAGS + " -Dss_family=__ss_family", &doug)) 110 $defs[-1] = "-DHAVE_SOCKADDR_STORAGE" 111 doug = proc {have_struct_member("struct sockaddr_storage", "ss_len", headers)} 112 doug[] or with_cppflags($CPPFLAGS + " -Dss_len=__ss_len", &doug) 113end 114 115if have_struct_member("struct sockaddr", "sa_len", headers) 116 $defs[-1] = "-DHAVE_SA_LEN " 117end 118 119have_header("netinet/tcp.h") if /cygwin/ !~ RUBY_PLATFORM # for cygwin 1.1.5 120have_header("netinet/udp.h") 121 122if !have_macro("IPPROTO_IPV6", headers) && have_const("IPPROTO_IPV6", headers) 123 IO.read(File.join(File.dirname(__FILE__), "mkconstants.rb")).sub(/\A.*^__END__$/m, '').split(/\r?\n/).grep(/\AIPPROTO_\w*/){$&}.each {|name| 124 have_const(name, headers) unless $defs.include?("-DHAVE_CONST_#{name.upcase}") 125 } 126end 127 128if have_func("sendmsg") | have_func("recvmsg") 129 have_struct_member('struct msghdr', 'msg_control', ['sys/types.h', 'sys/socket.h']) 130 have_struct_member('struct msghdr', 'msg_accrights', ['sys/types.h', 'sys/socket.h']) 131end 132 133if checking_for("recvmsg() with MSG_PEEK allocate file descriptors") {try_run(cpp_include(headers) + <<'EOF')} 134#include <stdlib.h> 135#include <stdio.h> 136#include <string.h> 137#include <sys/types.h> 138#include <sys/stat.h> 139#include <sys/socket.h> 140#include <sys/un.h> 141#include <unistd.h> 142 143int main(int argc, char *argv[]) 144{ 145 int ps[2], sv[2]; 146 int ret; 147 ssize_t ss; 148 int s_fd, r_fd; 149 struct msghdr s_msg, r_msg; 150 union { 151 struct cmsghdr hdr; 152 char dummy[CMSG_SPACE(sizeof(int))]; 153 } s_cmsg, r_cmsg; 154 struct iovec s_iov, r_iov; 155 char s_buf[1], r_buf[1]; 156 struct stat s_statbuf, r_statbuf; 157 158 ret = pipe(ps); 159 if (ret == -1) { perror("pipe"); exit(EXIT_FAILURE); } 160 161 s_fd = ps[0]; 162 163 ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, sv); 164 if (ret == -1) { perror("socketpair"); exit(EXIT_FAILURE); } 165 166 s_msg.msg_name = NULL; 167 s_msg.msg_namelen = 0; 168 s_msg.msg_iov = &s_iov; 169 s_msg.msg_iovlen = 1; 170 s_msg.msg_control = &s_cmsg; 171 s_msg.msg_controllen = CMSG_SPACE(sizeof(int));; 172 s_msg.msg_flags = 0; 173 174 s_iov.iov_base = &s_buf; 175 s_iov.iov_len = sizeof(s_buf); 176 177 s_buf[0] = 'a'; 178 179 s_cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int)); 180 s_cmsg.hdr.cmsg_level = SOL_SOCKET; 181 s_cmsg.hdr.cmsg_type = SCM_RIGHTS; 182 memcpy(CMSG_DATA(&s_cmsg.hdr), (char *)&s_fd, sizeof(int)); 183 184 ss = sendmsg(sv[0], &s_msg, 0); 185 if (ss == -1) { perror("sendmsg"); exit(EXIT_FAILURE); } 186 187 r_msg.msg_name = NULL; 188 r_msg.msg_namelen = 0; 189 r_msg.msg_iov = &r_iov; 190 r_msg.msg_iovlen = 1; 191 r_msg.msg_control = &r_cmsg; 192 r_msg.msg_controllen = CMSG_SPACE(sizeof(int)); 193 r_msg.msg_flags = 0; 194 195 r_iov.iov_base = &r_buf; 196 r_iov.iov_len = sizeof(r_buf); 197 198 r_buf[0] = '0'; 199 200 memset(&r_cmsg, 0xff, CMSG_SPACE(sizeof(int))); 201 202 ss = recvmsg(sv[1], &r_msg, MSG_PEEK); 203 if (ss == -1) { perror("recvmsg"); exit(EXIT_FAILURE); } 204 205 if (ss != 1) { 206 fprintf(stderr, "unexpected return value from recvmsg: %ld\n", (long)ss); 207 exit(EXIT_FAILURE); 208 } 209 if (r_buf[0] != 'a') { 210 fprintf(stderr, "unexpected return data from recvmsg: 0x%02x\n", r_buf[0]); 211 exit(EXIT_FAILURE); 212 } 213 214 if (r_msg.msg_controllen < CMSG_LEN(sizeof(int))) { 215 fprintf(stderr, "unexpected: r_msg.msg_controllen < CMSG_LEN(sizeof(int)) not hold: %ld\n", 216 (long)r_msg.msg_controllen); 217 exit(EXIT_FAILURE); 218 } 219 if (r_cmsg.hdr.cmsg_len < CMSG_LEN(sizeof(int))) { 220 fprintf(stderr, "unexpected: r_cmsg.hdr.cmsg_len < CMSG_LEN(sizeof(int)) not hold: %ld\n", 221 (long)r_cmsg.hdr.cmsg_len); 222 exit(EXIT_FAILURE); 223 } 224 memcpy((char *)&r_fd, CMSG_DATA(&r_cmsg.hdr), sizeof(int)); 225 226 if (r_fd < 0) { 227 fprintf(stderr, "negative r_fd: %d\n", r_fd); 228 exit(EXIT_FAILURE); 229 } 230 231 if (r_fd == s_fd) { 232 fprintf(stderr, "r_fd and s_fd is same: %d\n", r_fd); 233 exit(EXIT_FAILURE); 234 } 235 236 ret = fstat(s_fd, &s_statbuf); 237 if (ret == -1) { perror("fstat(s_fd)"); exit(EXIT_FAILURE); } 238 239 ret = fstat(r_fd, &r_statbuf); 240 if (ret == -1) { perror("fstat(r_fd)"); exit(EXIT_FAILURE); } 241 242 if (s_statbuf.st_dev != r_statbuf.st_dev || 243 s_statbuf.st_ino != r_statbuf.st_ino) { 244 fprintf(stderr, "dev/ino doesn't match: s_fd:%ld/%ld r_fd:%ld/%ld\n", 245 (long)s_statbuf.st_dev, (long)s_statbuf.st_ino, 246 (long)r_statbuf.st_dev, (long)r_statbuf.st_ino); 247 exit(EXIT_FAILURE); 248 } 249 250 return EXIT_SUCCESS; 251} 252EOF 253 $defs << "-DFD_PASSING_WORK_WITH_RECVMSG_MSG_PEEK" 254end 255 256getaddr_info_ok = (enable_config("wide-getaddrinfo") && :wide) || 257 (checking_for("wide getaddrinfo") {try_run(<<EOF)} && :os) 258#{cpp_include(headers)} 259#include <stdlib.h> 260 261#ifndef EXIT_SUCCESS 262#define EXIT_SUCCESS 0 263#endif 264#ifndef EXIT_FAILURE 265#define EXIT_FAILURE 1 266#endif 267 268#ifndef AF_LOCAL 269#define AF_LOCAL AF_UNIX 270#endif 271 272int 273main(void) 274{ 275 int passive, gaierr, inet4 = 0, inet6 = 0; 276 struct addrinfo hints, *ai, *aitop; 277 char straddr[INET6_ADDRSTRLEN], strport[16]; 278#ifdef _WIN32 279 WSADATA retdata; 280 281 WSAStartup(MAKEWORD(2, 0), &retdata); 282#endif 283 284 for (passive = 0; passive <= 1; passive++) { 285 memset(&hints, 0, sizeof(hints)); 286 hints.ai_family = AF_UNSPEC; 287 hints.ai_protocol = IPPROTO_TCP; 288 hints.ai_flags = passive ? AI_PASSIVE : 0; 289 hints.ai_socktype = SOCK_STREAM; 290 if ((gaierr = getaddrinfo(NULL, "54321", &hints, &aitop)) != 0) { 291 (void)gai_strerror(gaierr); 292 goto bad; 293 } 294 for (ai = aitop; ai; ai = ai->ai_next) { 295 if (ai->ai_family == AF_LOCAL) continue; 296 if (ai->ai_addr == NULL) 297 goto bad; 298#if defined(_AIX) 299 if (ai->ai_family == AF_INET6 && passive) { 300 inet6++; 301 continue; 302 } 303 ai->ai_addr->sa_len = ai->ai_addrlen; 304 ai->ai_addr->sa_family = ai->ai_family; 305#endif 306 if (ai->ai_addrlen == 0 || 307 getnameinfo(ai->ai_addr, ai->ai_addrlen, 308 straddr, sizeof(straddr), strport, sizeof(strport), 309 NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 310 goto bad; 311 } 312 if (strcmp(strport, "54321") != 0) { 313 goto bad; 314 } 315 switch (ai->ai_family) { 316 case AF_INET: 317 if (passive) { 318 if (strcmp(straddr, "0.0.0.0") != 0) { 319 goto bad; 320 } 321 } else { 322 if (strcmp(straddr, "127.0.0.1") != 0) { 323 goto bad; 324 } 325 } 326 inet4++; 327 break; 328 case AF_INET6: 329 if (passive) { 330 if (strcmp(straddr, "::") != 0) { 331 goto bad; 332 } 333 } else { 334 if (strcmp(straddr, "::1") != 0) { 335 goto bad; 336 } 337 } 338 inet6++; 339 break; 340 case AF_UNSPEC: 341 goto bad; 342 break; 343 default: 344 /* another family support? */ 345 break; 346 } 347 } 348 } 349 350 if (!(inet4 == 0 || inet4 == 2)) 351 goto bad; 352 if (!(inet6 == 0 || inet6 == 2)) 353 goto bad; 354 355 if (aitop) 356 freeaddrinfo(aitop); 357 return EXIT_SUCCESS; 358 359 bad: 360 if (aitop) 361 freeaddrinfo(aitop); 362 return EXIT_FAILURE; 363} 364EOF 365if ipv6 and not getaddr_info_ok 366 abort <<EOS 367 368Fatal: --enable-ipv6 is specified, and your OS seems to support IPv6 feature. 369But your getaddrinfo() and getnameinfo() are appeared to be broken. Sorry, 370you cannot compile IPv6 socket classes with broken these functions. 371You can try --enable-wide-getaddrinfo. 372EOS 373end 374 375case with_config("lookup-order-hack", "UNSPEC") 376when "INET" 377 $defs << "-DLOOKUP_ORDER_HACK_INET" 378when "INET6" 379 $defs << "-DLOOKUP_ORDER_HACK_INET6" 380when "UNSPEC" 381 # nothing special 382else 383 abort <<EOS 384 385Fatal: invalid value for --with-lookup-order-hack (expected INET, INET6 or UNSPEC) 386EOS 387end 388 389have_type("struct addrinfo", headers) 390have_func("freehostent") 391have_func("freeaddrinfo") 392if /haiku/ !~ RUBY_PLATFORM and have_func("gai_strerror") 393 if checking_for("gai_strerror() returns const pointer") {!try_compile(<<EOF)} 394#{cpp_include(headers)} 395#include <stdlib.h> 396void 397conftest_gai_strerror_is_const() 398{ 399 *gai_strerror(0) = 0; 400} 401EOF 402 $defs << "-DGAI_STRERROR_CONST" 403 end 404end 405 406have_func("accept4") 407 408$objs = [ 409 "init.#{$OBJEXT}", 410 "constants.#{$OBJEXT}", 411 "basicsocket.#{$OBJEXT}", 412 "socket.#{$OBJEXT}", 413 "ipsocket.#{$OBJEXT}", 414 "tcpsocket.#{$OBJEXT}", 415 "tcpserver.#{$OBJEXT}", 416 "sockssocket.#{$OBJEXT}", 417 "udpsocket.#{$OBJEXT}", 418 "unixsocket.#{$OBJEXT}", 419 "unixserver.#{$OBJEXT}", 420 "option.#{$OBJEXT}", 421 "ancdata.#{$OBJEXT}", 422 "raddrinfo.#{$OBJEXT}" 423] 424 425if getaddr_info_ok == :wide or 426 !have_func("getnameinfo", headers) or !have_func("getaddrinfo", headers) 427 if have_struct_member("struct in6_addr", "s6_addr8", headers) 428 $defs[-1] = "s6_addr=s6_addr8" 429 end 430 if ipv6 == "kame" && have_struct_member("struct in6_addr", "s6_addr32", headers) 431 $defs[-1] = "-DFAITH" 432 end 433 $CPPFLAGS="-I. "+$CPPFLAGS 434 $objs += ["getaddrinfo.#{$OBJEXT}"] 435 $objs += ["getnameinfo.#{$OBJEXT}"] 436 $defs << "-DGETADDRINFO_EMU" 437end 438 439have_func('inet_ntop(0, (const void *)0, (char *)0, 0)') or 440 have_func("inet_ntoa(*(struct in_addr *)NULL)") 441have_func('inet_pton(0, "", (void *)0)') or have_func('inet_aton("", (struct in_addr *)0)') 442have_func('getservbyport(0, "")') 443have_header("arpa/nameser.h") 444have_header("resolv.h") 445 446have_header("ifaddrs.h") 447have_func("getifaddrs") 448have_header("sys/ioctl.h") 449have_header("sys/sockio.h") 450have_header("net/if.h", headers) 451 452have_header("sys/param.h", headers) 453have_header("sys/ucred.h", headers) 454 455unless have_type("socklen_t", headers) 456 $defs << "-Dsocklen_t=int" 457end 458 459have_header("sys/un.h") 460have_header("sys/uio.h") 461have_type("struct in_pktinfo", headers) {|src| 462 src.sub(%r'^/\*top\*/', '\&'"\n#if defined(IPPROTO_IP) && defined(IP_PKTINFO)") << 463 "#else\n" << "#error\n" << ">>>>>> no in_pktinfo <<<<<<\n" << "#endif\n" 464} and have_struct_member("struct in_pktinfo", "ipi_spec_dst", headers) 465have_type("struct in6_pktinfo", headers) {|src| 466 src.sub(%r'^/\*top\*/', '\&'"\n#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO)") << 467 "#else\n" << "#error\n" << ">>>>>> no in6_pktinfo <<<<<<\n" << "#endif\n" 468} 469 470have_type("struct sockcred", headers) 471have_type("struct cmsgcred", headers) 472 473have_func("getpeereid") 474 475have_header("ucred.h", headers) 476have_func("getpeerucred") 477 478have_func("if_indextoname") 479 480have_type("struct ip_mreq", headers) # 4.4BSD 481have_type("struct ip_mreqn", headers) # Linux 2.4 482have_type("struct ipv6_mreq", headers) # RFC 3493 483 484# workaround for recent Windows SDK 485$defs << "-DIPPROTO_IPV6=IPPROTO_IPV6" if $defs.include?("-DHAVE_CONST_IPPROTO_IPV6") && !have_macro("IPPROTO_IPV6") 486 487$distcleanfiles << "constants.h" << "constdefs.*" 488 489if have_func(test_func) 490 have_func("hsterror") 491 have_func("getipnodebyname") or have_func("gethostbyname2") 492 if !have_func("socketpair(0, 0, 0, 0)") and have_func("rb_w32_socketpair(0, 0, 0, 0)") 493 $defs << "-Dsocketpair(a,b,c,d)=rb_w32_socketpair((a),(b),(c),(d))" 494 $defs << "-DHAVE_SOCKETPAIR" 495 end 496 unless have_func("gethostname((char *)0, 0)") 497 have_func("uname") 498 end 499 if enable_config("socks", ENV["SOCKS_SERVER"]) 500 if have_library("socks5", "SOCKSinit") 501 $defs << "-DSOCKS5" << "-DSOCKS" 502 elsif have_library("socks", "Rconnect") 503 $defs << "-DSOCKS" 504 end 505 end 506 hdr = "netinet6/in6.h" 507 if /darwin/ =~ RUBY_PLATFORM and !try_compile(<<"SRC", nil, :werror=>true) 508#include <netinet/in.h> 509int t(struct in6_addr *addr) {return IN6_IS_ADDR_UNSPECIFIED(addr);} 510SRC 511 print "fixing apple's netinet6/in6.rb ..."; $stdout.flush 512 in6 = File.read("/usr/include/#{hdr}") 513 if in6.gsub!(/\*\(const\s+__uint32_t\s+\*\)\(const\s+void\s+\*\)\(&(\(\w+\))->s6_addr\[(\d+)\]\)/) do 514 i, r = $2.to_i.divmod(4) 515 if r.zero? 516 "#$1->__u6_addr.__u6_addr32[#{i}]" 517 else 518 $& 519 end 520 end 521 FileUtils.mkdir_p(File.dirname(hdr)) 522 open(hdr, "w") {|f| f.write(in6)} 523 $distcleanfiles << hdr 524 $distcleandirs << File.dirname(hdr) 525 puts "done" 526 else 527 puts "not needed" 528 end 529 end 530 create_makefile("socket") 531end 532