dockerfiles/anylink/server/handler/link_tunnel.go

254 lines
7.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"bytes"
"fmt"
"log"
"net"
"net/http"
"net/http/httputil"
"os"
"strings"
"text/template"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/dbdata"
"github.com/bjdgyc/anylink/sessdata"
)
var (
hn string
)
func init() {
// 获取主机名称
hn, _ = os.Hostname()
}
func HttpSetHeader(w http.ResponseWriter, key string, value string) {
w.Header()[key] = []string{value}
}
func HttpAddHeader(w http.ResponseWriter, key string, value string) {
w.Header()[key] = append(w.Header()[key], value)
}
func LinkTunnel(w http.ResponseWriter, r *http.Request) {
// TODO 调试信息输出
if base.GetLogLevel() == base.LogLevelTrace {
hd, _ := httputil.DumpRequest(r, true)
base.Trace("LinkTunnel: ", string(hd))
}
// 判断session-token的值
cookie, err := r.Cookie("webvpn")
if err != nil || cookie.Value == "" {
w.WriteHeader(http.StatusBadRequest)
return
}
sess := sessdata.SToken2Sess(cookie.Value)
if sess == nil {
w.WriteHeader(http.StatusBadRequest)
return
}
// 开启link
cSess := sess.NewConn()
if cSess == nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
// 客户端信息
cstpMtu := r.Header.Get("X-CSTP-MTU")
cstpBaseMtu := r.Header.Get("X-CSTP-Base-MTU")
masterSecret := r.Header.Get("X-DTLS-Master-Secret")
localIp := r.Header.Get("X-Cstp-Local-Address-Ip4")
mobile := r.Header.Get("X-Cstp-License")
cSess.SetMtu(cstpMtu)
cSess.MasterSecret = masterSecret
cSess.RemoteAddr = r.RemoteAddr
cSess.UserAgent = strings.ToLower(r.UserAgent())
cSess.LocalIp = net.ParseIP(localIp)
cstpKeepalive := base.Cfg.CstpKeepalive
cstpDpd := base.Cfg.CstpDpd
cSess.Client = "pc"
if mobile == "mobile" {
// 手机客户端
cstpKeepalive = base.Cfg.MobileKeepalive
cstpDpd = base.Cfg.MobileDpd
cSess.Client = "mobile"
}
cSess.CstpDpd = cstpDpd
dtlsPort := "4433"
if strings.Contains(base.Cfg.ServerDTLSAddr, ":") {
ss := strings.Split(base.Cfg.ServerDTLSAddr, ":")
dtlsPort = ss[1]
}
base.Debug(cSess.IpAddr, cSess.MacHw, sess.Username, mobile)
// 压缩
if cmpName, ok := cSess.SetPickCmp("cstp", r.Header.Get("X-Cstp-Accept-Encoding")); ok {
HttpSetHeader(w, "X-CSTP-Content-Encoding", cmpName)
}
if cmpName, ok := cSess.SetPickCmp("dtls", r.Header.Get("X-Dtls-Accept-Encoding")); ok {
HttpSetHeader(w, "X-DTLS-Content-Encoding", cmpName)
}
// 返回客户端数据
HttpSetHeader(w, "Server", fmt.Sprintf("%s %s", base.APP_NAME, base.APP_VER))
HttpSetHeader(w, "X-CSTP-Version", "1")
HttpSetHeader(w, "X-CSTP-Server-Name", fmt.Sprintf("%s %s", base.APP_NAME, base.APP_VER))
HttpSetHeader(w, "X-CSTP-Protocol", "Copyright (c) 2004 Cisco Systems, Inc.")
HttpSetHeader(w, "X-CSTP-Address", cSess.IpAddr.String()) // 分配的ip地址
HttpSetHeader(w, "X-CSTP-Netmask", sessdata.IpPool.Ipv4Mask.String()) // 子网掩码
HttpSetHeader(w, "X-CSTP-Hostname", hn) // 机器名称
HttpSetHeader(w, "X-CSTP-Base-MTU", cstpBaseMtu)
// 要发布的默认域
if base.Cfg.DefaultDomain != "" {
HttpSetHeader(w, "X-CSTP-Default-Domain", base.Cfg.DefaultDomain)
}
// 设置用户策略
SetUserPolicy(cSess.Username, cSess.Group)
// 允许本地LAN访问vpn网络必须放在路由的第一个
if cSess.Group.AllowLan {
HttpSetHeader(w, "X-CSTP-Split-Exclude", "0.0.0.0/255.255.255.255")
}
// dns地址
for _, v := range cSess.Group.ClientDns {
HttpAddHeader(w, "X-CSTP-DNS", v.Val)
}
// 允许的路由
for _, v := range cSess.Group.RouteInclude {
if v.Val == dbdata.All {
continue
}
HttpAddHeader(w, "X-CSTP-Split-Include", v.IpMask)
}
// 不允许的路由
for _, v := range cSess.Group.RouteExclude {
HttpAddHeader(w, "X-CSTP-Split-Exclude", v.IpMask)
}
HttpSetHeader(w, "X-CSTP-Lease-Duration", "1209600") // ip地址租期
HttpSetHeader(w, "X-CSTP-Session-Timeout", "none")
HttpSetHeader(w, "X-CSTP-Session-Timeout-Alert-Interval", "60")
HttpSetHeader(w, "X-CSTP-Session-Timeout-Remaining", "none")
HttpSetHeader(w, "X-CSTP-Idle-Timeout", "18000")
HttpSetHeader(w, "X-CSTP-Disconnected-Timeout", "18000")
HttpSetHeader(w, "X-CSTP-Keep", "true")
HttpSetHeader(w, "X-CSTP-Tunnel-All-DNS", "false")
HttpSetHeader(w, "X-CSTP-Rekey-Time", "43200") // 172800
HttpSetHeader(w, "X-CSTP-Rekey-Method", "new-tunnel")
HttpSetHeader(w, "X-DTLS-Rekey-Time", "43200")
HttpSetHeader(w, "X-DTLS-Rekey-Method", "new-tunnel")
HttpSetHeader(w, "X-CSTP-DPD", fmt.Sprintf("%d", cstpDpd))
HttpSetHeader(w, "X-CSTP-Keepalive", fmt.Sprintf("%d", cstpKeepalive))
// HttpSetHeader(w, "X-CSTP-Banner", banner.Banner)
HttpSetHeader(w, "X-CSTP-MSIE-Proxy-Lockdown", "true")
HttpSetHeader(w, "X-CSTP-Smartcard-Removal-Disconnect", "true")
HttpSetHeader(w, "X-CSTP-MTU", fmt.Sprintf("%d", cSess.Mtu)) // 1399
HttpSetHeader(w, "X-DTLS-MTU", fmt.Sprintf("%d", cSess.Mtu))
HttpSetHeader(w, "X-DTLS-Session-ID", sess.DtlsSid)
HttpSetHeader(w, "X-DTLS-Port", dtlsPort)
HttpSetHeader(w, "X-DTLS-DPD", fmt.Sprintf("%d", cstpDpd))
HttpSetHeader(w, "X-DTLS-Keepalive", fmt.Sprintf("%d", cstpKeepalive))
HttpSetHeader(w, "X-DTLS12-CipherSuite", "ECDHE-ECDSA-AES128-GCM-SHA256")
HttpSetHeader(w, "X-CSTP-License", "accept")
HttpSetHeader(w, "X-CSTP-Routing-Filtering-Ignore", "false")
HttpSetHeader(w, "X-CSTP-Quarantine", "false")
HttpSetHeader(w, "X-CSTP-Disable-Always-On-VPN", "false")
HttpSetHeader(w, "X-CSTP-Client-Bypass-Protocol", "false")
HttpSetHeader(w, "X-CSTP-TCP-Keepalive", "false")
// 设置域名拆分隧道(移动端不支持)
if mobile != "mobile" {
SetPostAuthXml(cSess.Group, w)
}
w.WriteHeader(http.StatusOK)
hClone := w.Header().Clone()
headers := make([]byte, 0)
buf := bytes.NewBuffer(headers)
_ = hClone.Write(buf)
base.Debug(buf.String())
hj := w.(http.Hijacker)
conn, bufRW, err := hj.Hijack()
if err != nil {
base.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
// 开始数据处理
switch base.Cfg.LinkMode {
case base.LinkModeTUN:
err = LinkTun(cSess)
case base.LinkModeTAP:
err = LinkTap(cSess)
case base.LinkModeMacvtap:
err = LinkMacvtap(cSess)
}
if err != nil {
conn.Close()
base.Error(err)
return
}
dbdata.UserActLogIns.Add(dbdata.UserActLog{
Username: sess.Username,
GroupName: sess.Group,
IpAddr: cSess.IpAddr.String(),
RemoteAddr: cSess.RemoteAddr,
DeviceType: sess.DeviceType,
PlatformVersion: sess.PlatformVersion,
Status: dbdata.UserConnected,
}, cSess.UserAgent)
go LinkCstp(conn, bufRW, cSess)
}
// 设置域名拆分隧道
func SetPostAuthXml(g *dbdata.Group, w http.ResponseWriter) error {
if g.DsExcludeDomains == "" && g.DsIncludeDomains == "" {
return nil
}
tmpl, err := template.New("post_auth_xml").Parse(ds_domains_xml)
if err != nil {
return err
}
var result bytes.Buffer
err = tmpl.Execute(&result, g)
if err != nil {
return err
}
HttpSetHeader(w, "X-CSTP-Post-Auth-XML", result.String())
return nil
}
// 设置用户策略, 覆盖Group的属性值
func SetUserPolicy(username string, g *dbdata.Group) {
userPolicy := dbdata.GetPolicy(username)
if userPolicy.Id != 0 && userPolicy.Status == 1 {
base.Debug(username + " use UserPolicy")
g.AllowLan = userPolicy.AllowLan
g.ClientDns = userPolicy.ClientDns
g.RouteInclude = userPolicy.RouteInclude
g.RouteExclude = userPolicy.RouteExclude
g.DsExcludeDomains = userPolicy.DsExcludeDomains
g.DsIncludeDomains = userPolicy.DsIncludeDomains
}
}