更改目录结构

This commit is contained in:
bjdgyc
2021-03-01 15:46:08 +08:00
parent 3464d1d10e
commit 0f91c779e3
105 changed files with 29099 additions and 96 deletions

6
server/handler/dtls.go Normal file
View File

@@ -0,0 +1,6 @@
package handler
// 暂时没有实现
func startDtls() {
}

178
server/handler/link_auth.go Normal file
View File

@@ -0,0 +1,178 @@
package handler
import (
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"text/template"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/dbdata"
"github.com/bjdgyc/anylink/sessdata"
)
func LinkAuth(w http.ResponseWriter, r *http.Request) {
// 判断anyconnect客户端
userAgent := strings.ToLower(r.UserAgent())
xAggregateAuth := r.Header.Get("X-Aggregate-Auth")
xTranscendVersion := r.Header.Get("X-Transcend-Version")
if !(strings.Contains(userAgent, "anyconnect") &&
xAggregateAuth == "1" && xTranscendVersion == "1") {
w.WriteHeader(http.StatusForbidden)
fmt.Fprintf(w, "error request")
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
defer r.Body.Close()
cr := ClientRequest{}
err = xml.Unmarshal(body, &cr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
// fmt.Printf("%+v \n", cr)
setCommonHeader(w)
if cr.Type == "logout" {
// 退出删除session信息
if cr.SessionToken != "" {
sessdata.DelSessByStoken(cr.SessionToken)
}
w.WriteHeader(http.StatusOK)
return
}
if cr.Type == "init" {
w.WriteHeader(http.StatusOK)
data := RequestData{Group: cr.GroupSelect, Groups: dbdata.GetGroupNames()}
tplRequest(tpl_request, w, data)
return
}
// 登陆参数判断
if cr.Type != "auth-reply" {
w.WriteHeader(http.StatusBadRequest)
return
}
// TODO 用户密码校验
err = dbdata.CheckUser(cr.Auth.Username, cr.Auth.Password, cr.GroupSelect)
if err != nil {
base.Warn(err)
w.WriteHeader(http.StatusOK)
data := RequestData{Group: cr.GroupSelect, Groups: dbdata.GetGroupNames(), Error: "用户名或密码错误"}
tplRequest(tpl_request, w, data)
return
}
// if !ok {
// w.WriteHeader(http.StatusOK)
// data := RequestData{Group: cr.GroupSelect, Groups: base.Cfg.UserGroups, Error: "请先激活用户"}
// tplRequest(tpl_request, w, data)
// return
// }
// 创建新的session信息
sess := sessdata.NewSession("")
sess.Username = cr.Auth.Username
sess.Group = cr.GroupSelect
sess.MacAddr = strings.ToLower(cr.MacAddressList.MacAddress)
sess.UniqueIdGlobal = cr.DeviceId.UniqueIdGlobal
other := &dbdata.SettingOther{}
_ = dbdata.SettingGet(other)
rd := RequestData{SessionId: sess.Sid, SessionToken: sess.Sid + "@" + sess.Token,
Banner: other.Banner}
w.WriteHeader(http.StatusOK)
tplRequest(tpl_complete, w, rd)
base.Debug("login", cr.Auth.Username)
}
const (
tpl_request = iota
tpl_complete
)
func tplRequest(typ int, w io.Writer, data RequestData) {
if typ == tpl_request {
t, _ := template.New("auth_request").Parse(auth_request)
_ = t.Execute(w, data)
return
}
if strings.Contains(data.Banner, "\n") {
// 替换xml文件的换行符
data.Banner = strings.ReplaceAll(data.Banner, "\n", "
")
}
t, _ := template.New("auth_complete").Parse(auth_complete)
_ = t.Execute(w, data)
}
// 设置输出信息
type RequestData struct {
Groups []string
Group string
Error string
// complete
SessionId string
SessionToken string
Banner string
}
var auth_request = `<?xml version="1.0" encoding="UTF-8"?>
<config-auth client="vpn" type="auth-request" aggregate-auth-version="2">
<opaque is-for="sg">
<tunnel-group>{{.Group}}</tunnel-group>
<group-alias>{{.Group}}</group-alias>
<aggauth-handle>168179266</aggauth-handle>
<config-hash>1595829378234</config-hash>
<auth-method>multiple-cert</auth-method>
<auth-method>single-sign-on-v2</auth-method>
</opaque>
<auth id="main">
<title>Login</title>
<message>请输入你的用户名和密码</message>
<banner></banner>
{{if .Error}}
<error id="88" param1="{{.Error}}" param2="">登陆失败: %s</error>
{{end}}
<form>
<input type="text" name="username" label="Username:"></input>
<input type="password" name="password" label="Password:"></input>
<select name="group_list" label="GROUP:">
{{range $v := .Groups}}
<option {{if eq $v $.Group}} selected="true"{{end}}>{{$v}}</option>
{{end}}
</select>
</form>
</auth>
</config-auth>
`
var auth_complete = `<?xml version="1.0" encoding="UTF-8"?>
<config-auth client="vpn" type="complete" aggregate-auth-version="2">
<session-id>{{.SessionId}}</session-id>
<session-token>{{.SessionToken}}</session-token>
<auth id="success">
<banner>{{.Banner}}</banner>
<message id="0" param1="" param2=""></message>
</auth>
<capabilities>
<crypto-supported>ssl-dhe</crypto-supported>
</capabilities>
<config client="vpn" type="private">
<vpn-base-config>
<server-cert-hash>240B97A685B2BFA66AD699B90AAC49EA66495D69</server-cert-hash>
</vpn-base-config>
<opaque is-for="vpn-client"></opaque>
</config>
</config-auth>
`

View File

@@ -0,0 +1,66 @@
package handler
import (
"encoding/xml"
"log"
"net/http"
"os/exec"
)
const BufferSize = 2048
type ClientRequest struct {
XMLName xml.Name `xml:"config-auth"`
Client string `xml:"client,attr"` // 一般都是 vpn
Type string `xml:"type,attr"` // 请求类型 init logout auth-reply
AggregateAuthVersion string `xml:"aggregate-auth-version,attr"` // 一般都是 2
Version string `xml:"version"` // 客户端版本号
GroupAccess string `xml:"group-access"` // 请求的地址
GroupSelect string `xml:"group-select"` // 选择的组名
SessionId string `xml:"session-id"`
SessionToken string `xml:"session-token"`
Auth auth `xml:"auth"`
DeviceId deviceId `xml:"device-id"`
MacAddressList macAddressList `xml:"mac-address-list"`
}
type auth struct {
Username string `xml:"username"`
Password string `xml:"password"`
}
type deviceId struct {
ComputerName string `xml:"computer-name,attr"`
DeviceType string `xml:"device-type,attr"`
PlatformVersion string `xml:"platform-version,attr"`
UniqueId string `xml:"unique-id,attr"`
UniqueIdGlobal string `xml:"unique-id-global,attr"`
}
type macAddressList struct {
MacAddress string `xml:"mac-address"`
}
func setCommonHeader(w http.ResponseWriter) {
// Content-Length Date 默认已经存在
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("Pragma", "no-cache")
w.Header().Set("Transfer-Encoding", "chunked")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
w.Header().Set("X-Aggregate-Auth", "1")
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
}
func execCmd(cmdStrs []string) error {
for _, cmdStr := range cmdStrs {
cmd := exec.Command("bash", "-c", cmdStr)
b, err := cmd.CombinedOutput()
if err != nil {
log.Println(string(b), err)
return err
}
}
return nil
}

115
server/handler/link_cstp.go Normal file
View File

@@ -0,0 +1,115 @@
package handler
import (
"encoding/binary"
"net"
"time"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/sessdata"
)
func LinkCstp(conn net.Conn, cSess *sessdata.ConnSession) {
defer func() {
base.Debug("LinkCstp return", cSess.IpAddr)
_ = conn.Close()
cSess.Close()
}()
var (
err error
n int
dataLen uint16
dead = time.Duration(cSess.CstpDpd+5) * time.Second
)
go cstpWrite(conn, cSess)
for {
// 设置超时限制
err = conn.SetReadDeadline(time.Now().Add(dead))
if err != nil {
base.Error("SetDeadline: ", err)
return
}
hdata := make([]byte, BufferSize)
n, err = conn.Read(hdata)
if err != nil {
base.Error("read hdata: ", err)
return
}
// 限流设置
err = cSess.RateLimit(n, true)
if err != nil {
base.Error(err)
}
switch hdata[6] {
case 0x07: // KEEPALIVE
// do nothing
// base.Debug("recv keepalive", cSess.IpAddr)
case 0x05: // DISCONNECT
base.Debug("DISCONNECT", cSess.IpAddr)
return
case 0x03: // DPD-REQ
// base.Debug("recv DPD-REQ", cSess.IpAddr)
if payloadOut(cSess, sessdata.LTypeIPData, 0x04, nil) {
return
}
case 0x04:
// log.Println("recv DPD-RESP")
case 0x00: // DATA
dataLen = binary.BigEndian.Uint16(hdata[4:6]) // 4,5
if payloadIn(cSess, sessdata.LTypeIPData, 0x00, hdata[8:8+dataLen]) {
return
}
}
}
}
func cstpWrite(conn net.Conn, cSess *sessdata.ConnSession) {
defer func() {
base.Debug("cstpWrite return", cSess.IpAddr)
_ = conn.Close()
cSess.Close()
}()
var (
err error
n int
header []byte
payload *sessdata.Payload
)
for {
select {
case payload = <-cSess.PayloadOut:
case <-cSess.CloseChan:
return
}
if payload.LType != sessdata.LTypeIPData {
continue
}
header = []byte{'S', 'T', 'F', 0x01, 0x00, 0x00, payload.PType, 0x00}
if payload.PType == 0x00 { // data
binary.BigEndian.PutUint16(header[4:6], uint16(len(payload.Data)))
header = append(header, payload.Data...)
}
n, err = conn.Write(header)
if err != nil {
base.Error("write err", err)
return
}
// 限流设置
err = cSess.RateLimit(n, false)
if err != nil {
base.Error(err)
}
}
}

View File

@@ -0,0 +1,39 @@
package handler
import (
"fmt"
"net/http"
"strings"
"github.com/bjdgyc/anylink/admin"
)
func LinkHome(w http.ResponseWriter, r *http.Request) {
// fmt.Println(r.RemoteAddr)
// hu, _ := httputil.DumpRequest(r, true)
// fmt.Println("DumpHome: ", string(hu))
connection := strings.ToLower(r.Header.Get("Connection"))
userAgent := strings.ToLower(r.UserAgent())
if connection == "close" && strings.Contains(userAgent, "anyconnect") {
w.Header().Set("Connection", "close")
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "hello world")
}
func LinkOtpQr(w http.ResponseWriter, r *http.Request) {
_ = r.ParseForm()
idS := r.FormValue("id")
jwtToken := r.FormValue("jwt")
data, err := admin.GetJwtData(jwtToken)
if err != nil || idS != fmt.Sprint(data["id"]) {
w.WriteHeader(http.StatusForbidden)
return
}
admin.UserOtpQr(w, r)
}

241
server/handler/link_tap.go Normal file
View File

@@ -0,0 +1,241 @@
package handler
import (
"fmt"
"net"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/pkg/arpdis"
"github.com/bjdgyc/anylink/sessdata"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/songgao/packets/ethernet"
"github.com/songgao/water"
"github.com/songgao/water/waterutil"
)
const bridgeName = "anylink0"
var (
bridgeIp net.IP
bridgeHw net.HardwareAddr
)
func checkTap() {
brFace, err := net.InterfaceByName(bridgeName)
if err != nil {
base.Fatal("testTap err: ", err)
}
bridgeHw = brFace.HardwareAddr
addrs, err := brFace.Addrs()
if err != nil {
base.Fatal("testTap err: ", err)
}
for _, addr := range addrs {
ip, _, err := net.ParseCIDR(addr.String())
if err != nil || ip.To4() == nil {
continue
}
bridgeIp = ip
}
if bridgeIp == nil && bridgeHw == nil {
base.Fatal("bridgeIp is err")
}
if !sessdata.IpPool.Ipv4IPNet.Contains(bridgeIp) {
base.Fatal("bridgeIp or Ip network err")
}
}
// 创建tap网卡
func LinkTap(cSess *sessdata.ConnSession) error {
cfg := water.Config{
DeviceType: water.TAP,
}
ifce, err := water.New(cfg)
if err != nil {
base.Error(err)
return err
}
cSess.TunName = ifce.Name()
// arp on
cmdstr1 := fmt.Sprintf("ip link set dev %s up mtu %d multicast on", ifce.Name(), cSess.Mtu)
cmdstr2 := fmt.Sprintf("sysctl -w net.ipv6.conf.%s.disable_ipv6=1", ifce.Name())
cmdstr3 := fmt.Sprintf("ip link set dev %s master %s", ifce.Name(), bridgeName)
cmdStrs := []string{cmdstr1, cmdstr2, cmdstr3}
err = execCmd(cmdStrs)
if err != nil {
base.Error(err)
_ = ifce.Close()
return err
}
go tapRead(ifce, cSess)
go tapWrite(ifce, cSess)
return nil
}
func tapWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
defer func() {
base.Debug("LinkTap return", cSess.IpAddr)
cSess.Close()
_ = ifce.Close()
}()
var (
err error
payload *sessdata.Payload
)
for {
select {
case payload = <-cSess.PayloadIn:
case <-cSess.CloseChan:
return
}
var frame ethernet.Frame
switch payload.LType {
default:
// log.Println(payload)
case sessdata.LTypeEthernet:
frame = payload.Data
case sessdata.LTypeIPData: // 需要转换成 Ethernet 数据
data := payload.Data
ip_src := waterutil.IPv4Source(data)
if waterutil.IsIPv6(data) || !ip_src.Equal(cSess.IpAddr) {
// 过滤掉IPv6的数据
// 非分配给客户端ip直接丢弃
continue
}
// packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default)
// fmt.Println("get:", packet)
ip_dst := waterutil.IPv4Destination(data)
// fmt.Println("get:", ip_src, ip_dst)
var dstHw net.HardwareAddr
if !sessdata.IpPool.Ipv4IPNet.Contains(ip_dst) || ip_dst.Equal(sessdata.IpPool.Ipv4Gateway) {
// 不是同一网段使用网关mac地址
dstAddr := arpdis.Lookup(sessdata.IpPool.Ipv4Gateway, false)
dstHw = dstAddr.HardwareAddr
} else {
dstAddr := arpdis.Lookup(ip_dst, true)
// fmt.Println("dstAddr", dstAddr)
if dstAddr != nil {
dstHw = dstAddr.HardwareAddr
} else {
dstHw = bridgeHw
}
}
// fmt.Println("Gateway", ip_dst, dstAddr.HardwareAddr)
frame.Prepare(dstHw, cSess.MacHw, ethernet.NotTagged, ethernet.IPv4, len(data))
copy(frame[12+2:], data)
}
// packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)
// fmt.Println("write:", packet)
_, err = ifce.Write(frame)
if err != nil {
base.Error("tap Write err", err)
return
}
}
}
func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
defer func() {
base.Debug("tapRead return", cSess.IpAddr)
_ = ifce.Close()
}()
var (
err error
n int
buf []byte
)
for {
var frame ethernet.Frame
frame.Resize(BufferSize)
n, err = ifce.Read(frame)
if err != nil {
base.Error("tap Read err", n, err)
return
}
frame = frame[:n]
switch frame.Ethertype() {
default:
// packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)
// fmt.Println(packet)
continue
case ethernet.IPv6:
continue
case ethernet.IPv4:
// 发送IP数据
data := frame.Payload()
ip_dst := waterutil.IPv4Destination(data)
if !ip_dst.Equal(cSess.IpAddr) {
// 过滤非本机地址
// log.Println(ip_dst, sess.Ip)
continue
}
// packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default)
// fmt.Println("put:", packet)
if payloadOut(cSess, sessdata.LTypeIPData, 0x00, data) {
return
}
case ethernet.ARP:
// 暂时仅实现了ARP协议
packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)
layer := packet.Layer(layers.LayerTypeARP)
arpReq := layer.(*layers.ARP)
if !cSess.IpAddr.Equal(arpReq.DstProtAddress) {
// 过滤非本机地址
continue
}
// fmt.Println("arp", net.IP(arpReq.SourceProtAddress), sess.Ip)
// fmt.Println(packet)
// 返回ARP数据
src := &arpdis.Addr{IP: cSess.IpAddr, HardwareAddr: cSess.MacHw}
dst := &arpdis.Addr{IP: arpReq.SourceProtAddress, HardwareAddr: frame.Source()}
buf, err = arpdis.NewARPReply(src, dst)
if err != nil {
base.Error(err)
return
}
// 从接受的arp信息添加arp地址
addr := &arpdis.Addr{
IP: make([]byte, len(arpReq.SourceProtAddress)),
HardwareAddr: make([]byte, len(frame.Source())),
}
// addr.IP = arpReq.SourceProtAddress
// addr.HardwareAddr = frame.Source()
copy(addr.IP, arpReq.SourceProtAddress)
copy(addr.HardwareAddr, frame.Source())
arpdis.Add(addr)
if payloadIn(cSess, sessdata.LTypeEthernet, 0x00, buf) {
return
}
}
}
}

