Cảm ơn Sasha Pachev vì bản hack hay ở trên.
vpnagentd
cũng gây rối với trình phân giải bằng cách ghi đè các thay đổi được thực hiện /etc/resolv.conf
. Tôi đã giải quyết nó bằng cách cuối cùng chiến thắng cuộc đua chống lại nó:
#!/bin/bash
dnsfix() {
[ -f /etc/resolv.conf.vpnbackup ] || echo "Not connected?" >&2 || return 0 # do nothing in case of failure
while ! diff -q /etc/resolv.conf /etc/resolv.conf.vpnbackup #>/dev/null
do
cat /etc/resolv.conf.vpnbackup >/etc/resolv.conf
done
chattr +i /etc/resolv.conf
diff -q /etc/resolv.conf /etc/resolv.conf.vpnbackup >/dev/null
}
while ! dnsfix
do
echo "Retrying..."
chattr -i /etc/resolv.conf
done
Đừng quên chattr -i /etc/resolv.conf
khi ngắt kết nối.
Tôi đang cố gắng giải quyết nó bằng cách chặn cuộc gọi lại, như đối với phương thức định tuyến ở trên, nhưng chưa thể tìm thấy phương thức gọi lại tương ứng.
Update1 / 2: Một strace
tiết lộ vpnagentd
đang sử dụng inotify
API để theo dõi các thay đổi của tệp trình phân giải. Từ đó trở đi nó xuống dốc. Đây là bản hack bổ sung:
int _ZN18CFileSystemWatcher11AddNewWatchESsj(void *string, unsigned int integer)
{
return 0;
}
Đó là một chút quá mức cần thiết, được cấp, vì nó vô hiệu hóa tất cả các tập tin xem cho các đại lý. Nhưng dường như hoạt động tốt.
Tập lệnh trình bao bọc máy khách vpn bên dưới tích hợp tất cả các chức năng (được cập nhật để bao gồm bản hack bổ sung này). chattr
không còn được sử dụng / cần thiết.
Cập nhật 3: Đã sửa lỗi cài đặt tên người dùng / mật khẩu trong tập lệnh. Bây giờ nó sử dụng một vpn.conf
tệp có định dạng được mô tả dưới đây (và quyền chỉ gốc).
#!/bin/bash
# Change this as needed
CONF="/etc/vpnc/vpn.conf"
# vpn.conf format
#gateway <IP>
#username <username>
#password <password>
#delete_routes <"route spec"...> eg. "default gw 0.0.0.0 dev cscotun0"
#add_routes <"route spec"...> eg. "-net 192.168.10.0 netmask 255.255.255.0 dev cscotun0" "-host 10.10.10.1 dev cscotun0"
ANYCONNECT="/opt/cisco/anyconnect"
usage() {
echo "Usage: $0 {connect|disconnect|state|stats|hack}"
exit 1
}
CMD="$1"
[ -z "$CMD" ] && usage
ID=`id -u`
VPNC="$ANYCONNECT/bin/vpn"
dnsfix() {
[ -f /etc/resolv.conf.vpnbackup ] || echo "Not connected?" >&2 || return 0 # do nothing in case of failure
while ! diff -q /etc/resolv.conf /etc/resolv.conf.vpnbackup >/dev/null
do
cat /etc/resolv.conf.vpnbackup >/etc/resolv.conf
done
# chattr +i /etc/resolv.conf
diff -q /etc/resolv.conf /etc/resolv.conf.vpnbackup >/dev/null
}
case "$CMD" in
"connect")
[ $ID -ne 0 ] && echo "Needs root." && exit 1
HOST=`grep ^gateway $CONF | awk '{print $2}'`
USER=`grep ^user $CONF | awk '{print $2}'`
PASS=`grep ^password $CONF | awk '{print $2}'`
OLDIFS=$IFS
IFS='"'
DEL_ROUTES=(`sed -n '/^delete_routes/{s/delete_routes[ \t\"]*//;s/\"[ \t]*\"/\"/g;p}' $CONF`)
ADD_ROUTES=(`sed -n '/^add_routes/{s/add_routes[ \t\"]*//;s/\"[ \t]*\"/\"/g;p}' $CONF`)
IFS=$OLDIFS
/usr/bin/expect <<EOF
set vpn_client "$VPNC";
set ip "$HOST";
set user "$USER";
set pass "$PASS";
set timeout 5
spawn \$vpn_client connect \$ip
match_max 100000
expect {
timeout {
puts "timeout error\n"
spawn killall \$vpn_client
exit 1
}
">> The VPN client is not connected." { exit 0};
">> state: Disconnecting" { exit 0};
"Connect Anyway?"
}
sleep .1
send -- "y\r"
expect {
timeout {
puts "timeout error\n"
spawn killall \$vpn_client
exit 1
}
"Username:"
}
sleep .1
send -- "\$user\r"
expect {
timeout {
puts "timeout error\n"
spawn killall \$vpn_client
exit 1
}
"Password: "
}
send -- "\$pass\r";
expect eof
EOF
sleep 2
# iptables
iptables-save | grep -v DROP | iptables-restore
# routes
for ROUTE in "${DEL_ROUTES[@]}"
do
# echo route del $ROUTE
route del $ROUTE
done
for ROUTE in "${ADD_ROUTES[@]}"
do
# echo route add $ROUTE
route add $ROUTE
done
# dns
while ! dnsfix
do
echo "Try again..."
# chattr -i /etc/resolv.conf
done
echo "done."
;;
"disconnect")
# [ $ID -ne 0 ] && echo "Needs root." && exit 1
# dns
# chattr -i /etc/resolv.conf
$VPNC disconnect
;;
"state"|"stats")
$VPNC $CMD
;;
"hack")
[ $ID -ne 0 ] && echo "Needs root." && exit 1
/etc/init.d/vpnagentd stop
sleep 1
killall -9 vpnagentd 2>/dev/null
cat - >/tmp/hack.c <<EOF
#include <sys/socket.h>
#include <linux/netlink.h>
int _ZN27CInterfaceRouteMonitorLinux20routeCallbackHandlerEv()
{
int fd=50; // max fd to try
char buf[8192];
struct sockaddr_nl sa;
socklen_t len = sizeof(sa);
while (fd) {
if (!getsockname(fd, (struct sockaddr *)&sa, &len)) {
if (sa.nl_family == AF_NETLINK) {
ssize_t n = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
}
}
fd--;
}
return 0;
}
int _ZN18CFileSystemWatcher11AddNewWatchESsj(void *string, unsigned int integer)
{
return 0;
}
EOF
gcc -o /tmp/libhack.so -shared -fPIC /tmp/hack.c
mv /tmp/libhack.so $ANYCONNECT
sed -i "s+^\([ \t]*\)$ANYCONNECT/bin/vpnagentd+\1LD_PRELOAD=$ANYCONNECT/lib/libhack.so $ANYCONNECT/bin/vpnagentd+" /etc/init.d/vpnagentd
rm -f /tmp/hack.c
/etc/init.d/vpnagentd start
echo "done."
;;
*)
usage
;;
esac