From 16e5a903a45f1132e431f1770ab8d599a4127283 Mon Sep 17 00:00:00 2001 From: Bertrand Jacquin Date: Sat, 8 Mar 2008 16:27:47 +0100 Subject: last openvpn release with ipv6 support --- net-misc/openvpn/Manifest | 8 + net-misc/openvpn/files/down.sh | 33 + net-misc/openvpn/files/openvpn-2.1.conf | 18 + net-misc/openvpn/files/openvpn-2.1.init | 131 ++ net-misc/openvpn/files/openvpn-2.1_rc6-udp6.patch | 2521 +++++++++++++++++++++ net-misc/openvpn/files/openvpn-2.1_rc7-tap.patch | 57 + net-misc/openvpn/files/up.sh | 82 + net-misc/openvpn/openvpn-2.1_rc7.ebuild | 156 ++ 8 files changed, 3006 insertions(+) create mode 100644 net-misc/openvpn/Manifest create mode 100755 net-misc/openvpn/files/down.sh create mode 100644 net-misc/openvpn/files/openvpn-2.1.conf create mode 100755 net-misc/openvpn/files/openvpn-2.1.init create mode 100644 net-misc/openvpn/files/openvpn-2.1_rc6-udp6.patch create mode 100644 net-misc/openvpn/files/openvpn-2.1_rc7-tap.patch create mode 100755 net-misc/openvpn/files/up.sh create mode 100644 net-misc/openvpn/openvpn-2.1_rc7.ebuild (limited to 'net-misc') diff --git a/net-misc/openvpn/Manifest b/net-misc/openvpn/Manifest new file mode 100644 index 00000000..a6d3345b --- /dev/null +++ b/net-misc/openvpn/Manifest @@ -0,0 +1,8 @@ +AUX down.sh 943 RMD160 db43a525c9eb2ccb538e938e0b7f4359af22e4de SHA1 261acc68a24108526345a7d117bba15dbcebaa6e SHA256 39debebcd8c899f20e6d355cbc8eaab46e28b83a9f6c33a94c065688a4f3d2c7 +AUX openvpn-2.1.conf 892 RMD160 687a747ed2f801b051438d02da8fcd44c6954484 SHA1 e65db7d972483c9391ef92d2931d9db7b69e4329 SHA256 330149a83684ddabe413d134d4c8efad4c88b18c2ab67165014deff5f7fffad2 +AUX openvpn-2.1.init 4084 RMD160 1f815a77c3d89ee55d568e8dce582020fd68fc39 SHA1 811db5279b360f8d95bfbcd460a85bbf2c65ecbf SHA256 d844103cd67717ce505e191a3b4a1835b7fafbb856de7540791cf0e5fe017ec9 +AUX openvpn-2.1_rc6-udp6.patch 76842 RMD160 533ca831972ec759b39b1d0f94380091d2214242 SHA1 83caf4224d06d4ccd26e990fe0e2388e4a0e600a SHA256 0886a848f7fe52d0394bf51da814f9772ada6c14c682396a8e36085c1cd99c48 +AUX openvpn-2.1_rc7-tap.patch 1474 RMD160 a200e42f1fd224e9d0f02eb59ab6371224245715 SHA1 1453666b79208d528880b95e324900605172a420 SHA256 f26a2ccad67a23c863241ca64c110c36601d70a0e6e0d6d626c02d176c36a2db +AUX up.sh 2560 RMD160 34c2f0845a2c3b2eebb20da5a5c316e81cd1ef5a SHA1 d7026eeb7586fb931e5f938ca00fcc63c63923ad SHA256 3a7cedba5909fa06d0eb36b3426f0e5bc21bdc28943beaaa241ab651b4388218 +DIST openvpn-2.1_rc7.tar.gz 787379 RMD160 67e80d7bfb554046c71c95ec15d478504057f903 SHA1 f9f57a8ef3af8ab51924784ad2aab5d5f5798581 SHA256 c07fcbc41465bf508caff70dcc4238b8c95e9b2f35bec7bf891f7164027ecbf8 +EBUILD openvpn-2.1_rc7.ebuild 4472 RMD160 8305d69c254db1c55edaad7459169f31c5b5f7b3 SHA1 0868cf478f1ff2cf32f0354a72af6e1026ad6e53 SHA256 c513ac09ccf925b04c91609a1fe588f4c5bbebfb1dead66f0c22ea4f759b80f3 diff --git a/net-misc/openvpn/files/down.sh b/net-misc/openvpn/files/down.sh new file mode 100755 index 00000000..1c70db0e --- /dev/null +++ b/net-misc/openvpn/files/down.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# Copyright (c) 2006-2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# Contributed by Roy Marples (uberlord@gentoo.org) + +# If we have a service specific script, run this now +if [ -x /etc/openvpn/"${SVCNAME}"-down.sh ] ; then + /etc/openvpn/"${SVCNAME}"-down.sh "$@" +fi + +# Restore resolv.conf to how it was +if [ "${PEER_DNS}" != "no" ]; then + if [ -x /sbin/resolvconf ] ; then + /sbin/resolvconf -d "${dev}" + elif [ -e /etc/resolv.conf-"${dev}".sv ] ; then + # Important that we copy instead of move incase resolv.conf is + # a symlink and not an actual file + cp /etc/resolv.conf-"${dev}".sv /etc/resolv.conf + rm -f /etc/resolv.conf-"${dev}".sv + fi +fi + +if [ -n "${SVCNAME}" ]; then + # Re-enter the init script to start any dependant services + if /etc/init.d/"${SVCNAME}" --quiet status ; then + export IN_BACKGROUND=true + /etc/init.d/"${SVCNAME}" --quiet stop + fi +fi + +exit 0 + +# vim: ts=4 : diff --git a/net-misc/openvpn/files/openvpn-2.1.conf b/net-misc/openvpn/files/openvpn-2.1.conf new file mode 100644 index 00000000..72510c34 --- /dev/null +++ b/net-misc/openvpn/files/openvpn-2.1.conf @@ -0,0 +1,18 @@ +# OpenVPN automatically creates an /etc/resolv.conf (or sends it to +# resolvconf) if given DNS information by the OpenVPN server. +# Set PEER_DNS="no" to stop this. +PEER_DNS="yes" + +# OpenVPN can run in many modes. Most people will want the init script +# to automatically detect the mode and try and apply a good default +# configuration and setup scripts. However, there are cases where the +# OpenVPN configuration looks like a client, but it's really a peer or +# something else. DETECT_CLIENT controls this behaviour. +DETECT_CLIENT="yes" + +# If DETECT_CLIENT is no and you have your own scripts to re-enter the openvpn +# init script (ie, it first becomes "inactive" and the script then starts the +# script again to make it "started") then you can state this below. +# In other words, unless you understand service dependencies and are a +# competent shell scripter, don't set this. +RE_ENTER="no" diff --git a/net-misc/openvpn/files/openvpn-2.1.init b/net-misc/openvpn/files/openvpn-2.1.init new file mode 100755 index 00000000..633cae00 --- /dev/null +++ b/net-misc/openvpn/files/openvpn-2.1.init @@ -0,0 +1,131 @@ +#!/sbin/runscript +# Copyright 1999-2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +VPNDIR=${VPNDIR:-/etc/openvpn} +VPN=${SVCNAME#*.} +if [ -n "${VPN}" ] && [ ${SVCNAME} != "openvpn" ]; then + VPNPID="/var/run/openvpn.${VPN}.pid" +else + VPNPID="/var/run/openvpn.pid" +fi +VPNCONF="${VPNDIR}/${VPN}.conf" + +depend() { + need localmount net + use dns + after bootmisc +} + +checkconfig() { + # Linux has good dynamic tun/tap creation + if [ $(uname -s) = "Linux" ] ; then + if [ ! -e /dev/net/tun ]; then + if ! modprobe tun ; then + eerror "TUN/TAP support is not available" \ + "in this kernel" + return 1 + fi + fi + if [ -h /dev/net/tun ] && [ -c /dev/misc/net/tun ]; then + ebegin "Detected broken /dev/net/tun symlink, fixing..." + rm -f /dev/net/tun + ln -s /dev/misc/net/tun /dev/net/tun + eend $? + fi + return 0 + fi + + # Other OS's don't, so we rely on a pre-configured interface + # per vpn instance + local ifname=$(sed -n -e 's/[[:space:]]*dev[[:space:]][[:space:]]*\([^[:space:]]*\).*/\1/p' "${VPNCONF}") + if [ -z ${ifname} ] ; then + eerror "You need to specify the interface that this openvpn" \ + "instance should use" \ + "by using the dev option in ${VPNCONF}" + return 1 + fi + + if ! ifconfig "${ifname}" >/dev/null 2>/dev/null ; then + # Try and create it + echo > /dev/"${ifname}" >/dev/null + fi + if ! ifconfig "${ifname}" >/dev/null 2>/dev/null ; then + eerror "${VPNCONF} requires interface ${ifname}" \ + "but that does not exist" + return 1 + fi +} + +start() { + # If we are re-called by the openvpn gentoo-up.sh script + # then we don't actually want to start openvpn + [ "${IN_BACKGROUND}" = "true" ] && return 0 + + ebegin "Starting ${SVCNAME}" + + checkconfig || return 1 + + local args="" reenter=${RE_ENTER:-no} + # If the config file does not specify the cd option, we do + # But if we specify it, we override the config option which we do not want + if ! grep -q "^[ \t]*cd[ \t].*" "${VPNCONF}" ; then + args="${args} --cd ${VPNDIR}" + fi + + # We mark the service as inactive and then start it. + # When we get an authenticated packet from the peer then we run our script + # which configures our DNS if any and marks us as up. + if [ "${DETECT_CLIENT:-yes}" = "yes" ] && \ + grep -q "^[ \t]*remote[ \t].*" "${VPNCONF}" ; then + reenter="yes" + args="${args} --nobind --up-delay --up-restart" + args="${args} --up /etc/openvpn/up.sh" + args="${args} --down-pre --down /etc/openvpn/down.sh" + + # Warn about setting scripts as we override them + if grep -Eq "^[ \t]*(up|down)[ \t].*" "${VPNCONF}" ; then + ewarn "WARNING: You have defined your own up/down scripts" + ewarn "As you're running as a client, we now force Gentoo specific" + ewarn "scripts to be run for up and down events." + ewarn "These scripts will call /etc/openvpn/${SVCNAME}-{up,down}.sh" + ewarn "where you can put your own code." + fi + + # Warn about the inability to change ip/route/dns information when + # dropping privs + if grep -q "^[ \t]*user[ \t].*" "${VPNCONF}" ; then + ewarn "WARNING: You are dropping root privileges!" + ewarn "As such openvpn may not be able to change ip, routing" + ewarn "or DNS configuration." + fi + else + # So we're a server. Run as openvpn unless otherwise specified + grep -q "^[ \t]*user[ \t].*" "${VPNCONF}" || args="${args} --user openvpn" + grep -q "^[ \t]*group[ \t].*" "${VPNCONF}" || args="${args} --group openvpn" + fi + + # Ensure that our scripts get the PEER_DNS variable + export PEER_DNS + + [ "${reenter}" = "yes" ] && mark_service_inactive "${SVCNAME}" + start-stop-daemon --start --exec /usr/sbin/openvpn --pidfile "${VPNPID}" \ + -- --config "${VPNCONF}" --writepid "${VPNPID}" --daemon ${args} + eend $? "Check your logs to see why startup failed" +} + +stop() { + # If we are re-called by the openvpn gentoo-down.sh script + # then we don't actually want to stop openvpn + if [ "${IN_BACKGROUND}" = "true" ] ; then + mark_service_inactive "${SVCNAME}" + return 0 + fi + + ebegin "Stopping ${SVCNAME}" + start-stop-daemon --stop --quiet \ + --exec /usr/sbin/openvpn --pidfile "${VPNPID}" + eend $? +} + +# vim: set ts=4 : diff --git a/net-misc/openvpn/files/openvpn-2.1_rc6-udp6.patch b/net-misc/openvpn/files/openvpn-2.1_rc6-udp6.patch new file mode 100644 index 00000000..842839e5 --- /dev/null +++ b/net-misc/openvpn/files/openvpn-2.1_rc6-udp6.patch @@ -0,0 +1,2521 @@ +diff --git a/jjo-tests/libtest.sh b/jjo-tests/libtest.sh +new file mode 100755 +index 0000000..ab0d4b5 +--- /dev/null ++++ b/jjo-tests/libtest.sh +@@ -0,0 +1,115 @@ ++#!/bin/bash ++ ++typeset -i test_num=0 # increments w/each test_define ++typeset test_msg # prefix: "test nr#" for msgs ++typeset test_name # test name given ++typeset test_sanename # test name with all whitespace (and alike) replaced by '_' ++ ++TEST_CLEANUP=":" ++ ++say () { ++ echo "$@" >&3 ++} ++err () { ++ echo "$@" >&4 ++} ++debug () { ++ echo "$@" >&4 ++} ++test_define() { ++ test_name="$*" ++ test_sanename="$(echo -n $test_name| tr -c '[A-Za-z0-9._]' _ )" ++ test_num=test_num+1 ++ test_msg="test $test_num" ++ test_bg_cleanup ++} ++test_expect_success () { ++ local msg="$1" ++ test $# -ge 2 || { err "usage error: test_expect_failure msg cmd args ..."; return 1; } ++ say -n "$test_msg: -- $msg (expecting success) " ++ shift ++ (eval "$@" 4>&1) && { say -e "\n$test_msg: OK %%% $test_sanename" ; return 0 ; } ++ say -e "\n$test_msg: FAILED %%% $test_sanename" ++ return 1 ++} ++ ++test_expect_failure () { ++ local msg="$1" ++ test $# -ge 2 || { err "usage error: test_expect_failure msg cmd args ..."; return 1;} ++ say -n "$test_msg: -- $msg (expecting failure) " ++ shift ++ (eval "$@") && { say -e "\n$test_msg: FAILED %%% $test_sanename" >&3 ; return 0 ; } ++ say -e "\n$test_msg: OK %%% $test_sanename" ++ return 1 ++} ++ ++test_bg_egrep() { ++ local nsecs="$1" ++ local txt="$2" ++ local ret ++ shift 2 ++ test_expect_success "$test_name" \ ++ "set -m ; $@ &> $t/out-$test_sanename & ++ s=1; ++ for i in \$(seq 1 $nsecs);do ++ say -n '.' ++ kill -0 %1 || { egrep failed $t/out-$test_sanename >&4; break; } ++ o=\$(egrep \"$txt\" $t/out-$test_sanename ) && { debug \$o; s=0; break; } ++ sleep 1; ++ done; ++ kill %1 ++ wait ++ exit \$s ++ " 2>/dev/null 4>$t/err ++ ret=$? ++ test $ret -eq 0 && return 0 ++ say "$test_msg: see: $t/out-$test_sanename*" ++ return $ret ++} ++# run command in *current* shell ignoring stderr, evals args passed ++quiet2() { ++ #backup fd=2 ++ exec 250>&2 ++ exec 2>/dev/null ++ eval "$@" ++ #close auxfd ++ exec 2>&250 ++ exec 250>&- ++} ++test_bg_prev(){ local out=$t/out-$test_sanename-prev; $@ >& $out & } ++test_bg_cleanup() { quiet2 "$TEST_CLEANUP;${@:-:};kill % 2>/dev/null;wait" ;} ++test_set_cleanup () { TEST_CLEANUP="${*:-:}" ;} ++ ++t=/tmp ++exec 3>&1 ++exec 4>&2 ++ ++#========= ++ ++ln -sf $PWD/openvpn $t/openvpn-test ++export OPENVPN=$t/openvpn-test ++ ++test_set_cleanup "killall $OPENVPN" ++ ++trap 'test_bg_cleanup;exit' 0 2 15 ++ ++test_define "UDP6 loopback" ++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-udp6-0-loopback-self.sh ++ ++test_define "UDP6 loopback byname" ++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-udp6-0-loopback-byname.sh ++ ++test_define "TCP6 loopback" ++test_bg_prev ../jjo-tests/run-tcp6-0-loopback-server.sh ++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-tcp6-0-loopback-client.sh ++ ++test_define "TCP6 loopback byname" ++test_bg_prev ../jjo-tests/run-tcp6-0-loopback-server-byname.sh ++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-tcp6-0-loopback-client-byname.sh ++ ++test_define "UDP4 loopback" ++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-udp4-0-loopback-self.sh ++ ++test_define "TCP4 loopback" ++test_bg_prev ../jjo-tests/run-tcp4-0-loopback-server.sh ++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-tcp4-0-loopback-client.sh +diff --git a/jjo-tests/qemu-ifup-2.jjo.sh b/jjo-tests/qemu-ifup-2.jjo.sh +new file mode 100755 +index 0000000..60418ad +--- /dev/null ++++ b/jjo-tests/qemu-ifup-2.jjo.sh +@@ -0,0 +1,12 @@ ++#!/bin/sh -x ++#sudo sh -c "/sbin/ifconfig $1 hw ether 00:00:00:00:00:01; /sbin/ifconfig $1 192.168.254.1" ++export PATH=/sbin:/usr/sbin:$PATH ++NUM=2 ++DEV=qemutun$NUM ++sudo sh -x < 0) ++ { ++ strncpynt ((char *)ptr,str, cap); ++ *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */ ++ buf->len += (int) strlen ((char *)ptr); ++ } ++} ++ ++ + /* + * This is necessary due to certain buggy implementations of snprintf, + * that don't guarantee null termination for size > 0. +diff --git a/openvpn/configure.ac b/openvpn/configure.ac +index 8843a2c..8cea204 100644 +--- a/openvpn/configure.ac ++++ b/openvpn/configure.ac +@@ -101,6 +101,12 @@ AC_ARG_ENABLE(multihome, + [MULTIHOME="yes"] + ) + ++AC_ARG_ENABLE(ipv6, ++ [ --disable-ipv6 Disable UDP/IPv6 support], ++ [PF_INET6="$enableval"], ++ [PF_INET6="yes"] ++) ++ + AC_ARG_ENABLE(port-share, + [ --disable-port-share Disable TCP server port-share support (--port-share)], + [PORT_SHARE="$enableval"], +@@ -406,6 +412,16 @@ LDFLAGS="$LDFLAGS -Wl,--fatal-warnings" + AC_CHECK_FUNCS(epoll_create, AC_DEFINE([HAVE_EPOLL_CREATE], 1, [])) + LDFLAGS="$OLDLDFLAGS" + ++dnl ipv6 support ++if test "$PF_INET6" = "yes"; then ++ AC_CHECKING([for struct sockaddr_in6 for IPv6 support]) ++ AC_CHECK_TYPE( ++ [struct sockaddr_in6], ++ [AC_DEFINE(USE_PF_INET6, 1, [struct sockaddr_in6 is needed for IPv6 peer support])], ++ [], ++ [#include "syshead.h"]) ++fi ++ + dnl + dnl check for valgrind tool + dnl +diff --git a/openvpn/helper.c b/openvpn/helper.c +index d2b3d6f..459f00d 100644 +--- a/openvpn/helper.c ++++ b/openvpn/helper.c +@@ -358,6 +358,10 @@ #endif /* P2MP_SERVER */ + + if (o->proto == PROTO_TCPv4) + o->proto = PROTO_TCPv4_CLIENT; ++#ifdef USE_PF_INET6 ++ else if (o->proto == PROTO_TCPv6) ++ o->proto = PROTO_TCPv6_CLIENT; ++#endif + } + + #endif /* P2MP */ +diff --git a/openvpn/init.c b/openvpn/init.c +index 244f03c..0d929e4 100644 +--- a/openvpn/init.c ++++ b/openvpn/init.c +@@ -703,7 +703,7 @@ #ifdef ENABLE_MANAGEMENT + const char *detail = "SUCCESS"; + if (c->c1.tuntap) + tun_local = c->c1.tuntap->local; +- tun_remote = htonl (c->c1.link_socket_addr.actual.dest.sa.sin_addr.s_addr); ++ tun_remote = htonl (c->c1.link_socket_addr.actual.dest.addr.in4.sin_addr.s_addr); + if (flags & ISC_ERRORS) + detail = "ERROR"; + management_set_state (management, +@@ -1105,7 +1105,7 @@ do_deferred_options (struct context *c, + #ifdef ENABLE_OCC + if (found & OPT_P_EXPLICIT_NOTIFY) + { +- if (c->options.proto != PROTO_UDPv4 && c->options.explicit_exit_notification) ++ if (!proto_is_udp(c->options.proto) && c->options.explicit_exit_notification) + { + msg (D_PUSH, "OPTIONS IMPORT: --explicit-exit-notify can only be used with --proto udp"); + c->options.explicit_exit_notification = 0; +@@ -1200,12 +1200,21 @@ #endif + switch (c->options.proto) + { + case PROTO_UDPv4: ++#ifdef USE_PF_INET6 ++ case PROTO_UDPv6: ++#endif + if (proxy) + sec = c->options.connect_retry_seconds; + break; ++#ifdef USE_PF_INET6 ++ case PROTO_TCPv6_SERVER: ++#endif + case PROTO_TCPv4_SERVER: + sec = 1; + break; ++#ifdef USE_PF_INET6 ++ case PROTO_TCPv6_CLIENT: ++#endif + case PROTO_TCPv4_CLIENT: + sec = c->options.connect_retry_seconds; + break; +@@ -2292,7 +2301,7 @@ do_setup_fast_io (struct context *c) + #ifdef WIN32 + msg (M_INFO, "NOTE: --fast-io is disabled since we are running on Windows"); + #else +- if (c->options.proto != PROTO_UDPv4) ++ if (!proto_is_udp(c->options.proto)) + msg (M_INFO, "NOTE: --fast-io is disabled since we are not using UDP"); + else + { +@@ -2549,7 +2558,11 @@ init_instance (struct context *c, const + /* link_socket_mode allows CM_CHILD_TCP + instances to inherit acceptable fds + from a top-level parent */ ++#ifdef USE_PF_INET6 ++ if (c->options.proto == PROTO_TCPv4_SERVER || c->options.proto == PROTO_TCPv6_SERVER) ++#else + if (c->options.proto == PROTO_TCPv4_SERVER) ++#endif + { + if (c->mode == CM_TOP) + link_socket_mode = LS_MODE_TCP_LISTEN; +@@ -2809,17 +2822,7 @@ inherit_context_child (struct context *d + { + CLEAR (*dest); + +- switch (src->options.proto) +- { +- case PROTO_UDPv4: +- dest->mode = CM_CHILD_UDP; +- break; +- case PROTO_TCPv4_SERVER: +- dest->mode = CM_CHILD_TCP; +- break; +- default: +- ASSERT (0); +- } ++ dest->mode = proto_is_dgram(src->options.proto)? CM_CHILD_UDP : CM_CHILD_TCP; + + dest->gc = gc_new (); + +@@ -2924,7 +2927,7 @@ #endif + dest->c2.buffers_owned = false; + + dest->c2.event_set = NULL; +- if (src->options.proto == PROTO_UDPv4) ++ if (proto_is_dgram(src->options.proto)) + do_event_set_init (dest, false); + } + +diff --git a/openvpn/manage.c b/openvpn/manage.c +index 993afa2..2615137 100644 +--- a/openvpn/manage.c ++++ b/openvpn/manage.c +@@ -1351,9 +1351,9 @@ man_settings_init (struct man_settings * + /* + * Initialize socket address + */ +- ms->local.sa.sin_family = AF_INET; +- ms->local.sa.sin_addr.s_addr = 0; +- ms->local.sa.sin_port = htons (port); ++ ms->local.addr.in4.sin_family = AF_INET; ++ ms->local.addr.in4.sin_addr.s_addr = 0; ++ ms->local.addr.in4.sin_port = htons (port); + + /* + * Run management over tunnel, or +@@ -1365,7 +1365,7 @@ man_settings_init (struct man_settings * + } + else + { +- ms->local.sa.sin_addr.s_addr = getaddr ++ ms->local.addr.in4.sin_addr.s_addr = getaddr + (GETADDR_RESOLVE|GETADDR_WARN_ON_SIGNAL|GETADDR_FATAL, addr, 0, NULL, NULL); + } + +@@ -1634,7 +1634,7 @@ management_post_tunnel_open (struct mana + && man->connection.state == MS_INITIAL) + { + /* listen on our local TUN/TAP IP address */ +- man->settings.local.sa.sin_addr.s_addr = htonl (tun_local_ip); ++ man->settings.local.addr.in4.sin_addr.s_addr = htonl (tun_local_ip); + man_connection_init (man); + } + +diff --git a/openvpn/mroute.c b/openvpn/mroute.c +index a7d1215..8d85518 100644 +--- a/openvpn/mroute.c ++++ b/openvpn/mroute.c +@@ -175,25 +175,47 @@ bool mroute_extract_openvpn_sockaddr (st + const struct openvpn_sockaddr *osaddr, + bool use_port) + { +- if (osaddr->sa.sin_family == AF_INET) ++ switch (osaddr->addr.sa.sa_family) ++ { ++ case AF_INET: + { + if (use_port) + { + addr->type = MR_ADDR_IPV4 | MR_WITH_PORT; + addr->netbits = 0; + addr->len = 6; +- memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4); +- memcpy (addr->addr + 4, &osaddr->sa.sin_port, 2); ++ memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4); ++ memcpy (addr->addr + 4, &osaddr->addr.in4.sin_port, 2); + } + else + { + addr->type = MR_ADDR_IPV4; + addr->netbits = 0; + addr->len = 4; +- memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4); ++ memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4); + } + return true; + } ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++ if (use_port) ++ { ++ addr->type = MR_ADDR_IPV6 | MR_WITH_PORT; ++ addr->netbits = 0; ++ addr->len = 18; ++ memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16); ++ memcpy (addr->addr + 16, &osaddr->addr.in6.sin6_port, 2); ++ } ++ else ++ { ++ addr->type = MR_ADDR_IPV6; ++ addr->netbits = 0; ++ addr->len = 16; ++ memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16); ++ } ++ return true; ++#endif ++ } + return false; + } + +@@ -268,7 +290,37 @@ mroute_addr_print (const struct mroute_a + } + break; + case MR_ADDR_IPV6: ++#ifdef USE_PF_INET6 ++ { ++ struct buffer buf; ++ struct sockaddr_in6 sin6; ++ int port; ++ char buf6[INET6_ADDRSTRLEN] = ""; ++ memset(&sin6, 0, sizeof sin6); ++ sin6.sin6_family = AF_INET6; ++ buf_set_read (&buf, maddr.addr, maddr.len); ++ if (buf_read(&buf, &sin6.sin6_addr, sizeof (sin6.sin6_addr))) ++ { ++ if (getnameinfo((struct sockaddr *)&sin6, sizeof (struct sockaddr_in6), ++ buf6, sizeof (buf6), NULL, 0, NI_NUMERICHOST) != 0) ++ { ++ buf_printf (&out, "MR_ADDR_IPV6 getnameinfo() err"); ++ break; ++ } ++ buf_puts (&out, buf6); ++ if (maddr.type & MR_WITH_NETBITS) ++ buf_printf (&out, "/%d", maddr.netbits); ++ if (maddr.type & MR_WITH_PORT) ++ { ++ port = buf_read_u16 (&buf); ++ if (port >= 0) ++ buf_printf (&out, ":%d", port); ++ } ++ } ++ } ++#else /* old pre IPV6 1-line code: */ + buf_printf (&out, "IPV6"); ++#endif + break; + default: + buf_printf (&out, "UNKNOWN"); +diff --git a/openvpn/mtcp.c b/openvpn/mtcp.c +index 6ec9974..5011688 100644 +--- a/openvpn/mtcp.c ++++ b/openvpn/mtcp.c +@@ -159,6 +159,7 @@ multi_tcp_instance_specific_init (struct + ASSERT (mi->context.c2.link_socket); + ASSERT (mi->context.c2.link_socket->info.lsa); + ASSERT (mi->context.c2.link_socket->mode == LS_MODE_TCP_ACCEPT_FROM); ++ ASSERT (mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET); + if (!mroute_extract_openvpn_sockaddr (&mi->real, &mi->context.c2.link_socket->info.lsa->actual.dest, true)) + { + msg (D_MULTI_ERRORS, "MULTI TCP: TCP client address is undefined"); +diff --git a/openvpn/multi.c b/openvpn/multi.c +index ee74a46..bfd79b3 100644 +--- a/openvpn/multi.c ++++ b/openvpn/multi.c +@@ -990,8 +990,8 @@ multi_learn_in_addr_t (struct multi_cont + struct mroute_addr addr; + + CLEAR (remote_si); +- remote_si.sa.sin_family = AF_INET; +- remote_si.sa.sin_addr.s_addr = htonl (a); ++ remote_si.addr.in4.sin_family = AF_INET; ++ remote_si.addr.in4.sin_addr.s_addr = htonl (a); + ASSERT (mroute_extract_openvpn_sockaddr (&addr, &remote_si, false)); + + if (netbits >= 0) +@@ -2224,9 +2224,9 @@ management_callback_kill_by_addr (void * + int count = 0; + + CLEAR (saddr); +- saddr.sa.sin_family = AF_INET; +- saddr.sa.sin_addr.s_addr = htonl (addr); +- saddr.sa.sin_port = htons (port); ++ saddr.addr.in4.sin_family = AF_INET; ++ saddr.addr.in4.sin_addr.s_addr = htonl (addr); ++ saddr.addr.in4.sin_port = htons (port); + if (mroute_extract_openvpn_sockaddr (&maddr, &saddr, true)) + { + hash_iterator_init (m->iter, &hi, true); +@@ -2287,18 +2287,13 @@ tunnel_server (struct context *top) + { + ASSERT (top->options.mode == MODE_SERVER); + +- switch (top->options.proto) { +- case PROTO_UDPv4: +- tunnel_server_udp (top); +- break; +- case PROTO_TCPv4_SERVER: +- tunnel_server_tcp (top); +- break; +- default: +- ASSERT (0); +- } ++ if (proto_is_dgram(top->options.proto)) ++ tunnel_server_udp(top); ++ else ++ tunnel_server_tcp(top); + } + ++ + #else + static void dummy(void) {} + #endif /* P2MP_SERVER */ +diff --git a/openvpn/occ.c b/openvpn/occ.c +index 6b136dc..3ec7784 100644 +--- a/openvpn/occ.c ++++ b/openvpn/occ.c +@@ -375,7 +375,7 @@ process_received_occ_msg (struct context + c->c2.max_send_size_remote, + c->c2.max_recv_size_local); + if (!c->options.fragment +- && c->options.proto == PROTO_UDPv4 ++ && (proto_is_dgram(c->options.proto)) + && c->c2.max_send_size_local > TUN_MTU_MIN + && (c->c2.max_recv_size_remote < c->c2.max_send_size_local + || c->c2.max_recv_size_local < c->c2.max_send_size_remote)) +diff --git a/openvpn/options.c b/openvpn/options.c +index 94da98e..1490009 100644 +--- a/openvpn/options.c ++++ b/openvpn/options.c +@@ -73,6 +73,12 @@ #endif + #ifdef USE_PTHREAD + " [PTHREAD]" + #endif ++#ifdef ENABLE_IP_PKTINFO ++ " [MH]" ++#endif ++#ifdef USE_PF_INET6 ++ " [PF_INET6]" ++#endif + " built on " __DATE__ + ; + +@@ -93,6 +99,9 @@ static const char usage_message[] = + "--mode m : Major mode, m = 'p2p' (default, point-to-point) or 'server'.\n" + "--proto p : Use protocol p for communicating with peer.\n" + " p = udp (default), tcp-server, or tcp-client\n" ++#ifdef USE_PF_INET6 ++ " p = udp6, tcp6-server, or tcp6-client (IPv6)\n" ++#endif + "--connect-retry n : For --proto tcp-client, number of seconds to wait\n" + " between connection retries (default=%d).\n" + "--connect-timeout n : For --proto tcp-client, connection timeout (in seconds).\n" +@@ -1421,10 +1430,10 @@ #endif + * Sanity check on TCP mode options + */ + +- if (options->connect_retry_defined && options->proto != PROTO_TCPv4_CLIENT) ++ if (options->connect_retry_defined && options->proto != PROTO_TCPv4_CLIENT && options->proto != PROTO_TCPv6_CLIENT) + msg (M_USAGE, "--connect-retry doesn't make sense unless also used with --proto tcp-client"); + +- if (options->connect_timeout_defined && options->proto != PROTO_TCPv4_CLIENT) ++ if (options->connect_timeout_defined && options->proto != PROTO_TCPv4_CLIENT && options->proto != PROTO_TCPv6_CLIENT) + msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with --proto tcp-client"); + + /* +@@ -1434,7 +1443,7 @@ #endif + msg (M_USAGE, "only one of --tun-mtu or --link-mtu may be defined (note that --ifconfig implies --link-mtu %d)", LINK_MTU_DEFAULT); + + #ifdef ENABLE_OCC +- if (options->proto != PROTO_UDPv4 && options->mtu_test) ++ if (!proto_is_udp(options->proto) && options->mtu_test) + msg (M_USAGE, "--mtu-test only makes sense with --proto udp"); + #endif + +@@ -1479,7 +1488,8 @@ #endif + const char *remote = l->array[i].hostname; + const int remote_port = l->array[i].port; + +- if (string_defined_equal (options->local, remote) ++ if (proto_is_net(options->proto) ++ && string_defined_equal (options->local, remote) + && options->local_port == remote_port) + msg (M_USAGE, "--remote and --local addresses are the same"); + +@@ -1508,7 +1518,7 @@ #endif + if (!options->remote_list && !options->bind_local) + msg (M_USAGE, "--nobind doesn't make sense unless used with --remote"); + +- if (options->proto == PROTO_TCPv4_CLIENT && !options->local && !options->local_port_defined && !options->bind_defined) ++ if ((options->proto == PROTO_TCPv4_CLIENT||options->proto == PROTO_TCPv6_CLIENT) && !options->local && !options->local_port_defined && !options->bind_defined) + options->bind_local = false; + + #ifdef ENABLE_SOCKS +@@ -1565,16 +1575,16 @@ #endif + */ + + #ifdef ENABLE_FRAGMENT +- if (options->proto != PROTO_UDPv4 && options->fragment) ++ if (!proto_is_udp(options->proto) && options->fragment) + msg (M_USAGE, "--fragment can only be used with --proto udp"); + #endif + + #ifdef ENABLE_OCC +- if (options->proto != PROTO_UDPv4 && options->explicit_exit_notification) ++ if (!proto_is_udp(options->proto) && options->explicit_exit_notification) + msg (M_USAGE, "--explicit-exit-notify can only be used with --proto udp"); + #endif + +- if (!options->remote_list && options->proto == PROTO_TCPv4_CLIENT) ++ if (!options->remote_list && (options->proto == PROTO_TCPv4_CLIENT||options->proto == PROTO_TCPv6_CLIENT)) + msg (M_USAGE, "--remote MUST be used in TCP Client mode"); + + #ifdef ENABLE_HTTP_PROXY +@@ -1592,7 +1602,8 @@ #ifdef ENABLE_SOCKS + msg (M_USAGE, "--socks-proxy can not be used in TCP Server mode"); + #endif + +- if (options->proto == PROTO_TCPv4_SERVER && remote_list_len (options->remote_list) > 1) ++ if ((options->proto == PROTO_TCPv4_SERVER||options->proto == PROTO_TCPv6_SERVER) ++ && remote_list_len (options->remote_list) > 1) + msg (M_USAGE, "TCP server mode allows at most one --remote address"); + + #if P2MP_SERVER +@@ -1617,7 +1628,7 @@ #endif + msg (M_USAGE, "--mode server only works with --dev tun or --dev tap"); + if (options->pull) + msg (M_USAGE, "--pull cannot be used with --mode server"); +- if (!(options->proto == PROTO_UDPv4 || options->proto == PROTO_TCPv4_SERVER)) ++ if (!(proto_is_udp(options->proto) || options->proto == PROTO_TCPv4_SERVER || options->proto == PROTO_TCPv6_SERVER)) + msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"); + #if PORT_SHARE + if ((options->port_share_host || options->port_share_port) && options->proto != PROTO_TCPv4_SERVER) +@@ -1645,9 +1656,9 @@ #endif + msg (M_USAGE, "--inetd cannot be used with --mode server"); + if (options->ipchange) + msg (M_USAGE, "--ipchange cannot be used with --mode server (use --client-connect instead)"); +- if (!(options->proto == PROTO_UDPv4 || options->proto == PROTO_TCPv4_SERVER)) +- msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"); +- if (options->proto != PROTO_UDPv4 && (options->cf_max || options->cf_per)) ++ if (!(proto_is_dgram(options->proto) || options->proto == PROTO_TCPv4_SERVER || options->proto == PROTO_TCPv6_SERVER )) ++ msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server (also udp6/tcp6)"); ++ if (!proto_is_udp(options->proto) && (options->cf_max || options->cf_per)) + msg (M_USAGE, "--connect-freq only works with --mode server --proto udp. Try --max-clients instead."); + if (!(dev == DEV_TYPE_TAP || (dev == DEV_TYPE_TUN && options->topology == TOP_SUBNET)) && options->ifconfig_pool_netmask) + msg (M_USAGE, "The third parameter to --ifconfig-pool (netmask) is only valid in --dev tap mode"); +@@ -1724,7 +1735,7 @@ #ifdef USE_CRYPTO + /* + * Check consistency of replay options + */ +- if ((options->proto != PROTO_UDPv4) ++ if ((!proto_is_udp(options->proto)) + && (options->replay_window != defaults.replay_window + || options->replay_time != defaults.replay_time)) + msg (M_USAGE, "--replay-window only makes sense with --proto udp"); +@@ -1913,7 +1924,7 @@ #if P2MP + */ + if (options->pull + && options->ping_rec_timeout_action == PING_UNDEF +- && options->proto == PROTO_UDPv4) ++ && proto_is_udp(options->proto)) + { + options->ping_rec_timeout = PRE_PULL_INITIAL_PING_RESTART; + options->ping_rec_timeout_action = PING_RESTART; +diff --git a/openvpn/ps.c b/openvpn/ps.c +index 49b1a2c..472c626 100644 +--- a/openvpn/ps.c ++++ b/openvpn/ps.c +@@ -337,9 +337,9 @@ sock_addr_set (struct openvpn_sockaddr * + const int port) + { + CLEAR (*osaddr); +- osaddr->sa.sin_family = AF_INET; +- osaddr->sa.sin_addr.s_addr = htonl (addr); +- osaddr->sa.sin_port = htons (port); ++ osaddr->addr.in4.sin_family = AF_INET; ++ osaddr->addr.in4.sin_addr.s_addr = htonl (addr); ++ osaddr->addr.in4.sin_port = htons (port); + } + + static inline void +diff --git a/openvpn/socket.c b/openvpn/socket.c +index 70d8ffd..3074267 100644 +--- a/openvpn/socket.c ++++ b/openvpn/socket.c +@@ -42,10 +42,16 @@ + #include "memdbg.h" + + const int proto_overhead[] = { /* indexed by PROTO_x */ +- IPv4_UDP_HEADER_SIZE, ++ 0, ++ IPv4_UDP_HEADER_SIZE, /* IPv4 */ + IPv4_TCP_HEADER_SIZE, + IPv4_TCP_HEADER_SIZE, +- IPv4_TCP_HEADER_SIZE ++#ifdef USE_PF_INET6 ++ IPv6_UDP_HEADER_SIZE, /* IPv6 */ ++ IPv6_TCP_HEADER_SIZE, ++ IPv6_TCP_HEADER_SIZE, ++ IPv6_TCP_HEADER_SIZE, ++#endif + }; + + /* +@@ -260,20 +266,49 @@ + struct openvpn_sockaddr *addr, + bool *changed) + { +- if (host && addr) +- { +- const in_addr_t new_addr = getaddr ( +- GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, +- host, +- 1, +- NULL, +- NULL); +- if (new_addr && addr->sa.sin_addr.s_addr != new_addr) ++ switch(addr->addr.sa.sa_family) { ++ case AF_INET: ++ if (host && addr) ++ { ++ const in_addr_t new_addr = getaddr ( ++ GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, ++ host, ++ 1, ++ NULL, ++ NULL); ++ if (new_addr && addr->addr.in4.sin_addr.s_addr != new_addr) + { +- addr->sa.sin_addr.s_addr = new_addr; ++ addr->addr.in4.sin_addr.s_addr = new_addr; + *changed = true; + } +- } ++ } ++ break; ++#ifdef USE_PF_INET6 ++ case AF_INET6: /* jjoFIXME: should adapt getaddr() for AF_INET6 */ ++ if (host && addr) ++ { ++ struct addrinfo hints , *ai; ++ int err; ++ memset(&hints, 0, sizeof hints); ++ hints.ai_flags=AI_PASSIVE; ++ hints.ai_family=AF_INET6; ++ if ((err=getaddrinfo(host, NULL, &hints, &ai))==0) ++ { ++ struct sockaddr_in6 *sin6=(struct sockaddr_in6*)ai->ai_addr; ++ if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &addr->addr.in6.sin6_addr)) ++ { ++ int port=addr->addr.in6.sin6_port; /* backup current port for easier copy, restore later */ ++ addr->addr.in6=*sin6; /* ipv6 requires also eg. sin6_scope_id => easy to full copy*/ ++ addr->addr.in6.sin6_port=port; ++ } ++ freeaddrinfo(ai); ++ } ++ } ++ break; ++#endif ++ default: ++ ASSERT(0); ++ } + } + + static int +@@ -531,6 +566,44 @@ + return sd; + } + ++#ifdef USE_PF_INET6 ++static socket_descriptor_t ++create_socket_udp6 (const unsigned int flags) ++{ ++ socket_descriptor_t sd; ++ ++ if ((sd = socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) ++ msg (M_SOCKERR, "UDP: Cannot create UDP6 socket"); ++#if ENABLE_IP_PKTINFO ++ else if (flags & SF_USE_IP_PKTINFO) ++ { ++ int pad = 1; ++ setsockopt (sd, IPPROTO_IPV6, IPV6_PKTINFO, (void*)&pad, sizeof(pad)); ++ } ++#endif ++ return sd; ++} ++ ++static socket_descriptor_t ++create_socket_tcp6 (void) ++{ ++ socket_descriptor_t sd; ++ ++ if ((sd = socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP)) < 0) ++ msg (M_SOCKERR, "Cannot create TCP6 socket"); ++ ++ /* set SO_REUSEADDR on socket */ ++ { ++ int on = 1; ++ if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, ++ (void *) &on, sizeof (on)) < 0) ++ msg (M_SOCKERR, "TCP: Cannot setsockopt SO_REUSEADDR on TCP6 socket"); ++ } ++ ++ return sd; ++} ++ ++#endif + static void + create_socket (struct link_socket *sock) + { +@@ -549,6 +622,17 @@ + { + sock->sd = create_socket_tcp (); + } ++#ifdef USE_PF_INET6 ++ else if (sock->info.proto == PROTO_TCPv6_SERVER ++ || sock->info.proto == PROTO_TCPv6_CLIENT) ++ { ++ sock->sd = create_socket_tcp6 (); ++ } ++ else if (sock->info.proto == PROTO_UDPv6) ++ { ++ sock->sd = create_socket_udp6 (sock->sockflags); ++ } ++#endif + else + { + ASSERT (0); +@@ -586,7 +670,12 @@ + struct link_socket_actual *act, + const bool nowait) + { +- socklen_t remote_len = sizeof (act->dest.sa); ++ /* af_addr_size WILL return 0 in this case if AFs other than AF_INET ++ * are compiled because act is empty here. ++ * could use getsockname() to support later remote_len check ++ */ ++ socklen_t remote_len_af = af_addr_size(act->dest.addr.sa.sa_family); ++ socklen_t remote_len = sizeof(act->dest.addr); + socket_descriptor_t new_sd = SOCKET_UNDEFINED; + + CLEAR (*act); +@@ -594,7 +683,7 @@ + #ifdef HAVE_GETPEERNAME + if (nowait) + { +- new_sd = getpeername (sd, (struct sockaddr *) &act->dest.sa, &remote_len); ++ new_sd = getpeername (sd, &act->dest.addr.sa, &remote_len); + + if (!socket_defined (new_sd)) + msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: getpeername() failed"); +@@ -607,7 +696,7 @@ + #endif + else + { +- new_sd = accept (sd, (struct sockaddr *) &act->dest.sa, &remote_len); ++ new_sd = accept (sd, &act->dest.addr.sa, &remote_len); + } + + #if 0 /* For debugging only, test the effect of accept() failures */ +@@ -623,7 +712,8 @@ + { + msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: accept(%d) failed", sd); + } +- else if (remote_len != sizeof (act->dest.sa)) ++ /* only valid if we have remote_len_af!=0 */ ++ else if (remote_len_af && remote_len != remote_len_af) + { + msg (D_LINK_ERRORS, "TCP: Received strange incoming connection with unknown address length=%d", remote_len); + openvpn_close_socket (new_sd); +@@ -724,7 +814,7 @@ + { + struct gc_arena gc = gc_new (); + +- if (bind (sd, (struct sockaddr *) &local->sa, sizeof (local->sa))) ++ if (bind (sd, &local->addr.sa, af_addr_size(local->addr.sa.sa_family))) + { + const int errnum = openvpn_errno_socket (); + msg (M_FATAL, "%s: Socket bind failed on local address %s: %s", +@@ -745,7 +835,7 @@ + + #ifdef CONNECT_NONBLOCK + set_nonblock (sd); +- status = connect (sd, (struct sockaddr *) &remote->sa, sizeof (remote->sa)); ++ status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family)); + if (status) + status = openvpn_errno_socket (); + if (status == EINPROGRESS) +@@ -828,76 +918,99 @@ + int retry = 0; + + #ifdef CONNECT_NONBLOCK +- msg (M_INFO, "Attempting to establish TCP connection with %s [nonblock]", +- print_sockaddr (remote, &gc)); ++ msg (M_INFO, "Attempting to establish TCP connection with %s [nonblock]", ++ print_sockaddr (remote, &gc)); + #else +- msg (M_INFO, "Attempting to establish TCP connection with %s", +- print_sockaddr (remote, &gc)); ++ msg (M_INFO, "Attempting to establish TCP connection with %s", ++ print_sockaddr (remote, &gc)); + #endif + + while (true) +- { +- int status; ++ { ++ int status; + + #ifdef ENABLE_MANAGEMENT +- if (management) +- management_set_state (management, +- OPENVPN_STATE_TCP_CONNECT, +- NULL, +- (in_addr_t)0, +- (in_addr_t)0); ++ if (management) ++ management_set_state (management, ++ OPENVPN_STATE_TCP_CONNECT, ++ NULL, ++ (in_addr_t)0, ++ (in_addr_t)0); + #endif + +- status = openvpn_connect (*sd, remote, connect_timeout, signal_received); ++ status = openvpn_connect (*sd, remote, connect_timeout, signal_received); + +- get_signal (signal_received); +- if (*signal_received) +- goto done; +- +- if (!status) +- break; +- +- msg (D_LINK_ERRORS, +- "TCP: connect to %s failed, will try again in %d seconds: %s", +- print_sockaddr (remote, &gc), +- connect_retry_seconds, +- strerror_ts (status, &gc)); +- +- gc_reset (&gc); +- +- openvpn_close_socket (*sd); +- *sd = SOCKET_UNDEFINED; +- +- if (connect_retry_max > 0 && ++retry >= connect_retry_max) +- { +- *signal_received = SIGUSR1; +- goto done; +- } +- +- openvpn_sleep (connect_retry_seconds); +- +- get_signal (signal_received); +- if (*signal_received) +- goto done; ++ get_signal (signal_received); ++ if (*signal_received) ++ goto done; + +- if (remote_list) +- { +- remote_list_next (remote_list); +- remote_dynamic = remote_list_host (remote_list); +- remote->sa.sin_port = htons (remote_list_port (remote_list)); +- *remote_changed = true; +- } ++ if (!status) ++ break; + +- *sd = create_socket_tcp (); +- if (bind_local) +- socket_bind (*sd, local, "TCP Client"); +- update_remote (remote_dynamic, remote, remote_changed); +- } ++ msg (D_LINK_ERRORS, ++ "TCP: connect to %s failed, will try again in %d seconds: %s", ++ print_sockaddr (remote, &gc), ++ connect_retry_seconds, ++ strerror_ts (status, &gc)); ++ ++ ++ gc_reset (&gc); ++ ++ openvpn_close_socket (*sd); ++ *sd = SOCKET_UNDEFINED; ++ ++ if (connect_retry_max > 0 && ++retry >= connect_retry_max) ++ { ++ *signal_received = SIGUSR1; ++ goto done; ++ } ++ ++ openvpn_sleep (connect_retry_seconds); ++ ++ get_signal (signal_received); ++ if (*signal_received) ++ goto done; ++ ++ switch(remote->addr.sa.sa_family) { ++ case AF_INET: ++ if (remote_list) ++ { ++ remote_list_next (remote_list); ++ remote_dynamic = remote_list_host (remote_list); ++ remote->addr.in4.sin_port = htons (remote_list_port (remote_list)); ++ *remote_changed = true; ++ } ++ ++ *sd = create_socket_tcp (); ++ if (bind_local) ++ socket_bind (*sd, local, "TCP Client"); ++ update_remote (remote_dynamic, remote, remote_changed); ++ break; ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++ if (remote_list) ++ { ++ remote_list_next (remote_list); ++ remote_dynamic = remote_list_host (remote_list); ++ remote->addr.in6.sin6_port = htons (remote_list_port (remote_list)); ++ *remote_changed = true; ++ } ++ *sd = create_socket_tcp6 (); ++ if (bind_local) ++ socket_bind (*sd, local, "TCP6 Client"); ++ update_remote (remote_dynamic, remote, remote_changed); ++ break; ++#endif ++ default: ++ msg(M_FATAL, "Only TCP is supported for connection oriented, sa_family=%d", ++ remote->addr.sa.sa_family); ++ } ++ } + +- msg (M_INFO, "TCP connection established with %s", +- print_sockaddr (remote, &gc)); ++ msg (M_INFO, "TCP connection established with %s", ++ print_sockaddr (remote, &gc)); + +- done: ++done: + gc_free (&gc); + } + +@@ -952,17 +1065,44 @@ + + /* resolve local address if undefined */ + if (!addr_defined (&sock->info.lsa->local)) +- { +- sock->info.lsa->local.sa.sin_family = AF_INET; +- sock->info.lsa->local.sa.sin_addr.s_addr = +- (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL, +- sock->local_host, +- 0, +- NULL, +- NULL) +- : htonl (INADDR_ANY)); +- sock->info.lsa->local.sa.sin_port = htons (sock->local_port); ++ { ++ /* may return AF_{INET|INET6} guessed from local_host */ ++ switch(addr_guess_family(sock->info.proto, sock->local_host)) { ++ case AF_INET: ++ sock->info.lsa->local.addr.in4.sin_family = AF_INET; ++ sock->info.lsa->local.addr.in4.sin_addr.s_addr = ++ (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL, ++ sock->local_host, ++ 0, ++ NULL, ++ NULL) ++ : htonl (INADDR_ANY)); ++ sock->info.lsa->local.addr.in4.sin_port = htons (sock->local_port); ++ break; ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++ { ++ struct addrinfo hints , *ai; ++ int err; ++ memset(&hints, 0, sizeof hints); ++ hints.ai_flags=AI_PASSIVE; ++ hints.ai_family=AF_INET6; ++ /* if no local_host provided, ask for IN6ADDR_ANY ... */ ++ if ((err=getaddrinfo(sock->local_host? sock->local_host : "::", ++ NULL, &hints, &ai))==0) { ++ sock->info.lsa->local.addr.in6 = *((struct sockaddr_in6*)(ai->ai_addr)); ++ freeaddrinfo(ai); ++ } else { ++ msg (M_FATAL, "getaddrinfo() failed for local \"%s\": %s", ++ sock->local_host, ++ gai_strerror(err)); ++ } ++ sock->info.lsa->local.addr.in6.sin6_port = htons (sock->local_port); ++ } ++ break; ++#endif + } ++ } + + /* bind to local address/port */ + if (sock->bind_local) +@@ -986,103 +1126,128 @@ + struct gc_arena gc = gc_new (); + + if (!sock->did_resolve_remote) ++ { ++ /* resolve remote address if undefined */ ++ if (!addr_defined (&sock->info.lsa->remote)) + { +- /* resolve remote address if undefined */ +- if (!addr_defined (&sock->info.lsa->remote)) +- { +- sock->info.lsa->remote.sa.sin_family = AF_INET; +- sock->info.lsa->remote.sa.sin_addr.s_addr = 0; ++ switch(addr_guess_family(sock->info.proto, sock->remote_host)) ++ { ++ case AF_INET: ++ sock->info.lsa->remote.addr.in4.sin_family = AF_INET; ++ sock->info.lsa->remote.addr.in4.sin_addr.s_addr = 0; + + if (sock->remote_host) +- { +- unsigned int flags = GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE; +- int retry = 0; +- bool status = false; ++ { ++ unsigned int flags = GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE; ++ int retry = 0; ++ bool status = false; + +- if (remote_list_len (sock->remote_list) > 1 && sock->resolve_retry_seconds == RESOLV_RETRY_INFINITE) +- { +- if (phase == 2) +- flags |= (GETADDR_TRY_ONCE | GETADDR_FATAL); +- retry = 0; +- } +- else if (phase == 1) +- { +- if (sock->resolve_retry_seconds) +- { +- retry = 0; +- } +- else +- { +- flags |= (GETADDR_FATAL | GETADDR_MENTION_RESOLVE_RETRY); +- retry = 0; +- } +- } +- else if (phase == 2) +- { +- if (sock->resolve_retry_seconds) +- { +- flags |= GETADDR_FATAL; +- retry = sock->resolve_retry_seconds; +- } +- else +- { +- ASSERT (0); +- } +- } ++ if (remote_list_len (sock->remote_list) > 1 && sock->resolve_retry_seconds == RESOLV_RETRY_INFINITE) ++ { ++ if (phase == 2) ++ flags |= (GETADDR_TRY_ONCE | GETADDR_FATAL); ++ retry = 0; ++ } ++ else if (phase == 1) ++ { ++ if (sock->resolve_retry_seconds) ++ { ++ retry = 0; ++ } + else +- { +- ASSERT (0); +- } ++ { ++ flags |= (GETADDR_FATAL | GETADDR_MENTION_RESOLVE_RETRY); ++ retry = 0; ++ } ++ } ++ else if (phase == 2) ++ { ++ if (sock->resolve_retry_seconds) ++ { ++ flags |= GETADDR_FATAL; ++ retry = sock->resolve_retry_seconds; ++ } ++ else ++ { ++ ASSERT (0); ++ } ++ } ++ else ++ { ++ ASSERT (0); ++ } + +- sock->info.lsa->remote.sa.sin_addr.s_addr = getaddr ( +- flags, +- sock->remote_host, +- retry, +- &status, +- signal_received); +- +- dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d", +- flags, +- phase, +- retry, +- signal_received ? *signal_received : -1, +- status); ++ sock->info.lsa->remote.addr.in4.sin_addr.s_addr = getaddr ( ++ flags, ++ sock->remote_host, ++ retry, ++ &status, ++ signal_received); ++ ++ dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d", ++ flags, ++ phase, ++ retry, ++ signal_received ? *signal_received : -1, ++ status); + ++ if (signal_received) ++ { ++ if (*signal_received) ++ goto done; ++ } ++ if (!status) ++ { + if (signal_received) +- { +- if (*signal_received) +- goto done; +- } +- if (!status) +- { +- if (signal_received) +- *signal_received = SIGUSR1; +- goto done; +- } ++ *signal_received = SIGUSR1; ++ goto done; + } ++ } + +- sock->info.lsa->remote.sa.sin_port = htons (sock->remote_port); +- } +- +- /* should we re-use previous active remote address? */ +- if (link_socket_actual_defined (&sock->info.lsa->actual)) +- { +- msg (M_INFO, "TCP/UDP: Preserving recently used remote address: %s", +- print_link_socket_actual (&sock->info.lsa->actual, &gc)); +- if (remote_dynamic) +- *remote_dynamic = NULL; +- } +- else +- { +- CLEAR (sock->info.lsa->actual); +- sock->info.lsa->actual.dest = sock->info.lsa->remote; +- } ++ sock->info.lsa->remote.addr.in4.sin_port = htons (sock->remote_port); ++ break; + +- /* remember that we finished */ +- sock->did_resolve_remote = true; ++#ifdef USE_PF_INET6 ++ case AF_INET6: /* jjoFIXME: ipv6 signal logic */ ++ { ++ struct addrinfo hints , *ai; ++ int err; ++ memset(&hints, 0, sizeof hints); ++ hints.ai_flags=0; ++ hints.ai_family=AF_INET6; ++ if ((err=getaddrinfo(sock->remote_host? sock->remote_host : "::" , NULL, &hints, &ai))==0) { ++ sock->info.lsa->remote.addr.in6 = *((struct sockaddr_in6*)(ai->ai_addr)); ++ freeaddrinfo(ai); ++ } else { ++ msg (M_FATAL, "getaddrinfo() failed for remote \"%s\": %s", ++ sock->remote_host, ++ gai_strerror(err)); ++ } ++ sock->info.lsa->remote.addr.in6.sin6_port = htons (sock->remote_port); ++ } ++ break; ++#endif ++ } ++ } ++ /* should we re-use previous active remote address? */ ++ if (link_socket_actual_defined (&sock->info.lsa->actual)) ++ { ++ msg (M_INFO, "TCP/UDP: Preserving recently used remote address: %s", ++ print_link_socket_actual (&sock->info.lsa->actual, &gc)); ++ if (remote_dynamic) ++ *remote_dynamic = NULL; ++ } ++ else ++ { ++ CLEAR (sock->info.lsa->actual); ++ sock->info.lsa->actual.dest = sock->info.lsa->remote; + } + +- done: ++ /* remember that we finished */ ++ sock->did_resolve_remote = true; ++ } ++ ++done: + gc_free (&gc); + } + +@@ -1312,7 +1477,11 @@ + goto done; + + /* TCP client/server */ +- if (sock->info.proto == PROTO_TCPv4_SERVER) ++ if (sock->info.proto == PROTO_TCPv4_SERVER ++#ifdef USE_PF_INET6 ++ ||sock->info.proto == PROTO_TCPv6_SERVER ++#endif ++ ) + { + switch (sock->mode) + { +@@ -1347,7 +1516,11 @@ + ASSERT (0); + } + } +- else if (sock->info.proto == PROTO_TCPv4_CLIENT) ++ else if (sock->info.proto == PROTO_TCPv4_CLIENT ++#ifdef USE_PF_INET6 ++ ||sock->info.proto == PROTO_TCPv6_CLIENT ++#endif ++ ) + { + + #ifdef GENERAL_PROXY_SUPPORT +@@ -1432,8 +1605,8 @@ + sock->remote_port = sock->proxy_dest_port; + sock->did_resolve_remote = false; + +- sock->info.lsa->actual.dest.sa.sin_addr.s_addr = 0; +- sock->info.lsa->remote.sa.sin_addr.s_addr = 0; ++ addr_zero_host(&sock->info.lsa->actual.dest); ++ addr_zero_host(&sock->info.lsa->remote); + + resolve_remote (sock, 1, NULL, signal_received); + +@@ -1448,7 +1621,7 @@ + if (remote_changed) + { + msg (M_INFO, "TCP/UDP: Dynamic remote address changed during TCP connection establishment"); +- sock->info.lsa->remote.sa.sin_addr.s_addr = sock->info.lsa->actual.dest.sa.sin_addr.s_addr; ++ addr_copy_host(&sock->info.lsa->remote, &sock->info.lsa->actual.dest); + } + } + +@@ -1620,13 +1793,20 @@ + { + struct gc_arena gc = gc_new (); + +- msg (D_LINK_ERRORS, +- "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)", +- print_link_socket_actual (from_addr, &gc), +- (int)from_addr->dest.sa.sin_family, +- print_sockaddr (&info->lsa->remote, &gc)); ++ switch(from_addr->dest.addr.sa.sa_family) ++ { ++ case AF_INET: ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++#endif ++ msg (D_LINK_ERRORS, ++ "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)", ++ print_link_socket_actual (from_addr, &gc), ++ (int)from_addr->dest.addr.sa.sa_family, ++ print_sockaddr (&info->lsa->remote, &gc)); ++ break; ++ } + buf->len = 0; +- + gc_free (&gc); + } + +@@ -1641,10 +1821,25 @@ + { + const struct link_socket_addr *lsa = info->lsa; + ++/* ++ * This logic supports "redirect-gateway" semantic, which ++ * makes sense only for PF_INET routes over PF_INET endpoints ++ * ++ * Maybe in the future consider PF_INET6 endpoints also ... ++ * by now just ignore it ++ * ++ */ ++#if defined ( USE_PF_INET6 ) ++ if(lsa->actual.dest.addr.sa.sa_family != AF_INET) ++ return 0; ++#else ++ ASSERT(lsa->actual.dest.addr.sa.sa_family == AF_INET); ++#endif ++ + if (link_socket_actual_defined (&lsa->actual)) +- return ntohl (lsa->actual.dest.sa.sin_addr.s_addr); ++ return ntohl (lsa->actual.dest.addr.in4.sin_addr.s_addr); + else if (addr_defined (&lsa->remote)) +- return ntohl (lsa->remote.sa.sin_addr.s_addr); ++ return ntohl (lsa->remote.addr.in4.sin_addr.s_addr); + else + return 0; + } +@@ -1871,27 +2066,58 @@ + const unsigned int flags, + struct gc_arena *gc) + { +- if (addr) +- { +- struct buffer out = alloc_buf_gc (64, gc); +- const int port = ntohs (addr->sa.sin_port); +- +- mutex_lock_static (L_INET_NTOA); +- buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]")); +- mutex_unlock_static (L_INET_NTOA); ++ struct buffer out; ++ bool addr_is_defined; ++ if (!addr) { ++ return "[NULL]"; ++ } ++ addr_is_defined = addr_defined (addr); ++ switch(addr->addr.sa.sa_family) { ++ case AF_INET: ++ { ++ const int port= ntohs (addr->addr.in4.sin_port); ++ out = alloc_buf_gc (128, gc); ++ buf_puts (&out, "[AF_INET]"); ++ mutex_lock_static (L_INET_NTOA); ++ buf_puts (&out, (addr_is_defined ? inet_ntoa (addr->addr.in4.sin_addr) : "[undef]")); ++ mutex_unlock_static (L_INET_NTOA); + +- if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED))) +- && port) ++ if (((flags & PS_SHOW_PORT) || (addr_is_defined && (flags & PS_SHOW_PORT_IF_DEFINED))) ++ && port) + { + if (separator) + buf_printf (&out, "%s", separator); + + buf_printf (&out, "%d", port); + } +- return BSTR (&out); +- } +- else +- return "[NULL]"; ++ } ++ break; ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++ { ++ const int port= ntohs (addr->addr.in6.sin6_port); ++ char buf[INET6_ADDRSTRLEN] = "[undef]"; ++ out = alloc_buf_gc (128, gc); ++ buf_puts (&out, "[AF_INET6]"); ++ if (addr_is_defined) ++ { ++ getnameinfo(&addr->addr.sa, sizeof (struct sockaddr_in6), ++ buf, sizeof (buf), NULL, 0, NI_NUMERICHOST); ++ buf_puts (&out, buf); ++ } ++ if (((flags & PS_SHOW_PORT) || (addr_is_defined && (flags & PS_SHOW_PORT_IF_DEFINED))) ++ && port) ++ { ++ if (separator) ++ buf_puts (&out, separator); ++ ++ buf_printf (&out, "%d", port); ++ } ++ } ++ break; ++#endif ++ } ++ return BSTR (&out); + } + + const char * +@@ -1911,12 +2137,38 @@ + struct buffer out = alloc_buf_gc (128, gc); + buf_printf (&out, "%s", print_sockaddr_ex (&act->dest, separator, flags, gc)); + #if ENABLE_IP_PKTINFO +- if ((flags & PS_SHOW_PKTINFO) && act->pi.ipi_spec_dst.s_addr) ++ if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act)) + { ++ switch(act->dest.addr.sa.sa_family) ++ { ++ case AF_INET: ++ { + struct openvpn_sockaddr sa; + CLEAR (sa); +- sa.sa.sin_addr = act->pi.ipi_spec_dst; ++ sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst; + buf_printf (&out, " (via %s)", print_sockaddr_ex (&sa, separator, 0, gc)); ++ } ++ break; ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++ { ++ struct sockaddr_in6 sin6; ++ char buf[INET6_ADDRSTRLEN] = "[undef]"; ++ memset(&sin6, 0, sizeof sin6); ++ sin6.sin6_family = AF_INET6; ++ sin6.sin6_addr = act->pi.in6.ipi6_addr; ++ { ++ if (getnameinfo((struct sockaddr *)&sin6, sizeof (struct sockaddr_in6), ++ buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) == 0) ++ buf_printf (&out, " (via %s)", buf); ++ else ++ buf_printf (&out, " (via [getnameinfo() err])"); ++ } ++ } ++ break; ++#endif ++ } ++ + } + #endif + return BSTR (&out); +@@ -1952,21 +2204,37 @@ + setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const bool flags) + { + char name_buf[256]; ++ char buf[128]; + +- if (flags & SA_IP_PORT) +- openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix); +- else +- openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix); ++ switch(addr->addr.sa.sa_family) { ++ case AF_INET: ++ if (flags & SA_IP_PORT) ++ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix); ++ else ++ openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix); + +- mutex_lock_static (L_INET_NTOA); +- setenv_str (es, name_buf, inet_ntoa (addr->sa.sin_addr)); +- mutex_unlock_static (L_INET_NTOA); ++ mutex_lock_static (L_INET_NTOA); ++ setenv_str (es, name_buf, inet_ntoa (addr->addr.in4.sin_addr)); ++ mutex_unlock_static (L_INET_NTOA); ++ ++ if ((flags & SA_IP_PORT) && addr->addr.in4.sin_port) ++ { ++ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix); ++ setenv_int (es, name_buf, ntohs (addr->addr.in4.sin_port)); ++ } ++ break; ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip6", name_prefix); ++ getnameinfo(&addr->addr.sa, sizeof (struct sockaddr_in6), ++ buf, sizeof(buf), NULL, 0, NI_NUMERICHOST); ++ setenv_str (es, name_buf, buf); + +- if ((flags & SA_IP_PORT) && addr->sa.sin_port) +- { + openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix); +- setenv_int (es, name_buf, ntohs (addr->sa.sin_port)); +- } ++ setenv_int (es, name_buf, ntohs (addr->addr.in6.sin6_port)); ++ break; ++#endif ++ } + } + + void +@@ -1976,7 +2244,8 @@ + { + struct openvpn_sockaddr si; + CLEAR (si); +- si.sa.sin_addr.s_addr = htonl (addr); ++ si.addr.in4.sin_family = AF_INET; ++ si.addr.in4.sin_addr.s_addr = htonl (addr); + setenv_sockaddr (es, name_prefix, &si, flags); + } + } +@@ -1997,16 +2266,63 @@ + struct proto_names { + const char *short_form; + const char *display_form; ++ bool is_dgram; ++ bool is_net; ++ sa_family_t proto_af; + }; + + /* Indexed by PROTO_x */ +-static const struct proto_names proto_names[] = { +- {"udp", "UDPv4"}, +- {"tcp-server", "TCPv4_SERVER"}, +- {"tcp-client", "TCPv4_CLIENT"}, +- {"tcp", "TCPv4"} ++static const struct proto_names proto_names[PROTO_N] = { ++ {"proto-uninitialized", "proto-NONE",0,0, AF_UNSPEC}, ++ {"udp", "UDPv4",1,1, AF_INET}, ++ {"tcp-server", "TCPv4_SERVER",0,1, AF_INET}, ++ {"tcp-client", "TCPv4_CLIENT",0,1, AF_INET}, ++ {"tcp", "TCPv4",0,1, AF_INET}, ++#ifdef USE_PF_INET6 ++ {"udp6" ,"UDPv6",1,1, AF_INET6}, ++ {"tcp6-server","TCPv6_SERVER",0,1, AF_INET6}, ++ {"tcp6-client","TCPv6_CLIENT",0,1, AF_INET6}, ++ {"tcp6" ,"TCPv6",0,1, AF_INET6}, ++#endif + }; + ++bool ++proto_is_net(int proto) ++{ ++ if (proto < 0 || proto >= PROTO_N) ++ ASSERT(0); ++ return proto_names[proto].is_net; ++} ++bool ++proto_is_dgram(int proto) ++{ ++ if (proto < 0 || proto >= PROTO_N) ++ ASSERT(0); ++ return proto_names[proto].is_dgram; ++} ++bool ++proto_is_udp(int proto) ++{ ++ if (proto < 0 || proto >= PROTO_N) ++ ASSERT(0); ++ return proto_names[proto].is_dgram&&proto_names[proto].is_net; ++} ++bool ++proto_is_tcp(int proto) ++{ ++ if (proto < 0 || proto >= PROTO_N) ++ ASSERT(0); ++ return (!proto_names[proto].is_dgram)&&proto_names[proto].is_net; ++} ++ ++sa_family_t ++proto_sa_family(int proto) ++{ ++ if (proto < 0 || proto >= PROTO_N) ++ ASSERT(0); ++ return proto_names[proto].proto_af; ++} ++ + int + ascii2proto (const char* proto_name) + { +@@ -2046,6 +2362,46 @@ + return BSTR (&out); + } + ++int ++addr_guess_family(int proto, const char *name) ++{ ++ sa_family_t ret; ++ if (proto) { ++ return proto_sa_family(proto); /* already stamped */ ++ } ++#ifdef USE_PF_UNIX ++ else if (name && name[0] == '/') { ++ return AF_UNIX; ++ } ++#endif ++#ifdef USE_PF_INET6 ++ else { ++ struct addrinfo hints , *ai; ++ int err; ++ memset(&hints, 0, sizeof hints); ++ hints.ai_flags=AI_NUMERICHOST; ++ if ((err=getaddrinfo(name, NULL, &hints, &ai))==0) { ++ ret=ai->ai_family; ++ freeaddrinfo(ai); ++ return ret; ++ } ++ } ++#endif ++ return AF_INET; /* default */ ++} ++const char * ++addr_family_name (int af) ++{ ++ switch (af) { ++ case AF_INET: return "AF_INET"; ++ case AF_INET6: return "AF_INET6"; ++#ifdef USE_PF_UNIX ++ case AF_UNIX: return "AF_UNIX"; ++#endif ++ } ++ return "AF_UNSPEC"; ++} ++ + /* + * Given a local proto, return local proto + * if !remote, or compatible remote proto +@@ -2060,10 +2416,15 @@ + ASSERT (proto >= 0 && proto < PROTO_N); + if (remote) + { +- if (proto == PROTO_TCPv4_SERVER) +- return PROTO_TCPv4_CLIENT; +- if (proto == PROTO_TCPv4_CLIENT) +- return PROTO_TCPv4_SERVER; ++ switch (proto) ++ { ++ case PROTO_TCPv4_SERVER: return PROTO_TCPv4_CLIENT; ++ case PROTO_TCPv4_CLIENT: return PROTO_TCPv4_SERVER; ++#ifdef USE_PF_INET6 ++ case PROTO_TCPv6_SERVER: return PROTO_TCPv6_CLIENT; ++ case PROTO_TCPv6_CLIENT: return PROTO_TCPv6_SERVER; ++#endif ++ } + } + return proto; + } +@@ -2121,12 +2482,27 @@ + + #if ENABLE_IP_PKTINFO + +-struct openvpn_pktinfo ++struct openvpn_in4_pktinfo + { + struct cmsghdr cmsghdr; +- struct in_pktinfo in_pktinfo; ++ struct in_pktinfo pi; ++}; ++#ifdef USE_PF_INET6 ++struct openvpn_in6_pktinfo ++{ ++ struct cmsghdr cmsghdr; ++ struct in6_pktinfo pi6; ++}; ++#endif ++ ++union openvpn_pktinfo { ++ struct openvpn_in4_pktinfo cmsgpi; ++#ifdef USE_PF_INET6 ++ struct openvpn_in6_pktinfo cmsgpi6; ++#endif + }; + ++ + static socklen_t + link_socket_read_udp_posix_recvmsg (struct link_socket *sock, + struct buffer *buf, +@@ -2134,15 +2510,15 @@ + struct link_socket_actual *from) + { + struct iovec iov; +- struct openvpn_pktinfo opi; ++ union openvpn_pktinfo opi; + struct msghdr mesg; +- socklen_t fromlen = sizeof (from->dest.sa); ++ socklen_t fromlen = sizeof (from->dest.addr); + + iov.iov_base = BPTR (buf); + iov.iov_len = maxsize; + mesg.msg_iov = &iov; + mesg.msg_iovlen = 1; +- mesg.msg_name = &from->dest.sa; ++ mesg.msg_name = &from->dest.addr; + mesg.msg_namelen = fromlen; + mesg.msg_control = &opi; + mesg.msg_controllen = sizeof (opi); +@@ -2159,9 +2535,21 @@ + && cmsg->cmsg_len >= sizeof (opi)) + { + struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg); +- from->pi.ipi_ifindex = pkti->ipi_ifindex; +- from->pi.ipi_spec_dst = pkti->ipi_spec_dst; ++ from->pi.in4.ipi_ifindex = pkti->ipi_ifindex; ++ from->pi.in4.ipi_spec_dst = pkti->ipi_spec_dst; ++ } ++#ifdef USE_PF_INET6 ++ else if (cmsg != NULL ++ && CMSG_NXTHDR (&mesg, cmsg) == NULL ++ && cmsg->cmsg_level == IPPROTO_IPV6 ++ && cmsg->cmsg_type == IPV6_PKTINFO ++ && cmsg->cmsg_len >= sizeof (struct openvpn_in6_pktinfo)) ++ { ++ struct in6_pktinfo *pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg); ++ from->pi.in6.ipi6_ifindex = pkti6->ipi6_ifindex; ++ from->pi.in6.ipi6_addr = pkti6->ipi6_addr; + } ++#endif + } + return fromlen; + } +@@ -2173,18 +2561,20 @@ + int maxsize, + struct link_socket_actual *from) + { +- socklen_t fromlen = sizeof (from->dest.sa); +- from->dest.sa.sin_addr.s_addr = 0; ++ socklen_t fromlen = sizeof (from->dest.addr); ++ socklen_t expectedlen = af_addr_size(proto_sa_family(sock->info.proto)); ++ addr_zero_host(&from->dest); + ASSERT (buf_safe (buf, maxsize)); + #if ENABLE_IP_PKTINFO +- if (sock->sockflags & SF_USE_IP_PKTINFO) ++ /* Both PROTO_UDPv4 and PROTO_UDPv6 */ ++ if (proto_is_udp(sock->info.proto) && sock->sockflags & SF_USE_IP_PKTINFO) + fromlen = link_socket_read_udp_posix_recvmsg (sock, buf, maxsize, from); + else + #endif + buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0, +- (struct sockaddr *) &from->dest.sa, &fromlen); +- if (fromlen != sizeof (from->dest.sa)) +- bad_address_length (fromlen, sizeof (from->dest.sa)); ++ &from->dest.addr.sa, &fromlen); ++ if (buf->len >= 0 && expectedlen && fromlen != expectedlen) ++ bad_address_length (fromlen, expectedlen); + return buf->len; + } + +@@ -2221,26 +2611,52 @@ + struct iovec iov; + struct msghdr mesg; + struct cmsghdr *cmsg; +- struct in_pktinfo *pkti; +- struct openvpn_pktinfo opi; + + iov.iov_base = BPTR (buf); + iov.iov_len = BLEN (buf); + mesg.msg_iov = &iov; + mesg.msg_iovlen = 1; +- mesg.msg_name = &to->dest.sa; +- mesg.msg_namelen = sizeof (to->dest.sa); +- mesg.msg_control = &opi; +- mesg.msg_controllen = sizeof (opi); +- mesg.msg_flags = 0; +- cmsg = CMSG_FIRSTHDR (&mesg); +- cmsg->cmsg_len = sizeof (opi); +- cmsg->cmsg_level = SOL_IP; +- cmsg->cmsg_type = IP_PKTINFO; +- pkti = (struct in_pktinfo *) CMSG_DATA (cmsg); +- pkti->ipi_ifindex = to->pi.ipi_ifindex; +- pkti->ipi_spec_dst = to->pi.ipi_spec_dst; +- pkti->ipi_addr.s_addr = 0; ++ switch (sock->info.lsa->remote.addr.sa.sa_family) ++ { ++ case AF_INET: { ++ struct openvpn_in4_pktinfo opi; ++ struct in_pktinfo *pkti; ++ mesg.msg_name = &to->dest.addr.sa; ++ mesg.msg_namelen = sizeof (struct sockaddr_in); ++ mesg.msg_control = &opi; ++ mesg.msg_controllen = sizeof (opi); ++ mesg.msg_flags = 0; ++ cmsg = CMSG_FIRSTHDR (&mesg); ++ cmsg->cmsg_len = sizeof (opi); ++ cmsg->cmsg_level = SOL_IP; ++ cmsg->cmsg_type = IP_PKTINFO; ++ pkti = (struct in_pktinfo *) CMSG_DATA (cmsg); ++ pkti->ipi_ifindex = to->pi.in4.ipi_ifindex; ++ pkti->ipi_spec_dst = to->pi.in4.ipi_spec_dst; ++ pkti->ipi_addr.s_addr = 0; ++ break; ++ } ++#ifdef USE_PF_INET6 ++ case AF_INET6: { ++ struct openvpn_in6_pktinfo opi6; ++ struct in6_pktinfo *pkti6; ++ mesg.msg_name = &to->dest.addr.sa; ++ mesg.msg_namelen = sizeof (struct sockaddr_in6); ++ mesg.msg_control = &opi6; ++ mesg.msg_controllen = sizeof (opi6); ++ mesg.msg_flags = 0; ++ cmsg = CMSG_FIRSTHDR (&mesg); ++ cmsg->cmsg_len = sizeof (opi6); ++ cmsg->cmsg_level = IPPROTO_IPV6; ++ cmsg->cmsg_type = IPV6_PKTINFO; ++ pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg); ++ pkti6->ipi6_ifindex = to->pi.in6.ipi6_ifindex; ++ pkti6->ipi6_addr = to->pi.in6.ipi6_addr; ++ break; ++ } ++#endif ++ default: ASSERT(0); ++ } + return sendmsg (sock->sd, &mesg, 0); + } + +@@ -2384,7 +2800,7 @@ + { + /* set destination address for UDP writes */ + sock->writes.addr_defined = true; +- sock->writes.addr = to->dest.sa; ++ sock->writes.addr = to->dest.addr.in4; + sock->writes.addrlen = sizeof (sock->writes.addr); + + status = WSASendTo( +@@ -2540,10 +2956,10 @@ + { + if (io->addrlen != sizeof (io->addr)) + bad_address_length (io->addrlen, sizeof (io->addr)); +- from->dest.sa = io->addr; ++ from->dest.addr.sa = io->addr; + } + else +- CLEAR (from->dest.sa); ++ CLEAR (from->dest.addr.sa); + } + + if (buf) +diff --git a/openvpn/socket.h b/openvpn/socket.h +index 28bf41f..435cf38 100644 +--- a/openvpn/socket.h ++++ b/openvpn/socket.h +@@ -81,7 +81,13 @@ #define ntohps(x) ntohs(x) + struct openvpn_sockaddr + { + /*int dummy;*/ /* add offset to force a bug if sa not explicitly dereferenced */ +- struct sockaddr_in sa; ++ union { ++ struct sockaddr sa; ++ struct sockaddr_in in4; ++#ifdef USE_PF_INET6 ++ struct sockaddr_in6 in6; ++#endif ++ } addr; + }; + + /* actual address of remote, based on source address of received packets */ +@@ -90,7 +96,12 @@ struct link_socket_actual + int dummy; /* JYFIXME -- add offset to force a bug if dest not explicitly dereferenced */ + struct openvpn_sockaddr dest; + #if ENABLE_IP_PKTINFO +- struct in_pktinfo pi; ++ union { ++ struct in_pktinfo in4; ++#ifdef USE_PF_INET6 ++ struct in6_pktinfo in6; ++#endif ++ } pi; + #endif + }; + +@@ -411,6 +422,14 @@ socket_descriptor_t create_socket_tcp (v + socket_descriptor_t socket_do_accept (socket_descriptor_t sd, + struct link_socket_actual *act, + const bool nowait); ++/* ++ * proto related ++ */ ++bool proto_is_net(int proto); ++bool proto_is_dgram(int proto); ++bool proto_is_udp(int proto); ++bool proto_is_tcp(int proto); ++ + + /* + * DNS resolution +@@ -436,23 +455,49 @@ in_addr_t getaddr (unsigned int flags, + * Transport protocol naming and other details. + */ + +-#define PROTO_UDPv4 0 +-#define PROTO_TCPv4_SERVER 1 +-#define PROTO_TCPv4_CLIENT 2 +-#define PROTO_TCPv4 3 +-#define PROTO_N 4 ++#if 0 /* PRE UDPv6/TCPv6 code */ ++#define PROTO_NONE 0 /* catch for uninitialized */ ++#define PROTO_UDPv4 1 ++#define PROTO_TCPv4_SERVER 2 ++#define PROTO_TCPv4_CLIENT 3 ++#define PROTO_TCPv4 4 ++#define PROTO_UDPv6 5 ++#define PROTO_TCPv6_SERVER 6 ++#define PROTO_TCPv6_CLIENT 7 ++#define PROTO_TCPv6 8 ++#define PROTO_N 9 ++#endif /* if 0 */ ++ ++/* ++ * Use enum's instead of #define to allow for easier ++ * optional proto support ++ */ ++enum proto_num { ++ PROTO_NONE, /* catch for uninitialized */ ++ PROTO_UDPv4, ++ PROTO_TCPv4_SERVER, ++ PROTO_TCPv4_CLIENT, ++ PROTO_TCPv4, ++ PROTO_UDPv6, ++ PROTO_TCPv6_SERVER, ++ PROTO_TCPv6_CLIENT, ++ PROTO_TCPv6, ++ PROTO_N ++}; + + int ascii2proto (const char* proto_name); + const char *proto2ascii (int proto, bool display_form); + const char *proto2ascii_all (struct gc_arena *gc); + int proto_remote (int proto, bool remote); ++const char *addr_family_name(int af); + + /* + * Overhead added to packets by various protocols. + */ + #define IPv4_UDP_HEADER_SIZE 28 + #define IPv4_TCP_HEADER_SIZE 40 +-#define IPv6_UDP_HEADER_SIZE 40 ++#define IPv6_UDP_HEADER_SIZE 48 ++#define IPv6_TCP_HEADER_SIZE 60 + + extern const int proto_overhead[]; + +@@ -485,7 +530,7 @@ legal_ipv4_port (int port) + static inline bool + link_socket_proto_connection_oriented (int proto) + { +- return proto == PROTO_TCPv4_SERVER || proto == PROTO_TCPv4_CLIENT; ++ return !proto_is_dgram(proto); + } + + static inline bool +@@ -500,7 +545,30 @@ link_socket_connection_oriented (const s + static inline bool + addr_defined (const struct openvpn_sockaddr *addr) + { +- return addr->sa.sin_addr.s_addr != 0; ++ if (!addr) return 0; ++ switch (addr->addr.sa.sa_family) { ++ case AF_INET: return addr->addr.in4.sin_addr.s_addr != 0; ++#ifdef USE_PF_INET6 ++ case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&addr->addr.in6.sin6_addr); ++#endif ++ default: return 0; ++ } ++} ++static inline bool ++addr_defined_ipi (const struct link_socket_actual *lsa) ++{ ++#if ENABLE_IP_PKTINFO ++ if (!lsa) return 0; ++ switch (lsa->dest.addr.sa.sa_family) { ++ case AF_INET: return lsa->pi.in4.ipi_spec_dst.s_addr != 0; ++#ifdef USE_PF_INET6 ++ case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&lsa->pi.in6.ipi6_addr); ++#endif ++ default: return 0; ++ } ++#else ++ ASSERT(0); ++#endif + } + + static inline bool +@@ -512,20 +580,50 @@ link_socket_actual_defined (const struct + static inline bool + addr_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2) + { +- return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr; ++ switch(a1->addr.sa.sa_family) { ++ case AF_INET: ++ return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr; ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++ return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr); ++#endif ++ } ++ ASSERT(0); ++ return false; + } + + static inline in_addr_t +-addr_host (const struct openvpn_sockaddr *s) ++addr_host (const struct openvpn_sockaddr *addr) + { +- return ntohl (s->sa.sin_addr.s_addr); ++ /* ++ * "public" addr returned is checked against ifconfig for ++ * possible clash: non sense for now given ++ * that we do ifconfig only IPv4 ++ */ ++#if defined(USE_PF_INET6) ++ if(addr->addr.sa.sa_family != AF_INET) ++ return 0; ++#else ++ ASSERT(addr->addr.sa.sa_family == AF_INET); ++#endif ++ return ntohl (addr->addr.in4.sin_addr.s_addr); + } + + static inline bool + addr_port_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2) + { +- return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr +- && a1->sa.sin_port == a2->sa.sin_port; ++ switch(a1->addr.sa.sa_family) { ++ case AF_INET: ++ return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr ++ && a1->addr.in4.sin_port == a2->addr.in4.sin_port; ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++ return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr) ++ && a1->addr.in6.sin6_port == a2->addr.in6.sin6_port; ++#endif ++ } ++ ASSERT(0); ++ return false; + } + + static inline bool +@@ -538,6 +636,74 @@ addr_match_proto (const struct openvpn_s + : addr_port_match (a1, a2); + } + ++static inline void ++addr_zero_host(struct openvpn_sockaddr *addr) ++{ ++ switch(addr->addr.sa.sa_family) { ++ case AF_INET: ++ addr->addr.in4.sin_addr.s_addr = 0; ++ break; ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++ memset(&addr->addr.in6.sin6_addr, 0, sizeof (struct in6_addr)); ++ break; ++#endif ++ } ++} ++ ++static inline void ++addr_copy_sa(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src) ++{ ++ dst->addr = src->addr; ++} ++ ++static inline void ++addr_copy_host(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src) ++{ ++ switch(src->addr.sa.sa_family) { ++ case AF_INET: ++ dst->addr.in4.sin_addr.s_addr = src->addr.in4.sin_addr.s_addr; ++ break; ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++ dst->addr.in6.sin6_addr = src->addr.in6.sin6_addr; ++ break; ++#endif ++ } ++} ++ ++static inline bool ++addr_inet4or6(struct sockaddr *addr) ++{ ++ return addr->sa_family == AF_INET || addr->sa_family == AF_INET6; ++} ++ ++int addr_guess_family(int proto, const char *name); ++static inline int ++af_addr_size(sa_family_t af) ++{ ++#if defined(USE_PF_INET6) || defined (USE_PF_UNIX) ++ switch(af) { ++ case AF_INET: return sizeof (struct sockaddr_in); ++#ifdef USE_PF_UNIX ++ case AF_UNIX: return sizeof (struct sockaddr_un); ++#endif ++#ifdef USE_PF_INET6 ++ case AF_INET6: return sizeof (struct sockaddr_in6); ++#endif ++ default: ++#if 0 ++ /* could be called from socket_do_accept() with empty addr */ ++ msg (M_ERR, "Bad address family: %d\n", addr->sa_family); ++ ASSERT(0); ++#endif ++ return 0; ++ } ++#else /* only AF_INET */ ++ return sizeof(struct sockaddr_in); ++#endif ++} ++ + static inline bool + link_socket_actual_match (const struct link_socket_actual *a1, const struct link_socket_actual *a2) + { +@@ -594,14 +760,18 @@ link_socket_verify_incoming_addr (struct + { + if (buf->len > 0) + { +- if (from_addr->dest.sa.sin_family != AF_INET) +- return false; +- if (!link_socket_actual_defined (from_addr)) +- return false; +- if (info->remote_float || !addr_defined (&info->lsa->remote)) +- return true; +- if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto)) +- return true; ++ switch (from_addr->dest.addr.sa.sa_family) { ++#ifdef USE_PF_INET6 ++ case AF_INET6: ++#endif ++ case AF_INET: ++ if (!link_socket_actual_defined (from_addr)) ++ return false; ++ if (info->remote_float || !addr_defined (&info->lsa->remote)) ++ return true; ++ if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto)) ++ return true; ++ } + } + return false; + } +@@ -707,7 +877,7 @@ link_socket_read (struct link_socket *so + int maxsize, + struct link_socket_actual *from) + { +- if (sock->info.proto == PROTO_UDPv4) ++ if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */ + { + int res; + +@@ -718,10 +888,10 @@ #else + #endif + return res; + } +- else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT) ++ else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */ + { + /* from address was returned by accept */ +- from->dest.sa = sock->info.lsa->actual.dest.sa; ++ addr_copy_sa(&from->dest, &sock->info.lsa->actual.dest); + return link_socket_read_tcp (sock, buf); + } + else +@@ -776,13 +946,14 @@ #if ENABLE_IP_PKTINFO + struct buffer *buf, + struct link_socket_actual *to); + +- if (sock->sockflags & SF_USE_IP_PKTINFO) ++ if (proto_is_udp(sock->info.proto) && (sock->sockflags & SF_USE_IP_PKTINFO) ++ && addr_defined_ipi(to)) + return link_socket_write_udp_posix_sendmsg (sock, buf, to); + else + #endif + return sendto (sock->sd, BPTR (buf), BLEN (buf), 0, +- (struct sockaddr *) &to->dest.sa, +- (socklen_t) sizeof (to->dest.sa)); ++ (struct sockaddr *) &to->dest.addr.sa, ++ (socklen_t) af_addr_size(to->dest.addr.sa.sa_family)); + } + + static inline int +@@ -813,11 +984,11 @@ link_socket_write (struct link_socket *s + struct buffer *buf, + struct link_socket_actual *to) + { +- if (sock->info.proto == PROTO_UDPv4) ++ if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */ + { + return link_socket_write_udp (sock, buf, to); + } +- else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT) ++ else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */ + { + return link_socket_write_tcp (sock, buf, to); + } +diff --git a/openvpn/socks.c b/openvpn/socks.c +index cc9d82f..7ec9a3e 100644 +--- a/openvpn/socks.c ++++ b/openvpn/socks.c +@@ -175,9 +175,9 @@ recv_socks_reply (socket_descriptor_t sd + + if (addr != NULL) + { +- addr->sa.sin_family = AF_INET; +- addr->sa.sin_addr.s_addr = htonl (INADDR_ANY); +- addr->sa.sin_port = htons (0); ++ addr->addr.in4.sin_family = AF_INET; ++ addr->addr.in4.sin_addr.s_addr = htonl (INADDR_ANY); ++ addr->addr.in4.sin_port = htons (0); + } + + while (len < 4 + alen + 2) +@@ -264,8 +264,8 @@ recv_socks_reply (socket_descriptor_t sd + /* ATYP == 1 (IP V4 address) */ + if (atyp == '\x01' && addr != NULL) + { +- memcpy (&addr->sa.sin_addr, buf + 4, sizeof (addr->sa.sin_addr)); +- memcpy (&addr->sa.sin_port, buf + 8, sizeof (addr->sa.sin_port)); ++ memcpy (&addr->addr.in4.sin_addr, buf + 4, sizeof (addr->addr.in4.sin_addr)); ++ memcpy (&addr->addr.in4.sin_port, buf + 8, sizeof (addr->addr.in4.sin_port)); + } + + +@@ -383,8 +383,8 @@ socks_process_incoming_udp (struct buffe + if (atyp != 1) /* ATYP == 1 (IP V4) */ + goto error; + +- buf_read (buf, &from->dest.sa.sin_addr, sizeof (from->dest.sa.sin_addr)); +- buf_read (buf, &from->dest.sa.sin_port, sizeof (from->dest.sa.sin_port)); ++ buf_read (buf, &from->dest.addr.in4.sin_addr, sizeof (from->dest.addr.in4.sin_addr)); ++ buf_read (buf, &from->dest.addr.in4.sin_port, sizeof (from->dest.addr.in4.sin_port)); + + return; + +@@ -416,8 +416,8 @@ socks_process_outgoing_udp (struct buffe + buf_write_u16 (&head, 0); /* RSV = 0 */ + buf_write_u8 (&head, 0); /* FRAG = 0 */ + buf_write_u8 (&head, '\x01'); /* ATYP = 1 (IP V4) */ +- buf_write (&head, &to->dest.sa.sin_addr, sizeof (to->dest.sa.sin_addr)); +- buf_write (&head, &to->dest.sa.sin_port, sizeof (to->dest.sa.sin_port)); ++ buf_write (&head, &to->dest.addr.in4.sin_addr, sizeof (to->dest.addr.in4.sin_addr)); ++ buf_write (&head, &to->dest.addr.in4.sin_port, sizeof (to->dest.addr.in4.sin_port)); + + return 10; + } diff --git a/net-misc/openvpn/files/openvpn-2.1_rc7-tap.patch b/net-misc/openvpn/files/openvpn-2.1_rc7-tap.patch new file mode 100644 index 00000000..0220194a --- /dev/null +++ b/net-misc/openvpn/files/openvpn-2.1_rc7-tap.patch @@ -0,0 +1,57 @@ +Index: tun.c +=================================================================== +--- tun.c (revision 2713) ++++ tun.c (revision 2715) +@@ -1220,26 +1220,44 @@ + { + if (tt) + { +-#ifdef CONFIG_FEATURE_IPROUTE + if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig) + { + char command_line[256]; + struct gc_arena gc = gc_new (); + ++#ifdef CONFIG_FEATURE_IPROUTE ++ if (is_tun_p2p (tt)) ++ { ++ openvpn_snprintf (command_line, sizeof (command_line), ++ "%s addr del dev %s local %s peer %s", ++ iproute_path, ++ tt->actual_name, ++ print_in_addr_t (tt->local, 0, &gc), ++ print_in_addr_t (tt->remote_netmask, 0, &gc) ++ ); ++ } ++ else ++ { ++ openvpn_snprintf (command_line, sizeof (command_line), ++ "%s addr del dev %s %s/%d", ++ iproute_path, ++ tt->actual_name, ++ print_in_addr_t (tt->local, 0, &gc), ++ count_netmask_bits(print_in_addr_t (tt->remote_netmask, 0, &gc)) ++ ); ++ } ++#else + openvpn_snprintf (command_line, sizeof (command_line), +- "%s addr del dev %s local %s peer %s", +- iproute_path, +- tt->actual_name, +- print_in_addr_t (tt->local, 0, &gc), +- print_in_addr_t (tt->remote_netmask, 0, &gc) +- ); ++ IFCONFIG_PATH "%s addr 0.0.0.0", ++ tt->actual_name ++ ); ++#endif + + msg (M_INFO, "%s", command_line); + system_check (command_line, NULL, S_FATAL, "Linux ip addr del failed"); + + gc_free (&gc); + } +-#endif + close_tun_generic (tt); + free (tt); + } diff --git a/net-misc/openvpn/files/up.sh b/net-misc/openvpn/files/up.sh new file mode 100755 index 00000000..d0879b83 --- /dev/null +++ b/net-misc/openvpn/files/up.sh @@ -0,0 +1,82 @@ +#!/bin/sh +# Copyright (c) 2006-2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# Contributed by Roy Marples (uberlord@gentoo.org) + +# Setup our resolv.conf +# Vitally important that we use the domain entry in resolv.conf so we +# can setup the nameservers are for the domain ONLY in resolvconf if +# we're using a decent dns cache/forwarder like dnsmasq and NOT nscd/libc. +# nscd/libc users will get the VPN nameservers before their other ones +# and will use the first one that responds - maybe the LAN ones? +# non resolvconf users just the the VPN resolv.conf + +# FIXME:- if we have >1 domain, then we have to use search :/ +# We need to add a flag to resolvconf to say +# "these nameservers should only be used for the listed search domains +# if other global nameservers are present on other interfaces" +# This however, will break compatibility with Debians resolvconf +# A possible workaround would be to just list multiple domain lines +# and try and let resolvconf handle it + +if [ "${PEER_DNS}" != "no" ]; then + NS= + DOMAIN= + SEARCH= + i=1 + while true ; do + eval opt=\$foreign_option_${i} + [ -z "${opt}" ] && break + if [ "${opt}" != "${opt#dhcp-option DOMAIN *}" ] ; then + if [ -z "${DOMAIN}" ] ; then + DOMAIN="${opt#dhcp-option DOMAIN *}" + else + SEARCH="${SEARCH:+ }${opt#dhcp-option DOMAIN *}" + fi + elif [ "${opt}" != "${opt#dhcp-option DNS *}" ] ; then + NS="${NS}nameserver ${opt#dhcp-option DNS *}\n" + fi + i=$((${i} + 1)) + done + + if [ -n "${NS}" ] ; then + DNS="# Generated by openvpn for interface ${dev}\n" + if [ -n "${SEARCH}" ] ; then + DNS="${DNS}search ${DOMAIN} ${SEARCH}\n" + else + DNS="${DNS}domain ${DOMAIN}\n" + fi + DNS="${DNS}${NS}" + if [ -x /sbin/resolvconf ] ; then + printf "${DNS}" | /sbin/resolvconf -a "${dev}" + else + # Preserve the existing resolv.conf + if [ -e /etc/resolv.conf ] ; then + cp /etc/resolv.conf /etc/resolv.conf-"${dev}".sv + fi + printf "${DNS}" > /etc/resolv.conf + chmod 644 /etc/resolv.conf + fi + fi +fi + +# Below section is Gentoo specific +# Quick summary - our init scripts are re-entrant and set the SVCNAME env var +# as we could have >1 openvpn service + +if [ -n "${SVCNAME}" ]; then + # If we have a service specific script, run this now + if [ -x /etc/openvpn/"${SVCNAME}"-up.sh ] ; then + /etc/openvpn/"${SVCNAME}"-up.sh "$@" + fi + + # Re-enter the init script to start any dependant services + if ! /etc/init.d/"${SVCNAME}" --quiet status ; then + export IN_BACKGROUND=true + /etc/init.d/${SVCNAME} --quiet start + fi +fi + +exit 0 + +# vim: ts=4 : diff --git a/net-misc/openvpn/openvpn-2.1_rc7.ebuild b/net-misc/openvpn/openvpn-2.1_rc7.ebuild new file mode 100644 index 00000000..326f8739 --- /dev/null +++ b/net-misc/openvpn/openvpn-2.1_rc7.ebuild @@ -0,0 +1,156 @@ +# Copyright 1999-2008 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-x86/net-misc/openvpn/openvpn-2.1_rc7.ebuild,v 1.1 2008/02/09 16:20:56 alonbl Exp $ + +inherit eutils multilib + +DESCRIPTION="OpenVPN is a robust and highly flexible tunneling application compatible with many OSes." +SRC_URI="http://openvpn.net/release/${P}.tar.gz" +HOMEPAGE="http://openvpn.net/" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~hppa ~ppc ~ppc64 ~sh ~sparc ~sparc-fbsd ~x86 ~x86-fbsd" +IUSE="examples iproute2 ipv6 minimal pam passwordsave selinux ssl static pkcs11 threads userland_BSD" + +DEPEND=">=dev-libs/lzo-1.07 + kernel_linux? ( + iproute2? ( sys-apps/iproute2 ) !iproute2? ( sys-apps/net-tools ) + ) + !minimal? ( pam? ( virtual/pam ) ) + selinux? ( sec-policy/selinux-openvpn ) + ssl? ( >=dev-libs/openssl-0.9.6 ) + pkcs11? ( >=dev-libs/pkcs11-helper-1.05 )" + +pkg_setup() { + if use iproute2 ; then + if built_with_use sys-apps/iproute2 minimal ; then + eerror "iproute2 support requires that sys-apps/iproute2 was not" + eerror "built with the minimal USE flag" + die "iproute2 support not available" + fi + fi +} + +src_unpack() { + unpack ${A} + cd "${S}" + + use ipv6 && epatch "${FILESDIR}/${PN}"-2.1_rc6-udp6.patch + epatch "${FILESDIR}/${P}-tap.patch" +} + +src_compile() { + local myconf="" + + if use minimal ; then + myconf="${myconf} --disable-plugins" + myconf="${myconf} --disable-pkcs11" + else + myconf="$(use_enable pkcs11)" + fi + + econf ${myconf} \ + $(use_enable ipv6) \ + $(use_enable passwordsave password-save) \ + $(use_enable ssl) \ + $(use_enable ssl crypto) \ + $(use_enable threads pthread) \ + $(use_enable iproute2) \ + || die "configure failed" + + use static && sed -i -e '/^LIBS/s/LIBS = /LIBS = -static /' Makefile + + emake || die "make failed" + + if ! use minimal ; then + cd plugin + for i in $( ls 2>/dev/null ); do + [[ ${i} == "README" || ${i} == "examples" ]] && continue + [[ ${i} == "auth-pam" ]] && ! use pam && continue + einfo "Building ${i} plugin" + cd "${i}" + emake || die "make failed" + cd .. + done + cd .. + fi +} + +src_install() { + make DESTDIR="${D}" install || die "make install failed" + + # install documentation + dodoc AUTHORS ChangeLog PORTS README + + # Empty dir + dodir /etc/openvpn + keepdir /etc/openvpn + + # Install some helper scripts + exeinto /etc/openvpn + doexe "${FILESDIR}/up.sh" + doexe "${FILESDIR}/down.sh" + + # Install the init script and config file + newinitd "${FILESDIR}/${PN}-2.1.init" openvpn + newconfd "${FILESDIR}/${PN}-2.1.conf" openvpn + + # install examples, controlled by the respective useflag + if use examples ; then + # dodoc does not supportly support directory traversal, #15193 + insinto /usr/share/doc/${PF}/examples + doins -r sample-{config-files,keys,scripts} contrib + prepalldocs + fi + + # Install plugins and easy-rsa + if ! use minimal ; then + cd easy-rsa/2.0 + make install "DESTDIR=${D}/usr/share/${PN}/easy-rsa" + cd ../.. + + exeinto "/usr/$(get_libdir)/${PN}" + doexe plugin/*/*.so + fi +} + +pkg_postinst() { + # Add openvpn user so openvpn servers can drop privs + # Clients should run as root so they can change ip addresses, + # dns information and other such things. + enewgroup openvpn + enewuser openvpn "" "" "" openvpn + + if [[ -n $(ls /etc/openvpn/*/local.conf 2>/dev/null) ]] ; then + ewarn "WARNING: The openvpn init script has changed" + ewarn "" + fi + + einfo "The openvpn init script expects to find the configuration file" + einfo "openvpn.conf in /etc/openvpn along with any extra files it may need." + einfo "" + einfo "To create more VPNs, simply create a new .conf file for it and" + einfo "then create a symlink to the openvpn init script from a link called" + einfo "openvpn.newconfname - like so" + einfo " cd /etc/openvpn" + einfo " ${EDITOR##*/} foo.conf" + einfo " cd /etc/init.d" + einfo " ln -s openvpn openvpn.foo" + einfo "" + einfo "You can then treat openvpn.foo as any other service, so you can" + einfo "stop one vpn and start another if you need to." + + if grep -Eq "^[ \t]*(up|down)[ \t].*" "${ROOT}/etc/openvpn"/*.conf 2>/dev/null ; then + ewarn "" + ewarn "WARNING: If you use the remote keyword then you are deemed to be" + ewarn "a client by our init script and as such we force up,down scripts." + ewarn "These scripts call /etc/openvpn/\$SVCNAME-{up,down}.sh where you" + ewarn "can move your scripts to." + fi + + if ! use minimal ; then + einfo "" + einfo "plugins have been installed into /usr/$(get_libdir)/${PN}" + fi +} -- cgit v1.2.3