122
server/handler/link_tun.go Normal file
View File

@@ -0,0 +1,122 @@
package handler
import (
"fmt"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/sessdata"
"github.com/songgao/water"
)
func checkTun() {
// 测试tun
cfg := water.Config{
DeviceType: water.TUN,
}
ifce, err := water.New(cfg)
if err != nil {
base.Fatal("open tun err: ", err)
}
defer ifce.Close()
// 测试ip命令
cmdstr := fmt.Sprintf("ip link set dev %s up mtu %s multicast off", ifce.Name(), "1399")
err = execCmd([]string{cmdstr})
if err != nil {
base.Fatal("testTun err: ", err)
}
}
// 创建tun网卡
func LinkTun(cSess *sessdata.ConnSession) error {
cfg := water.Config{
DeviceType: water.TUN,
}
ifce, err := water.New(cfg)
if err != nil {
base.Error(err)
return err
}
// log.Printf("Interface Name: %s\n", ifce.Name())
cSess.SetTunName(ifce.Name())
// cSess.TunName = ifce.Name()
cmdstr1 := fmt.Sprintf("ip link set dev %s up mtu %d multicast off", ifce.Name(), cSess.Mtu)
cmdstr2 := fmt.Sprintf("ip addr add dev %s local %s peer %s/32",
ifce.Name(), base.Cfg.Ipv4Gateway, cSess.IpAddr)
cmdstr3 := fmt.Sprintf("sysctl -w net.ipv6.conf.%s.disable_ipv6=1", ifce.Name())
cmdStrs := []string{cmdstr1, cmdstr2, cmdstr3}
err = execCmd(cmdStrs)
if err != nil {
base.Error(err)
_ = ifce.Close()
return err
}
go tunRead(ifce, cSess)
go tunWrite(ifce, cSess)
return nil
}
func tunWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
defer func() {
base.Debug("LinkTun return", cSess.IpAddr)
cSess.Close()
_ = ifce.Close()
}()
var (
err error
payload *sessdata.Payload
)
for {
select {
case payload = <-cSess.PayloadIn:
case <-cSess.CloseChan:
return
}
_, err = ifce.Write(payload.Data)
if err != nil {
base.Error("tun Write err", err)
return
}
}
}
func tunRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
defer func() {
base.Debug("tunRead return", cSess.IpAddr)
_ = ifce.Close()
}()
var (
err error
n int
)
for {
data := make([]byte, BufferSize)
n, err = ifce.Read(data)
if err != nil {
base.Error("tun Read err", n, err)
return
}
data = data[:n]
// ip_src := waterutil.IPv4Source(data)
// ip_dst := waterutil.IPv4Destination(data)
// ip_port := waterutil.IPv4DestinationPort(data)
// fmt.Println("sent:", ip_src, ip_dst, ip_port)
// packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default)
// fmt.Println("read:", packet)
if payloadOut(cSess, sessdata.LTypeIPData, 0x00, data) {
return
}
}
}

