mirror of
https://github.com/bjdgyc/anylink.git
synced 2025-08-11 01:37:26 +08:00
增加基于tap设备的桥接访问模式
This commit is contained in:
@@ -3,12 +3,14 @@ package handler
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
const BufferSize = 2048
|
||||
|
||||
type ClientRequest struct {
|
||||
XMLName xml.Name `xml:"config-auth"`
|
||||
Client string `xml:"client,attr"` // 一般都是 vpn
|
||||
@@ -42,19 +44,19 @@ type macAddressList struct {
|
||||
}
|
||||
|
||||
// 判断anyconnect客户端
|
||||
func checkVpnClient(h httprouter.Handle) httprouter.Handle {
|
||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
func checkLinkClient(h http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO 调试信息输出
|
||||
// hd, _ := httputil.DumpRequest(r, true)
|
||||
// fmt.Println("DumpRequest: ", string(hd))
|
||||
fmt.Println(r.RemoteAddr)
|
||||
// fmt.Println(r.RemoteAddr)
|
||||
|
||||
user_Agent := strings.ToLower(r.UserAgent())
|
||||
userAgent := strings.ToLower(r.UserAgent())
|
||||
x_Aggregate_Auth := r.Header.Get("X-Aggregate-Auth")
|
||||
x_Transcend_Version := r.Header.Get("X-Transcend-Version")
|
||||
if strings.Contains(user_Agent, "anyconnect") &&
|
||||
if strings.Contains(userAgent, "anyconnect") &&
|
||||
x_Aggregate_Auth == "1" && x_Transcend_Version == "1" {
|
||||
h(w, r, ps)
|
||||
h(w, r)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
fmt.Fprintf(w, "error request")
|
||||
@@ -73,3 +75,15 @@ func setCommonHeader(w http.ResponseWriter) {
|
||||
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
|
||||
}
|
||||
|
@@ -2,5 +2,4 @@ package handler
|
||||
|
||||
// 暂时没有实现
|
||||
func startDtls() {
|
||||
|
||||
}
|
||||
|
@@ -9,10 +9,10 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/bjdgyc/anylink/common"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/bjdgyc/anylink/sessdata"
|
||||
)
|
||||
|
||||
func LinkAuth(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
func LinkAuth(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
@@ -32,7 +32,7 @@ func LinkAuth(w http.ResponseWriter, r *http.Request, params httprouter.Params)
|
||||
if cr.Type == "logout" {
|
||||
// 退出删除session信息
|
||||
if cr.SessionToken != "" {
|
||||
DelSessByStoken(cr.SessionToken)
|
||||
sessdata.DelSessByStoken(cr.SessionToken)
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
@@ -40,29 +40,30 @@ func LinkAuth(w http.ResponseWriter, r *http.Request, params httprouter.Params)
|
||||
|
||||
if cr.Type == "init" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
data := RequestData{Group: cr.GroupSelect, Groups: common.ServerCfg.LinkGroups}
|
||||
data := RequestData{Group: cr.GroupSelect, Groups: common.ServerCfg.UserGroups}
|
||||
tplRequest(tpl_request, w, data)
|
||||
return
|
||||
}
|
||||
|
||||
// 登陆参数判断
|
||||
if cr.Type != "auth-reply" || cr.Auth.Username == "" || cr.Auth.Password == "" {
|
||||
if cr.Type != "auth-reply" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO 用户密码校验
|
||||
if !common.CheckUser(cr.Auth.Username, cr.Auth.Password, cr.GroupSelect) {
|
||||
if !CheckUser(cr.Auth.Username, cr.Auth.Password, cr.GroupSelect) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
data := RequestData{Group: cr.GroupSelect, Groups: common.ServerCfg.LinkGroups, Error: true}
|
||||
data := RequestData{Group: cr.GroupSelect, Groups: common.ServerCfg.UserGroups, Error: true}
|
||||
tplRequest(tpl_request, w, data)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建新的session信息
|
||||
sess := NewSession()
|
||||
sess := sessdata.NewSession()
|
||||
sess.UserName = cr.Auth.Username
|
||||
sess.MacAddr = strings.ToLower(cr.MacAddressList.MacAddress)
|
||||
sess.UniqueIdGlobal = cr.DeviceId.UniqueIdGlobal
|
||||
cd := RequestData{SessionId: sess.Sid, SessionToken: sess.Sid + "@" + sess.Token,
|
||||
Banner: common.ServerCfg.Banner}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
@@ -2,16 +2,17 @@ package handler
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/bjdgyc/anylink/common"
|
||||
"github.com/bjdgyc/anylink/sessdata"
|
||||
)
|
||||
|
||||
func LinkCstp(conn net.Conn, sess *ConnSession) {
|
||||
// fmt.Println("HandlerCstp")
|
||||
func LinkCstp(conn net.Conn, sess *sessdata.ConnSession) {
|
||||
log.Println("HandlerCstp")
|
||||
sessdata.Sess = sess
|
||||
defer func() {
|
||||
log.Println("LinkCstp return")
|
||||
conn.Close()
|
||||
@@ -20,6 +21,7 @@ func LinkCstp(conn net.Conn, sess *ConnSession) {
|
||||
|
||||
var (
|
||||
err error
|
||||
n int
|
||||
dataLen uint16
|
||||
dead = time.Duration(common.ServerCfg.CstpDpd+2) * time.Second
|
||||
)
|
||||
@@ -27,54 +29,53 @@ func LinkCstp(conn net.Conn, sess *ConnSession) {
|
||||
go cstpWrite(conn, sess)
|
||||
|
||||
for {
|
||||
|
||||
// 设置超时限制
|
||||
err = conn.SetDeadline(time.Now().Add(dead))
|
||||
err = conn.SetReadDeadline(time.Now().Add(dead))
|
||||
if err != nil {
|
||||
log.Println("SetDeadline: ", err)
|
||||
return
|
||||
}
|
||||
hdata := make([]byte, 1500)
|
||||
_, err = conn.Read(hdata)
|
||||
hdata := make([]byte, BufferSize)
|
||||
n, err = conn.Read(hdata)
|
||||
if err != nil {
|
||||
log.Println("read hdata: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 限流设置
|
||||
err = sess.RateLimit(n, true)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
switch hdata[6] {
|
||||
case 0x07: // KEEPALIVE
|
||||
// do nothing
|
||||
// fmt.Println("keepalive")
|
||||
// log.Println("recv keepalive")
|
||||
case 0x05: // DISCONNECT
|
||||
// fmt.Println("DISCONNECT")
|
||||
// log.Println("DISCONNECT")
|
||||
return
|
||||
case 0x03: // DPD-REQ
|
||||
fmt.Println("DPD-REQ")
|
||||
payload := &Payload{
|
||||
ptype: 0x04, // DPD-RESP
|
||||
}
|
||||
// 直接返回给客户端 resp
|
||||
select {
|
||||
case sess.PayloadOut <- payload:
|
||||
case <-sess.Closed:
|
||||
// log.Println("recv DPD-REQ")
|
||||
if payloadOut(sess, sessdata.LTypeIPData, 0x04, nil) {
|
||||
return
|
||||
}
|
||||
break
|
||||
case 0x00:
|
||||
case 0x04:
|
||||
// log.Println("recv DPD-RESP")
|
||||
case 0x00: // DATA
|
||||
dataLen = binary.BigEndian.Uint16(hdata[4:6]) // 4,5
|
||||
payload := &Payload{
|
||||
ptype: 0x00, // DPD-RESP
|
||||
data: hdata[8 : 8+dataLen],
|
||||
}
|
||||
select {
|
||||
case sess.PayloadIn <- payload:
|
||||
case <-sess.Closed:
|
||||
data := hdata[8 : 8+dataLen]
|
||||
|
||||
if payloadIn(sess, sessdata.LTypeIPData, 0x00, data) {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cstpWrite(conn net.Conn, sess *ConnSession) {
|
||||
func cstpWrite(conn net.Conn, sess *sessdata.ConnSession) {
|
||||
defer func() {
|
||||
log.Println("cstpWrite return")
|
||||
conn.Close()
|
||||
@@ -83,26 +84,37 @@ func cstpWrite(conn net.Conn, sess *ConnSession) {
|
||||
|
||||
var (
|
||||
err error
|
||||
n int
|
||||
header []byte
|
||||
payload *Payload
|
||||
payload *sessdata.Payload
|
||||
)
|
||||
|
||||
for {
|
||||
select {
|
||||
case payload = <-sess.PayloadOut:
|
||||
case <-sess.Closed:
|
||||
case <-sess.CloseChan:
|
||||
return
|
||||
}
|
||||
|
||||
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...)
|
||||
if payload.LType != sessdata.LTypeIPData {
|
||||
continue
|
||||
}
|
||||
_, err = conn.Write(header)
|
||||
|
||||
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 {
|
||||
log.Println("write err", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 限流设置
|
||||
err = sess.RateLimit(n, false)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,17 +5,16 @@ import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
func LinkHome(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
func LinkHome(w http.ResponseWriter, r *http.Request) {
|
||||
hu, _ := httputil.DumpRequest(r, true)
|
||||
fmt.Println("DumpHome: ", string(hu))
|
||||
fmt.Println(r.RemoteAddr)
|
||||
|
||||
connection := strings.ToLower(r.Header.Get("Connection"))
|
||||
if connection == "close" {
|
||||
userAgent := strings.ToLower(r.UserAgent())
|
||||
if connection == "close" && strings.Contains(userAgent, "anyconnect") {
|
||||
w.Header().Set("Connection", "close")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
|
272
handler/link_tap.go
Normal file
272
handler/link_tap.go
Normal file
@@ -0,0 +1,272 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/bjdgyc/anylink/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"
|
||||
|
||||
func checkTap() {
|
||||
brFace, err := net.InterfaceByName(bridgeName)
|
||||
if err != nil {
|
||||
log.Fatal("testTap err: ", err)
|
||||
}
|
||||
bridgeHw := brFace.HardwareAddr
|
||||
var bridgeIp net.IP
|
||||
addrs, err := brFace.Addrs()
|
||||
for _, addr := range addrs {
|
||||
ip, _, err := net.ParseCIDR(addr.String())
|
||||
if err != nil || ip.To4() == nil {
|
||||
continue
|
||||
}
|
||||
bridgeIp = ip
|
||||
}
|
||||
if bridgeIp == nil && bridgeHw == nil {
|
||||
log.Fatalln("bridgeIp is err")
|
||||
}
|
||||
|
||||
if !sessdata.IpPool.Ipv4IPNet.Contains(bridgeIp) {
|
||||
log.Fatalln("bridgeIp or Ip network err")
|
||||
}
|
||||
|
||||
// 设置本机ip arp为静态
|
||||
addr := &arpdis.Addr{IP: bridgeIp.To4(), HardwareAddr: bridgeHw, Type: arpdis.TypeStatic}
|
||||
arpdis.Add(addr)
|
||||
}
|
||||
|
||||
// 创建tap网卡
|
||||
func LinkTap(sess *sessdata.ConnSession) {
|
||||
defer func() {
|
||||
log.Println("LinkTap return")
|
||||
sess.Close()
|
||||
}()
|
||||
|
||||
cfg := water.Config{
|
||||
DeviceType: water.TAP,
|
||||
}
|
||||
|
||||
ifce, err := water.New(cfg)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
sess.TunName = ifce.Name()
|
||||
defer ifce.Close()
|
||||
|
||||
// arp on
|
||||
cmdstr1 := fmt.Sprintf("ip link set dev %s up mtu %d multicast on", ifce.Name(), sess.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 {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO 测试
|
||||
// sess.MacHw, _ = net.ParseMAC("3c:8c:40:a0:6a:3d")
|
||||
|
||||
go loopArp(sess)
|
||||
go tapRead(ifce, sess)
|
||||
|
||||
var (
|
||||
payload *sessdata.Payload
|
||||
)
|
||||
|
||||
for {
|
||||
select {
|
||||
case payload = <-sess.PayloadIn:
|
||||
case <-sess.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(sess.Ip) {
|
||||
// 过滤掉IPv6的数据
|
||||
// 非分配给客户端ip,直接丢弃
|
||||
continue
|
||||
}
|
||||
|
||||
ip_dst := waterutil.IPv4Destination(data)
|
||||
// fmt.Println("get:", ip_src, ip_dst)
|
||||
|
||||
var dstAddr *arpdis.Addr
|
||||
if !sessdata.IpPool.Ipv4IPNet.Contains(ip_dst) || ip_dst.Equal(sessdata.IpPool.Ipv4Gateway) {
|
||||
// 不是同一网段,使用网关mac地址
|
||||
ip_dst = sessdata.IpPool.Ipv4Gateway
|
||||
dstAddr = arpdis.Lookup(ip_dst, false)
|
||||
if dstAddr == nil {
|
||||
log.Println("Ipv4Gateway mac err", ip_dst)
|
||||
return
|
||||
}
|
||||
// fmt.Println("Gateway", ip_dst, dstAddr.HardwareAddr)
|
||||
} else {
|
||||
// 同一网段内的其他主机
|
||||
dstAddr = arpdis.Lookup(ip_dst, true)
|
||||
// fmt.Println("other", ip_src, ip_dst, dstAddr)
|
||||
if dstAddr == nil || dstAddr.Type == arpdis.TypeUnreachable {
|
||||
// 异步检测发送数据包
|
||||
select {
|
||||
case sess.PayloadArp <- payload:
|
||||
case <-sess.CloseChan:
|
||||
return
|
||||
default:
|
||||
// PayloadArp 容量已经满了
|
||||
log.Println("PayloadArp is full", sess.Ip, ip_dst)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
frame.Prepare(dstAddr.HardwareAddr, sess.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 {
|
||||
log.Println("tap Write err", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 异步处理获取ip对应的mac地址的数据
|
||||
func loopArp(sess *sessdata.ConnSession) {
|
||||
defer func() {
|
||||
log.Println("loopArp return")
|
||||
}()
|
||||
|
||||
var (
|
||||
payload *sessdata.Payload
|
||||
dstAddr *arpdis.Addr
|
||||
ip_dst net.IP
|
||||
)
|
||||
|
||||
for {
|
||||
select {
|
||||
case payload = <-sess.PayloadArp:
|
||||
case <-sess.CloseChan:
|
||||
return
|
||||
}
|
||||
|
||||
ip_dst = waterutil.IPv4Destination(payload.Data)
|
||||
dstAddr = arpdis.Lookup(ip_dst, false)
|
||||
// 不可达数据包
|
||||
if dstAddr == nil || dstAddr.Type == arpdis.TypeUnreachable {
|
||||
// 直接丢弃数据
|
||||
// fmt.Println("Lookup", ip_dst)
|
||||
continue
|
||||
}
|
||||
|
||||
// 正常获取mac地址
|
||||
if payloadInData(sess, payload) {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func tapRead(ifce *water.Interface, sess *sessdata.ConnSession) {
|
||||
defer func() {
|
||||
log.Println("tapRead return")
|
||||
ifce.Close()
|
||||
}()
|
||||
|
||||
var (
|
||||
err error
|
||||
n int
|
||||
buf []byte
|
||||
)
|
||||
fmt.Println(sess.MacHw)
|
||||
|
||||
for {
|
||||
var frame ethernet.Frame
|
||||
frame.Resize(BufferSize)
|
||||
n, err = ifce.Read(frame)
|
||||
if err != nil {
|
||||
log.Println("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(sess.Ip) {
|
||||
// 过滤非本机地址
|
||||
// log.Println(ip_dst, sess.Ip)
|
||||
continue
|
||||
}
|
||||
|
||||
if payloadOut(sess, sessdata.LTypeIPData, 0x00, data) {
|
||||
return
|
||||
}
|
||||
|
||||
case ethernet.ARP:
|
||||
// 暂时仅实现了ARP协议
|
||||
packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.NoCopy)
|
||||
layer := packet.Layer(layers.LayerTypeARP)
|
||||
arpReq := layer.(*layers.ARP)
|
||||
|
||||
// fmt.Println("arp", net.IP(arpReq.SourceProtAddress), sess.Ip)
|
||||
if !sess.Ip.Equal(arpReq.DstProtAddress) {
|
||||
// 过滤非本机地址
|
||||
continue
|
||||
}
|
||||
|
||||
// fmt.Println("arp", arpReq.SourceProtAddress, sess.Ip)
|
||||
// fmt.Println(packet)
|
||||
|
||||
// 返回ARP数据
|
||||
src := &arpdis.Addr{IP: sess.Ip, HardwareAddr: sess.MacHw}
|
||||
dst := &arpdis.Addr{IP: arpReq.SourceProtAddress, HardwareAddr: frame.Source()}
|
||||
buf, err = arpdis.NewARPReply(src, dst)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 从接受的arp信息添加arp地址
|
||||
addr := &arpdis.Addr{}
|
||||
copy(addr.IP, arpReq.SourceProtAddress)
|
||||
copy(addr.HardwareAddr, frame.Source())
|
||||
arpdis.Add(addr)
|
||||
|
||||
if payloadIn(sess, sessdata.LTypeEthernet, 0x00, buf) {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,13 +3,13 @@ package handler
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
|
||||
"github.com/bjdgyc/anylink/common"
|
||||
"github.com/bjdgyc/anylink/sessdata"
|
||||
"github.com/songgao/water"
|
||||
)
|
||||
|
||||
func testTun() {
|
||||
func checkTun() {
|
||||
// 测试tun
|
||||
cfg := water.Config{
|
||||
DeviceType: water.TUN,
|
||||
@@ -19,17 +19,18 @@ func testTun() {
|
||||
if err != nil {
|
||||
log.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 {
|
||||
log.Fatal("ip cmd err: ", err)
|
||||
log.Fatal("testTun err: ", err)
|
||||
}
|
||||
ifce.Close()
|
||||
}
|
||||
|
||||
// 创建tun网卡
|
||||
func LinkTun(sess *ConnSession) {
|
||||
func LinkTun(sess *sessdata.ConnSession) {
|
||||
defer func() {
|
||||
log.Println("LinkTun return")
|
||||
sess.Close()
|
||||
@@ -48,10 +49,9 @@ func LinkTun(sess *ConnSession) {
|
||||
sess.TunName = ifce.Name()
|
||||
defer ifce.Close()
|
||||
|
||||
// arp on
|
||||
cmdstr1 := fmt.Sprintf("ip link set dev %s up mtu %s multicast off", ifce.Name(), sess.Mtu)
|
||||
cmdstr1 := fmt.Sprintf("ip link set dev %s up mtu %d multicast off", ifce.Name(), sess.Mtu)
|
||||
cmdstr2 := fmt.Sprintf("ip addr add dev %s local %s peer %s/32",
|
||||
ifce.Name(), common.ServerCfg.Ipv4GateWay, sess.NetIp)
|
||||
ifce.Name(), common.ServerCfg.Ipv4Gateway, sess.Ip)
|
||||
cmdstr3 := fmt.Sprintf("sysctl -w net.ipv6.conf.%s.disable_ipv6=1", ifce.Name())
|
||||
cmdStrs := []string{cmdstr1, cmdstr2, cmdstr3}
|
||||
err = execCmd(cmdStrs)
|
||||
@@ -61,21 +61,16 @@ func LinkTun(sess *ConnSession) {
|
||||
|
||||
go tunRead(ifce, sess)
|
||||
|
||||
var payload *Payload
|
||||
var payload *sessdata.Payload
|
||||
|
||||
for {
|
||||
select {
|
||||
case payload = <-sess.PayloadIn:
|
||||
case <-sess.Closed:
|
||||
case <-sess.CloseChan:
|
||||
return
|
||||
}
|
||||
|
||||
// ip_src := waterutil.IPv4Source(payload.data)
|
||||
// ip_des := waterutil.IPv4Destination(payload.data)
|
||||
// ip_port := waterutil.IPv4DestinationPort(payload.data)
|
||||
// fmt.Println("write: ", ip_src, ip_des.String(), ip_port, len(payload.data))
|
||||
|
||||
_, err = ifce.Write(payload.data)
|
||||
_, err = ifce.Write(payload.Data)
|
||||
if err != nil {
|
||||
log.Println("tun Write err", err)
|
||||
return
|
||||
@@ -84,7 +79,7 @@ func LinkTun(sess *ConnSession) {
|
||||
|
||||
}
|
||||
|
||||
func tunRead(ifce *water.Interface, sess *ConnSession) {
|
||||
func tunRead(ifce *water.Interface, sess *sessdata.ConnSession) {
|
||||
defer func() {
|
||||
log.Println("tunRead return")
|
||||
ifce.Close()
|
||||
@@ -95,34 +90,25 @@ func tunRead(ifce *water.Interface, sess *ConnSession) {
|
||||
)
|
||||
|
||||
for {
|
||||
packet := make([]byte, 1500)
|
||||
n, err = ifce.Read(packet)
|
||||
data := make([]byte, BufferSize)
|
||||
n, err = ifce.Read(data)
|
||||
if err != nil {
|
||||
log.Println("tun Read err", n, err)
|
||||
return
|
||||
}
|
||||
|
||||
payload := &Payload{
|
||||
ptype: 0x00,
|
||||
data: packet[:n],
|
||||
}
|
||||
data = data[:n]
|
||||
|
||||
select {
|
||||
case sess.PayloadOut <- payload:
|
||||
case <-sess.Closed:
|
||||
// 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(sess, sessdata.LTypeIPData, 0x00, data) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@@ -3,10 +3,12 @@ package handler
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/bjdgyc/anylink/common"
|
||||
"github.com/bjdgyc/anylink/sessdata"
|
||||
)
|
||||
|
||||
var hn string
|
||||
@@ -20,7 +22,7 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO 调试信息输出
|
||||
// hd, _ := httputil.DumpRequest(r, true)
|
||||
// fmt.Println("DumpRequest: ", string(hd))
|
||||
fmt.Println("LinkTunnel", r.RemoteAddr)
|
||||
// fmt.Println("LinkTunnel", r.RemoteAddr)
|
||||
|
||||
// 判断session-token的值
|
||||
cookie, err := r.Cookie("webvpn")
|
||||
@@ -29,31 +31,41 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
sess := SToken2Sess(cookie.Value)
|
||||
sess := sessdata.SToken2Sess(cookie.Value)
|
||||
if sess == nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 开启link
|
||||
cSess := sess.StartConn()
|
||||
cSess := sess.NewConn()
|
||||
if cSess == nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fmt.Println(cSess.Ip, cSess.MacHw)
|
||||
|
||||
// 客户端信息
|
||||
cstp_mtu := r.Header.Get("X-CSTP-MTU")
|
||||
master_Secret := r.Header.Get("X-DTLS-Master-Secret")
|
||||
local_ip := r.Header.Get("X-Cstp-Local-Address-Ip4")
|
||||
mobile := r.Header.Get("X-Cstp-License")
|
||||
cSess.SetMtu(cstp_mtu)
|
||||
cSess.MasterSecret = master_Secret
|
||||
cSess.Mtu = cstp_mtu
|
||||
cSess.RemoteAddr = r.RemoteAddr
|
||||
cSess.LocalIp = net.ParseIP(local_ip)
|
||||
cstpDpd := common.ServerCfg.CstpDpd
|
||||
if mobile == "mobile" {
|
||||
// 手机客户端
|
||||
cstpDpd = common.ServerCfg.MobileDpd
|
||||
}
|
||||
|
||||
// 返回客户端数据
|
||||
w.Header().Set("Server", fmt.Sprintf("%s %s", common.APP_NAME, common.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.NetIp.String()) // 分配的ip地址
|
||||
w.Header().Set("X-CSTP-Address", cSess.Ip.String()) // 分配的ip地址
|
||||
w.Header().Set("X-CSTP-Netmask", common.ServerCfg.Ipv4Netmask) // 子网掩码
|
||||
w.Header().Set("X-CSTP-Hostname", hn) // 机器名称
|
||||
for _, v := range common.ServerCfg.ClientDns {
|
||||
@@ -74,7 +86,7 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
|
||||
// w.Header().Add("X-CSTP-Split-Include", "192.168.0.0/255.255.0.0")
|
||||
// w.Header().Add("X-CSTP-Split-Exclude", "10.1.5.2/255.255.255.255")
|
||||
|
||||
w.Header().Set("X-CSTP-Lease-Duration", fmt.Sprintf("%d", common.IpLease)) // ip地址租期
|
||||
w.Header().Set("X-CSTP-Lease-Duration", fmt.Sprintf("%d", sessdata.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")
|
||||
@@ -82,16 +94,18 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
|
||||
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", "5400")
|
||||
|
||||
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", common.ServerCfg.CstpDpd)) // 30 Dead peer detection in seconds
|
||||
|
||||
w.Header().Set("X-CSTP-DPD", fmt.Sprintf("%d", cstpDpd)) // 30 Dead peer detection in seconds
|
||||
w.Header().Set("X-CSTP-Keepalive", fmt.Sprintf("%d", common.ServerCfg.CstpKeepalive)) // 20
|
||||
w.Header().Set("X-CSTP-Banner", "welcome") // urlencode
|
||||
w.Header().Set("X-CSTP-Banner", common.ServerCfg.Banner) // urlencode
|
||||
w.Header().Set("X-CSTP-MSIE-Proxy-Lockdown", "true")
|
||||
w.Header().Set("X-CSTP-Smartcard-Removal-Disconnect", "true")
|
||||
|
||||
w.Header().Set("X-CSTP-MTU", cstp_mtu) // 1399
|
||||
w.Header().Set("X-DTLS-MTU", cstp_mtu)
|
||||
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")
|
||||
@@ -117,6 +131,12 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// 开始数据处理
|
||||
go LinkTun(cSess)
|
||||
switch common.ServerCfg.LinkMode {
|
||||
case common.LinkModeTUN:
|
||||
go LinkTun(cSess)
|
||||
case common.LinkModeTAP:
|
||||
go LinkTap(cSess)
|
||||
}
|
||||
|
||||
go LinkCstp(conn, cSess)
|
||||
}
|
||||
|
47
handler/payload.go
Normal file
47
handler/payload.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package handler
|
||||
|
||||
import "github.com/bjdgyc/anylink/sessdata"
|
||||
|
||||
func payloadIn(sess *sessdata.ConnSession, lType sessdata.LType, pType byte, data []byte) bool {
|
||||
payload := &sessdata.Payload{
|
||||
LType: lType,
|
||||
PType: pType,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
return payloadInData(sess, payload)
|
||||
}
|
||||
|
||||
func payloadInData(sess *sessdata.ConnSession, payload *sessdata.Payload) bool {
|
||||
closed := false
|
||||
|
||||
select {
|
||||
case sess.PayloadIn <- payload:
|
||||
case <-sess.CloseChan:
|
||||
closed = true
|
||||
}
|
||||
|
||||
return closed
|
||||
}
|
||||
|
||||
func payloadOut(sess *sessdata.ConnSession, lType sessdata.LType, pType byte, data []byte) bool {
|
||||
payload := &sessdata.Payload{
|
||||
LType: lType,
|
||||
PType: pType,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
return payloadOutData(sess, payload)
|
||||
}
|
||||
|
||||
func payloadOutData(sess *sessdata.ConnSession, payload *sessdata.Payload) bool {
|
||||
closed := false
|
||||
|
||||
select {
|
||||
case sess.PayloadOut <- payload:
|
||||
case <-sess.CloseChan:
|
||||
closed = true
|
||||
}
|
||||
|
||||
return closed
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
package handler
|
||||
|
||||
type Payload struct {
|
||||
ptype byte
|
||||
data []byte
|
||||
}
|
||||
|
||||
/*
|
||||
var header = []byte{'S', 'T', 'F', 0x01, 0, 0, 0x00, 0}
|
||||
https://tools.ietf.org/html/draft-mavrogiannopoulos-openconnect-02#section-2.2
|
||||
|
||||
+---------------------+---------------------------------------------+
|
||||
| byte | value |
|
||||
+---------------------+---------------------------------------------+
|
||||
| 0 | fixed to 0x53 (S) |
|
||||
| | |
|
||||
| 1 | fixed to 0x54 (T) |
|
||||
| | |
|
||||
| 2 | fixed to 0x46 (F) |
|
||||
| | |
|
||||
| 3 | fixed to 0x01 |
|
||||
| | |
|
||||
| 4-5 | The length of the packet that follows this |
|
||||
| | header in big endian order |
|
||||
| | |
|
||||
| 6 | The type of the payload that follows (see |
|
||||
| | Table 3 for available types) |
|
||||
| | |
|
||||
| 7 | fixed to 0x00 |
|
||||
+---------------------+---------------------------------------------+
|
||||
|
||||
|
||||
The available payload types are listed in Table 3.
|
||||
+---------------------+---------------------------------------------+
|
||||
| Value | Description |
|
||||
+---------------------+---------------------------------------------+
|
||||
| 0x00 | DATA: the TLS record packet contains an |
|
||||
| | IPv4 or IPv6 packet |
|
||||
| | |
|
||||
| 0x03 | DPD-REQ: used for dead peer detection. Once |
|
||||
| | sent the peer should reply with a DPD-RESP |
|
||||
| | packet, that has the same contents as the |
|
||||
| | original request. |
|
||||
| | |
|
||||
| 0x04 | DPD-RESP: used as a response to a |
|
||||
| | previously received DPD-REQ. |
|
||||
| | |
|
||||
| 0x05 | DISCONNECT: sent by the client (or server) |
|
||||
| | to terminate the session. No data is |
|
||||
| | associated with this request. The session |
|
||||
| | will be invalidated after such request. |
|
||||
| | |
|
||||
| 0x07 | KEEPALIVE: sent by any peer. No data is |
|
||||
| | associated with this request. |
|
||||
| | |
|
||||
| 0x08 | COMPRESSED DATA: a Data packet which is |
|
||||
| | compressed prior to encryption. |
|
||||
| | |
|
||||
| 0x09 | TERMINATE: sent by the server to indicate |
|
||||
| | that the server is shutting down. No data |
|
||||
| | is associated with this request. |
|
||||
+---------------------+---------------------------------------------+
|
||||
*/
|
@@ -3,27 +3,32 @@ package handler
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/bjdgyc/anylink/proxyproto"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
_ "net/http/pprof"
|
||||
"net/http/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/bjdgyc/anylink/common"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/bjdgyc/anylink/proxyproto"
|
||||
"github.com/bjdgyc/anylink/router"
|
||||
)
|
||||
|
||||
func Start() {
|
||||
testTun()
|
||||
go startDebug()
|
||||
go startDtls()
|
||||
go startTls()
|
||||
}
|
||||
func startAdmin() {
|
||||
mux := router.NewHttpMux()
|
||||
mux.HandleFunc(router.ANY, "/", notFound)
|
||||
// mux.ServeFile(router.ANY, "/static/*", http.Dir("./static"))
|
||||
|
||||
func startDebug() {
|
||||
http.ListenAndServe(common.ServerCfg.DebugAddr, nil)
|
||||
// pprof
|
||||
mux.HandleFunc(router.ANY, "/debug/pprof/*", pprof.Index)
|
||||
mux.HandleFunc(router.ANY, "/debug/pprof/cmdline", pprof.Cmdline)
|
||||
mux.HandleFunc(router.ANY, "/debug/pprof/profile", pprof.Profile)
|
||||
mux.HandleFunc(router.ANY, "/debug/pprof/symbol", pprof.Symbol)
|
||||
mux.HandleFunc(router.ANY, "/debug/pprof/trace", pprof.Trace)
|
||||
|
||||
fmt.Println("Listen admin", common.ServerCfg.AdminAddr)
|
||||
err := http.ListenAndServe(common.ServerCfg.AdminAddr, mux)
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
func startTls() {
|
||||
@@ -62,17 +67,18 @@ func startTls() {
|
||||
}
|
||||
|
||||
func initRoute() http.Handler {
|
||||
router := httprouter.New()
|
||||
router.GET("/", checkVpnClient(LinkHome))
|
||||
router.POST("/", checkVpnClient(LinkAuth))
|
||||
router.HandlerFunc("CONNECT", "/CSCOSSLC/tunnel", LinkTunnel)
|
||||
router.NotFound = http.HandlerFunc(notFound)
|
||||
return router
|
||||
mux := router.NewHttpMux()
|
||||
mux.HandleFunc("GET", "/", checkLinkClient(LinkHome))
|
||||
mux.HandleFunc("POST", "/", checkLinkClient(LinkAuth))
|
||||
mux.HandleFunc("CONNECT", "/CSCOSSLC/tunnel", LinkTunnel)
|
||||
mux.SetNotFound(http.HandlerFunc(notFound))
|
||||
return mux
|
||||
}
|
||||
|
||||
func notFound(w http.ResponseWriter, r *http.Request) {
|
||||
hu, _ := httputil.DumpRequest(r, true)
|
||||
fmt.Println("NotFound: ", string(hu))
|
||||
// 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")
|
||||
|
@@ -1,157 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bjdgyc/anylink/common"
|
||||
)
|
||||
|
||||
var (
|
||||
sessMux = sync.Mutex{}
|
||||
sessions = make(map[string]*Session) // session_token -> SessUser
|
||||
)
|
||||
|
||||
// 连接sess
|
||||
type ConnSession struct {
|
||||
Sess *Session
|
||||
MasterSecret string // dtls协议的 master_secret
|
||||
NetIp net.IP // 分配的ip地址
|
||||
RemoteAddr string
|
||||
Mtu string
|
||||
TunName string
|
||||
closeOnce sync.Once
|
||||
Closed chan struct{}
|
||||
PayloadIn chan *Payload
|
||||
PayloadOut chan *Payload
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
Sid string // auth返回的 session-id
|
||||
Token string // session信息的唯一token
|
||||
DtlsSid string // dtls协议的 session_id
|
||||
MacAddr string // 客户端mac地址
|
||||
UserName string // 用户名
|
||||
LastLogin time.Time
|
||||
IsActive bool
|
||||
|
||||
// 开启link需要设置的参数
|
||||
CSess *ConnSession
|
||||
}
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// 检测过期的session
|
||||
go func() {
|
||||
if common.ServerCfg.SessionTimeout == 0 {
|
||||
return
|
||||
}
|
||||
timeout := time.Duration(common.ServerCfg.SessionTimeout) * time.Second
|
||||
tick := time.Tick(time.Second * 30)
|
||||
for range tick {
|
||||
t := time.Now()
|
||||
sessMux.Lock()
|
||||
for k, v := range sessions {
|
||||
if v.IsActive == true {
|
||||
continue
|
||||
}
|
||||
if t.Sub(v.LastLogin) > timeout {
|
||||
delete(sessions, k)
|
||||
}
|
||||
}
|
||||
sessMux.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func NewSession() *Session {
|
||||
// 生成32位的 token
|
||||
btoken := make([]byte, 32)
|
||||
rand.Read(btoken)
|
||||
|
||||
// 生成 dtls session_id
|
||||
dtlsid := make([]byte, 32)
|
||||
rand.Read(dtlsid)
|
||||
|
||||
token := fmt.Sprintf("%x", btoken)
|
||||
sess := &Session{
|
||||
Sid: fmt.Sprintf("%d", time.Now().Unix()),
|
||||
Token: token,
|
||||
DtlsSid: fmt.Sprintf("%x", dtlsid),
|
||||
LastLogin: time.Now(),
|
||||
}
|
||||
sessMux.Lock()
|
||||
defer sessMux.Unlock()
|
||||
sessions[token] = sess
|
||||
return sess
|
||||
}
|
||||
|
||||
func (s *Session) StartConn() *ConnSession {
|
||||
if s.IsActive == true {
|
||||
s.CSess.Close()
|
||||
}
|
||||
|
||||
limit := common.LimitClient(s.UserName, false)
|
||||
if limit == false {
|
||||
// s.NetIp = nil
|
||||
return nil
|
||||
}
|
||||
s.IsActive = true
|
||||
cSess := &ConnSession{
|
||||
Sess: s,
|
||||
NetIp: common.AcquireIp(s.MacAddr),
|
||||
closeOnce: sync.Once{},
|
||||
Closed: make(chan struct{}),
|
||||
PayloadIn: make(chan *Payload),
|
||||
PayloadOut: make(chan *Payload),
|
||||
}
|
||||
s.CSess = cSess
|
||||
return cSess
|
||||
}
|
||||
|
||||
func (cs *ConnSession) Close() {
|
||||
cs.closeOnce.Do(func() {
|
||||
log.Println("closeOnce")
|
||||
close(cs.Closed)
|
||||
cs.Sess.IsActive = false
|
||||
cs.Sess.LastLogin = time.Now()
|
||||
common.ReleaseIp(cs.NetIp, cs.Sess.MacAddr)
|
||||
common.LimitClient(cs.Sess.UserName, true)
|
||||
})
|
||||
}
|
||||
|
||||
func SToken2Sess(stoken string) *Session {
|
||||
stoken = strings.TrimSpace(stoken)
|
||||
sarr := strings.Split(stoken, "@")
|
||||
token := sarr[1]
|
||||
sessMux.Lock()
|
||||
defer sessMux.Unlock()
|
||||
if sess, ok := sessions[token]; ok {
|
||||
return sess
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Dtls2Sess(dtlsid []byte) *Session {
|
||||
return nil
|
||||
}
|
||||
|
||||
func DelSess(token string) {
|
||||
delete(sessions, token)
|
||||
}
|
||||
|
||||
func DelSessByStoken(stoken string) {
|
||||
stoken = strings.TrimSpace(stoken)
|
||||
sarr := strings.Split(stoken, "@")
|
||||
token := sarr[1]
|
||||
sessMux.Lock()
|
||||
defer sessMux.Unlock()
|
||||
delete(sessions, token)
|
||||
}
|
24
handler/start.go
Normal file
24
handler/start.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/bjdgyc/anylink/common"
|
||||
"github.com/bjdgyc/anylink/dbdata"
|
||||
"github.com/bjdgyc/anylink/sessdata"
|
||||
)
|
||||
|
||||
func Start() {
|
||||
dbdata.Start()
|
||||
sessdata.Start()
|
||||
|
||||
checkTun()
|
||||
if common.ServerCfg.LinkMode == common.LinkModeTAP {
|
||||
checkTap()
|
||||
}
|
||||
go startAdmin()
|
||||
go startTls()
|
||||
go startDtls()
|
||||
}
|
||||
|
||||
func Stop() {
|
||||
dbdata.Stop()
|
||||
}
|
71
handler/user.go
Normal file
71
handler/user.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/bjdgyc/anylink/common"
|
||||
"github.com/bjdgyc/anylink/dbdata"
|
||||
"github.com/xlzd/gotp"
|
||||
)
|
||||
|
||||
func CheckUser(name, pwd, group string) bool {
|
||||
return true
|
||||
|
||||
pl := len(pwd)
|
||||
if name == "" || pl < 6 {
|
||||
return false
|
||||
}
|
||||
v := &dbdata.User{}
|
||||
err := dbdata.Get(dbdata.BucketUser, name, v)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if !common.InArrStr(v.Group, group) {
|
||||
return false
|
||||
}
|
||||
pass := pwd[:pl-6]
|
||||
pwdHash := hashPass(pass)
|
||||
if v.Password != pwdHash {
|
||||
return false
|
||||
}
|
||||
otp := pwd[pl-6:]
|
||||
totp := gotp.NewDefaultTOTP(v.OtpSecret)
|
||||
unix := time.Now().Unix()
|
||||
verify := totp.Verify(otp, int(unix))
|
||||
if !verify {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func UserAdd(name, pwd string, group []string) dbdata.User {
|
||||
v := dbdata.User{
|
||||
Id: dbdata.NextId(dbdata.BucketUser),
|
||||
Username: name,
|
||||
Password: hashPass(pwd),
|
||||
OtpSecret: gotp.RandomSecret(32),
|
||||
Group: group,
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
fmt.Println(v)
|
||||
secret := "WHH7WA6POOGGEYVIQYXLZU75QLM7YLUX"
|
||||
totp := gotp.NewDefaultTOTP(secret)
|
||||
s := totp.ProvisioningUri("bjdtest", "bjdpro")
|
||||
fmt.Println(s)
|
||||
|
||||
// qr, _ := qrcode.New(s, qrcode.Medium)
|
||||
// a := qr.ToSmallString(false)
|
||||
// fmt.Println(a)
|
||||
// qr.WriteFile(512, "a.png")
|
||||
|
||||
os.Exit(0)
|
||||
return v
|
||||
}
|
||||
|
||||
func hashPass(pwd string) string {
|
||||
sum := sha1.Sum([]byte(pwd))
|
||||
return fmt.Sprintf("%x", sum)
|
||||
}
|
Reference in New Issue
Block a user