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