View File

@@ -0,0 +1,161 @@
package handler
import (
"bytes"
"fmt"
"log"
"net"
"net/http"
"os"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/sessdata"
)
var hn string
func init() {
// 获取主机名称
hn, _ = os.Hostname()
}
func LinkTunnel(w http.ResponseWriter, r *http.Request) {
// TODO 调试信息输出
// hd, _ := httputil.DumpRequest(r, true)
// fmt.Println("DumpRequest: ", string(hd))
// fmt.Println("LinkTunnel", r.RemoteAddr)
// 判断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")
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.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
base.Debug(cSess.IpAddr, cSess.MacHw, sess.Username, mobile)
// 返回客户端数据
w.Header().Set("Server", fmt.Sprintf("%s %s", base.APP_NAME, base.APP_VER))
w.Header().Set("X-CSTP-Version", "1")
w.Header().Set("X-CSTP-Protocol", "Copyright (c) 2004 Cisco Systems, Inc.")
w.Header().Set("X-CSTP-Address", cSess.IpAddr.String()) // 分配的ip地址
w.Header().Set("X-CSTP-Netmask", sessdata.IpPool.Ipv4Mask.String()) // 子网掩码
w.Header().Set("X-CSTP-Hostname", hn) // 机器名称
// 允许本地LAN访问vpn网络必须放在路由的第一个
if cSess.Group.AllowLan {
w.Header().Set("X-CSTP-Split-Exclude", "0.0.0.0/255.255.255.255")
}
// dns地址
for _, v := range cSess.Group.ClientDns {
w.Header().Add("X-CSTP-DNS", v.Val)
}
// 允许的路由
for _, v := range cSess.Group.RouteInclude {
w.Header().Add("X-CSTP-Split-Include", v.IpMask)
}
// 不允许的路由
for _, v := range cSess.Group.RouteExclude {
w.Header().Add("X-CSTP-Split-Exclude", v.IpMask)
}
w.Header().Set("X-CSTP-Lease-Duration", fmt.Sprintf("%d", base.Cfg.IpLease)) // ip地址租期
w.Header().Set("X-CSTP-Session-Timeout", "none")
w.Header().Set("X-CSTP-Session-Timeout-Alert-Interval", "60")
w.Header().Set("X-CSTP-Session-Timeout-Remaining", "none")
w.Header().Set("X-CSTP-Idle-Timeout", "18000")
w.Header().Set("X-CSTP-Disconnected-Timeout", "18000")
w.Header().Set("X-CSTP-Keep", "true")
w.Header().Set("X-CSTP-Tunnel-All-DNS", "false")
w.Header().Set("X-CSTP-Rekey-Time", "172800")
w.Header().Set("X-CSTP-Rekey-Method", "new-tunnel")
w.Header().Set("X-CSTP-DPD", fmt.Sprintf("%d", cstpDpd))
w.Header().Set("X-CSTP-Keepalive", fmt.Sprintf("%d", cstpKeepalive))
// w.Header().Set("X-CSTP-Banner", banner.Banner)
w.Header().Set("X-CSTP-MSIE-Proxy-Lockdown", "true")
w.Header().Set("X-CSTP-Smartcard-Removal-Disconnect", "true")
w.Header().Set("X-CSTP-MTU", fmt.Sprintf("%d", cSess.Mtu)) // 1399
w.Header().Set("X-DTLS-MTU", fmt.Sprintf("%d", cSess.Mtu))
w.Header().Set("X-DTLS-Session-ID", sess.DtlsSid)
w.Header().Set("X-DTLS-Port", "4433")
w.Header().Set("X-DTLS-Keepalive", fmt.Sprintf("%d", base.Cfg.CstpKeepalive))
w.Header().Set("X-DTLS-Rekey-Time", "5400")
w.Header().Set("X-DTLS12-CipherSuite", "ECDHE-ECDSA-AES128-GCM-SHA256")
// w.Header().Set("X-DTLS12-CipherSuite", "ECDHE-RSA-AES128-GCM-SHA256")
w.Header().Set("X-CSTP-License", "accept")
w.Header().Set("X-CSTP-Routing-Filtering-Ignore", "false")
w.Header().Set("X-CSTP-Quarantine", "false")
w.Header().Set("X-CSTP-Disable-Always-On-VPN", "false")
w.Header().Set("X-CSTP-Client-Bypass-Protocol", "false")
w.Header().Set("X-CSTP-TCP-Keepalive", "false")
// w.Header().Set("X-CSTP-Post-Auth-XML", ``)
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, _, 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)
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
go LinkCstp(conn, cSess)
}

