Package: erlang Version: 11.b.2-4 Severity: wishlist Tags: patch Included patch adds support for IPv6 to the ssl application. After applying the patch, you need to run autoconf and autoheader in the erts subdir.
-- System Information: Debian Release: 4.0 APT prefers testing APT policy: (750, 'testing'), (671, 'stable'), (500, 'testing'), (300, 'unstable'), (1, 'experimental') Architecture: i386 (i686) Shell: /bin/sh linked to /bin/bash Kernel: Linux 2.6.18-3-k7 Locale: LANG=sv_SE.UTF-8, LC_CTYPE=sv_SE.UTF-8 (charmap=UTF-8)
diff -ur erlang-11.b.2.orig/erts/configure.in erlang-11.b.2/erts/configure.in --- erlang-11.b.2.orig/erts/configure.in 2007-03-04 17:46:34.000000000 +0100 +++ erlang-11.b.2/erts/configure.in 2007-03-04 12:42:33.000000000 +0100 @@ -1051,6 +1051,8 @@ dnl inet_gethost with ipv6 support. AC_CHECK_FUNCS([getipnodebyname getipnodebyaddr gethostbyname2]) AC_CHECK_TYPES([struct in6_addr],,,[#include<netdb.h>]) +AC_CHECK_FUNCS([getnameinfo inet_pton]) +AC_CHECK_TYPES([struct sockaddr_storage],,,[#include <netinet/in.h>]) AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlopen \ pread pwrite writev memmove strerror strerror_r strncasecmp \ diff -ur erlang-11.b.2.orig/lib/ssl/c_src/esock.c erlang-11.b.2/lib/ssl/c_src/esock.c --- erlang-11.b.2.orig/lib/ssl/c_src/esock.c 2006-05-03 10:18:31.000000000 +0200 +++ erlang-11.b.2/lib/ssl/c_src/esock.c 2007-03-04 17:39:41.000000000 +0100 @@ -130,6 +130,11 @@ #define INADDR_NONE 0xffffffff /* Should be in <netinet/in.h>. */ #endif +#if defined(HAVE_STRUCT_SOCKADDR_STORAGE) && defined(HAVE_GETNAMEINFO) && \ + defined(HAVE_INET_PTON) +# define USE_IPV6 +#endif + #include "esock.h" #include "debuglog.h" #include "esock_utils.h" @@ -175,6 +180,15 @@ static void print_connections(void); static int check_num_sock_fds(FD fd); static void safe_close(FD fd); + +#if defined(USE_IPV6) +static FD do_connect6(char *lipstring, int lport, char *fipstring, int fport); +static FD do_listen6(char *ipstring, int lport, int backlog, int *aport); +static int ss_getport(const struct sockaddr *sa, socklen_t size); +static int reply_sockaddr(int cmd, int fd, const struct sockaddr *sa, + socklen_t size); +#endif + static Connection *new_connection(int state, FD fd); static Connection *get_connection(FD fd); static void remove_connection(Connection *conn); @@ -362,7 +376,11 @@ char *protocol_vsn, *cipher; unsigned char *cert, *bin; int certlen, binlen; +#if defined(USE_IPV6) + struct sockaddr_storage iserv_addr; +#else struct sockaddr_in iserv_addr; +#endif int sret = 1; Connection *cp, *cpnext, *newcp; Proxy *pp; @@ -439,7 +457,12 @@ /* Add to pending proxy connections */ SET_NONBLOCKING(proxysock); pp = new_proxy(proxysock); +#if defined(USE_IPV6) + pp->peer_port = + ss_getport((struct sockaddr *)&iserv_addr, length); +#else pp->peer_port = ntohs(iserv_addr.sin_port); +#endif DEBUGF(("-----------------------------------\n")); DEBUGF(("[PROXY_LISTEN_SOCK] conn accepted: " "proxyfd = %d, " @@ -494,9 +517,14 @@ * reply = {cmd(1), fd(4), port(2), * ipstring(N), 0(1)} */ +#if defined(USE_IPV6) + reply_sockaddr(ESOCK_GETPEERNAME_REP, fd, + (struct sockaddr*)&iserv_addr, length); +#else reply(ESOCK_GETPEERNAME_REP, "42s", fd, ntohs(iserv_addr.sin_port), inet_ntoa(iserv_addr.sin_addr)); +#endif } break; @@ -520,9 +548,14 @@ * reply = {cmd(1), fd(4), port(2), * ipstring(N), 0(1)} */ +#if defined(USE_IPV6) + reply_sockaddr(ESOCK_GETSOCKNAME_REP, fd, + (struct sockaddr*)&iserv_addr, length); +#else reply(ESOCK_GETSOCKNAME_REP, "42s", fd, ntohs(iserv_addr.sin_port), inet_ntoa(iserv_addr.sin_addr)); +#endif } break; @@ -698,8 +731,7 @@ } DEBUGF(("-> PASSIVE_LISTENING (fd = %d)\n", listensock)); /* Publish listensock */ - reply(ESOCK_LISTEN_REP, "442", intref, listensock, - ntohs(iserv_addr.sin_port)); + reply(ESOCK_LISTEN_REP, "442", intref, listensock, lport); break; case ESOCK_ACCEPT_CMD: @@ -1481,6 +1513,11 @@ struct sockaddr_in sock_addr; long inaddr; FD fd; + +#if defined(USE_IPV6) + if (strchr(fipstring, ':')) + return do_connect6(lipstring, lport, fipstring, fport); +#endif if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_FD) { DEBUGF(("Error calling socket()\n")); @@ -1538,9 +1575,14 @@ static int one = 1; /* Type must be int, not long */ struct sockaddr_in sock_addr; long inaddr; - int length; + unsigned int length; FD fd; +#if defined(USE_IPV6) + if (strchr(ipstring, ':')) + return do_listen6(ipstring, lport, backlog, aport); +#endif + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_FD) { DEBUGF(("Error calling socket()\n")); return fd; @@ -1596,6 +1638,145 @@ return fd; } +#if defined(USE_IPV6) +static FD do_connect6(char *lipstring, int lport, char *fipstring, int fport) +{ + struct sockaddr_in6 sock_addr; + FD fd; + + if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) == INVALID_FD) { + DEBUGF(("Error calling socket()\n")); + return fd; + } + if (check_num_sock_fds(fd) < 0) + return INVALID_FD; + DEBUGF((" fd = %d\n", fd)); + + memset(&sock_addr, 0, sizeof(sock_addr)); + /* local */ + if (inet_pton(AF_INET6, lipstring, &sock_addr.sin6_addr) <= 0) { + DEBUGF(("Error in inet_pton(): lipstring = %s\n", lipstring)); + safe_close(fd); + sock_set_errno(ERRNO_ADDRNOTAVAIL); + return INVALID_FD; + } + sock_addr.sin6_family = AF_INET6; + sock_addr.sin6_port = htons(lport); + if(bind(fd, (struct sockaddr*) &sock_addr, sizeof(sock_addr)) < 0) { + DEBUGF(("Error in bind()\n")); + safe_close(fd); + /* XXX Set error code for bind error */ + return INVALID_FD; + } + + /* foreign */ + memset(&sock_addr, 0, sizeof(sock_addr)); + if (inet_pton(AF_INET6, fipstring, &sock_addr.sin6_addr) <= 0) { + DEBUGF(("Error in inet_pton(): fipstring = %s\n", fipstring)); + safe_close(fd); + sock_set_errno(ERRNO_ADDRNOTAVAIL); + return INVALID_FD; + } + sock_addr.sin6_family = AF_INET6; + sock_addr.sin6_port = htons(fport); + + SET_NONBLOCKING(fd); + + if(connect(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) { + if (sock_errno() != ERRNO_PROGRESS && /* UNIX */ + sock_errno() != ERRNO_BLOCK) { /* WIN32 */ + DEBUGF(("Error in connect()\n")); + safe_close(fd); + return INVALID_FD; + } + } + return fd; +} + +static FD do_listen6(char *ipstring, int lport, int backlog, int *aport) +{ + static int one = 1; /* Type must be int, not long */ + struct sockaddr_in6 sock_addr; + socklen_t length; + FD fd; + + if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) == INVALID_FD) { + DEBUGF(("Error calling socket()\n")); + return fd; + } + if (check_num_sock_fds(fd) < 0) + return INVALID_FD; + DEBUGF((" fd = %d\n", fd)); + memset(&sock_addr, 0, sizeof(sock_addr)); + if (inet_pton(AF_INET6, ipstring, &sock_addr.sin6_addr) <= 0) { + DEBUGF(("Error in inet_pton(): ipstring = %s\n", ipstring)); + safe_close(fd); + sock_set_errno(ERRNO_ADDRNOTAVAIL); + return INVALID_FD; + } + sock_addr.sin6_family = AF_INET6; + sock_addr.sin6_port = htons(lport); + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); + + if(bind(fd, (struct sockaddr*) &sock_addr, sizeof(sock_addr)) < 0) { + DEBUGF(("Error in bind()\n")); + safe_close(fd); + return INVALID_FD; + } + if (listen(fd, backlog) < 0) { + DEBUGF(("Error in listen()\n")); + safe_close(fd); + return INVALID_FD; + } + /* find out assigned local port number */ + length = sizeof(sock_addr); + if (getsockname(fd, (struct sockaddr *)&sock_addr, &length) < 0) { + DEBUGF(("Error in getsockname()\n")); + safe_close(fd); + return INVALID_FD; + } + if (aport) + *aport = ntohs(sock_addr.sin6_port); + return fd; +} + +static int reply_sockaddr(int cmd, int fd, const struct sockaddr *sa, + socklen_t size) +{ + char addr[INET6_ADDRSTRLEN+1] = ""; + char port[10] = ""; + int res; + + res = getnameinfo(sa, size, + addr, sizeof(addr), port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV); + if (res) { + if (res != EAI_SYSTEM) { + sock_set_errno(ERRNO_INVAL); + } + return reply(cmd, "4s", fd, psx_errstr()); + } else { + return reply(cmd, "42s", fd, atoi(port), addr); + } +} + +static int ss_getport(const struct sockaddr *sa, socklen_t size) +{ + char port[10] = ""; + int res; + + res = getnameinfo(sa, size, + NULL, 0, port, sizeof(port), NI_NUMERICSERV); + if (res) { + return -1; + } else { + return atoi(port); + } +} +#endif + + static Connection *new_connection(int state, FD fd) { Connection *cp; diff -ur erlang-11.b.2.orig/lib/ssl/src/ssl_broker.erl erlang-11.b.2/lib/ssl/src/ssl_broker.erl --- erlang-11.b.2.orig/lib/ssl/src/ssl_broker.erl 2006-05-03 10:18:30.000000000 +0200 +++ erlang-11.b.2/lib/ssl/src/ssl_broker.erl 2007-03-04 12:42:33.000000000 +0100 @@ -441,7 +441,7 @@ debug(St, "peername: client = ~w~n", [Client]), Reply = case ssl_server:peername(St#st.fd) of {ok, {Address, Port}} -> - {ok, At} = inet_parse:ipv4_address(Address), + {ok, At} = inet_parse:address(Address), {ok, {At, Port}}; Error -> Error @@ -498,7 +498,7 @@ debug(St, "sockname: client = ~w~n", [Client]), Reply = case ssl_server:sockname(St#st.fd) of {ok, {Address, Port}} -> - {ok, At} = inet_parse:ipv4_address(Address), + {ok, At} = inet_parse:address(Address), {ok, {At, Port}}; Error -> Error @@ -661,7 +661,8 @@ SSLOpts = get_ssl_opts(Opts), FlagStr =mk_ssl_optstr(SSLOpts), BackLog = get_backlog(LOpts), - IP = get_ip(LOpts), + Family = get_family(Opts), + IP = get_ip(LOpts, Family), case ssl_server:listen_prim(ServerName, IP, Port, FlagStr, BackLog) of {ok, ListenFd, _Port0} -> ThisSocket = #sslsocket{fd = ListenFd, pid = self()}, @@ -684,10 +685,11 @@ COpts = get_tcp_connect_opts(Opts), SSLOpts = get_ssl_opts(Opts), FlagStr = mk_ssl_optstr(SSLOpts), - case inet:getaddr(FAddress, inet) of + Family = get_family(Opts), + case inet:getaddr(FAddress, Family) of {ok, FIP} -> %% Timeout is gen_server timeout - hence catch - LIP = get_ip(COpts), + LIP = get_ip(COpts, Family), LPort = get_port(COpts), case (catch ssl_server:connect_prim(ServerName, LIP, LPort, FIP, FPort, @@ -945,8 +947,13 @@ get_backlog(Opts) -> get_tagged_opt(backlog, Opts, ?DEF_BACKLOG). -get_ip(Opts) -> - get_tagged_opt(ip, Opts, {0, 0, 0, 0}). +get_ip(Opts, Family) -> + DefaultIp = + case Family of + inet -> {0, 0, 0, 0}; + inet6 -> {0, 0, 0, 0, 0, 0, 0, 0} + end, + get_tagged_opt(ip, Opts, DefaultIp). get_port(Opts) -> get_tagged_opt(port, Opts, 0). @@ -954,6 +961,9 @@ get_nodelay(Opts) -> get_tagged_opt(nodelay, Opts, empty). +get_family(Opts) -> + get_tagged_opt(family, transform_opts(Opts), inet). + %% %% add_default_*_opts(Opts) -> NOpts %% @@ -1002,6 +1012,8 @@ transform_opt(binary) -> [{mode, binary}]; transform_opt(list) -> [{mode, list}]; transform_opt({packet, raw}) -> [{packet, 0}]; +transform_opt(inet) -> [{family, inet}]; +transform_opt(inet6) -> [{family, inet6}]; transform_opt(raw) -> []; transform_opt(Opt) -> [Opt]. @@ -1009,10 +1021,10 @@ %% only. is_connect_opt(Opt) -> - is_tcp_connect_opt(Opt) or is_ssl_opt(Opt). + is_tcp_connect_opt(Opt) or is_ssl_opt(Opt) or is_family_opt(Opt). is_listen_opt(Opt) -> - is_tcp_listen_opt(Opt) or is_ssl_opt(Opt). + is_tcp_listen_opt(Opt) or is_ssl_opt(Opt) or is_family_opt(Opt). is_tcp_accept_opt(Opt) -> is_tcp_gen_opt(Opt). @@ -1064,6 +1076,10 @@ is_ssl_opt({cachetimeout, Timeout}) when Timeout >= 0 -> true; is_ssl_opt(_Opt) -> false. +is_family_opt({family, inet}) -> true; +is_family_opt({family, inet6}) -> true; +is_family_opt(_Opt) -> false. + %% Various types is_string(String) when is_list(String) -> lists:all(fun (C) when is_integer(C), 0 =< C, C =< 255 -> true; @@ -1074,11 +1090,20 @@ is_ip_address(Addr) when is_tuple(Addr), size(Addr) == 4 -> is_string(tuple_to_list(Addr)); +is_ip_address(Addr) when is_tuple(Addr), size(Addr) == 8 -> + is_ip6_string(tuple_to_list(Addr)); is_ip_address(Addr) when is_list(Addr) -> is_string(Addr); is_ip_address(_) -> false. +is_ip6_string(String) when is_list(String) -> + lists:all(fun (C) when is_integer(C), 0 =< C, C =< 65535 -> true; + (_C) -> false end, + String); +is_ip6_string(_) -> + false. + get_tagged_opt(Tag, Opts, Default) -> case lists:keysearch(Tag, 1, Opts) of {value, {_, Value}} -> diff -ur erlang-11.b.2.orig/lib/ssl/src/ssl_prim.erl erlang-11.b.2/lib/ssl/src/ssl_prim.erl --- erlang-11.b.2.orig/lib/ssl/src/ssl_prim.erl 2006-05-03 10:19:24.000000000 +0200 +++ erlang-11.b.2/lib/ssl/src/ssl_prim.erl 2007-03-04 12:42:33.000000000 +0100 @@ -107,7 +107,7 @@ peername(St) when record(St, st), St#st.status =:= open -> case ssl_server:peername_prim(ssl_server_prim, St#st.fd) of {ok, {Address, Port}} -> - {ok, At} = inet_parse:ipv4_address(Address), + {ok, At} = inet_parse:address(Address), {ok, {At, Port}}; Error -> Error @@ -119,7 +119,7 @@ sockname(St) when record(St, st), St#st.status =:= open -> case ssl_server:sockname_prim(ssl_server_prim, St#st.fd) of {ok, {Address, Port}} -> - {ok, At} = inet_parse:ipv4_address(Address), + {ok, At} = inet_parse:address(Address), {ok, {At, Port}}; Error -> Error diff -ur erlang-11.b.2.orig/lib/ssl/src/ssl_server.erl erlang-11.b.2/lib/ssl/src/ssl_server.erl --- erlang-11.b.2.orig/lib/ssl/src/ssl_server.erl 2006-05-03 10:18:31.000000000 +0200 +++ erlang-11.b.2/lib/ssl/src/ssl_server.erl 2007-03-04 12:42:33.000000000 +0100 @@ -1227,7 +1227,10 @@ ip_to_string({A,B,C,D}) -> [integer_to_list(A),$.,integer_to_list(B),$., - integer_to_list(C),$.,integer_to_list(D)]. + integer_to_list(C),$.,integer_to_list(D)]; + +ip_to_string(Addr) when is_tuple(Addr), size(Addr) == 8 -> + inet_parse:ntoa(Addr). debug(St, Format, Args) -> debug1(St#st.debug, Format, Args).