91
server/handler/payload.go Normal file
View File

@@ -0,0 +1,91 @@
package handler
import (
"github.com/bjdgyc/anylink/dbdata"
"github.com/bjdgyc/anylink/sessdata"
"github.com/songgao/water/waterutil"
)
func payloadIn(cSess *sessdata.ConnSession, lType sessdata.LType, pType byte, data []byte) bool {
payload := &sessdata.Payload{
LType: lType,
PType: pType,
Data: data,
}
return payloadInData(cSess, payload)
}
func payloadInData(cSess *sessdata.ConnSession, payload *sessdata.Payload) bool {
// 进行Acl规则判断
check := checkLinkAcl(cSess.Group, payload)
if !check {
// 校验不通过直接丢弃
return false
}
closed := false
select {
case cSess.PayloadIn <- payload:
case <-cSess.CloseChan:
closed = true
}
return closed
}
func payloadOut(cSess *sessdata.ConnSession, lType sessdata.LType, pType byte, data []byte) bool {
payload := &sessdata.Payload{
LType: lType,
PType: pType,
Data: data,
}
return payloadOutData(cSess, payload)
}
func payloadOutData(cSess *sessdata.ConnSession, payload *sessdata.Payload) bool {
closed := false
select {
case cSess.PayloadOut <- payload:
case <-cSess.CloseChan:
closed = true
}
return closed
}
// Acl规则校验
func checkLinkAcl(group *dbdata.Group, payload *sessdata.Payload) bool {
if payload.LType == sessdata.LTypeIPData && payload.PType == 0x00 && len(group.LinkAcl) > 0 {
} else {
return true
}
ip_dst := waterutil.IPv4Destination(payload.Data)
ip_port := waterutil.IPv4DestinationPort(payload.Data)
// fmt.Println("sent:", ip_dst, ip_port)
// 优先放行dns端口
for _, v := range group.ClientDns {
if v.Val == ip_dst.String() && ip_port == 53 {
return true
}
}
for _, v := range group.LinkAcl {
// 循环判断ip和端口
if v.IpNet.Contains(ip_dst) {
if v.Port == ip_port || v.Port == 0 {
if v.Action == dbdata.Allow {
return true
} else {
return false
}
}
}
}
return false
}

81
server/handler/server.go Normal file
View File

@@ -0,0 +1,81 @@
package handler
import (
"crypto/tls"
"fmt"
"log"
"net"
"net/http"
"time"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/pkg/proxyproto"
"github.com/gorilla/mux"
)
func GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) {
cert, err := tls.LoadX509KeyPair(base.Cfg.CertFile, base.Cfg.CertKey)
return &cert, err
}
func startTls() {
addr := base.Cfg.ServerAddr
certFile := base.Cfg.CertFile
keyFile := base.Cfg.CertKey
// 设置tls信息
tlsConfig := &tls.Config{
NextProtos: []string{"http/1.1"},
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: true,
GetCertificate: GetCertificate,
}
srv := &http.Server{
Addr: addr,
Handler: initRoute(),
TLSConfig: tlsConfig,
ErrorLog: base.GetBaseLog(),
}
var ln net.Listener
ln, err := net.Listen("tcp", addr)
if err != nil {
log.Fatal(err)
}
defer ln.Close()
if base.Cfg.ProxyProtocol {
ln = &proxyproto.Listener{Listener: ln, ProxyHeaderTimeout: time.Second * 5}
}
base.Info("listen server", addr)
err = srv.ServeTLS(ln, certFile, keyFile)
if err != nil {
base.Fatal(err)
}
}
func initRoute() http.Handler {
r := mux.NewRouter()
r.HandleFunc("/", LinkHome).Methods(http.MethodGet)
r.HandleFunc("/", LinkAuth).Methods(http.MethodPost)
r.HandleFunc("/CSCOSSLC/tunnel", LinkTunnel).Methods(http.MethodConnect)
r.HandleFunc("/otp_qr", LinkOtpQr).Methods(http.MethodGet)
r.PathPrefix("/files/").Handler(
http.StripPrefix("/files/",
http.FileServer(http.Dir(base.Cfg.FilesPath)),
),
)
r.NotFoundHandler = http.HandlerFunc(notFound)
return r
}
func notFound(w http.ResponseWriter, r *http.Request) {
// fmt.Println(r.RemoteAddr)
// hu, _ := httputil.DumpRequest(r, true)
// fmt.Println("NotFound: ", string(hu))
w.WriteHeader(http.StatusNotFound)
fmt.Fprintln(w, "404 page not found")
}

25
server/handler/start.go Normal file
View File

@@ -0,0 +1,25 @@
package handler
import (
"github.com/bjdgyc/anylink/admin"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/dbdata"
"github.com/bjdgyc/anylink/sessdata"
)
func Start() {
dbdata.Start()
sessdata.Start()
checkTun()
if base.Cfg.LinkMode == base.LinkModeTAP {
checkTap()
}
go admin.StartAdmin()
go startTls()
go startDtls()
}
func Stop() {
_ = dbdata.Stop()
}