This repository has been archived on 2024-03-03. You can view files and clone it, but cannot push or open issues or pull requests.
adlin/libadlin/tunnel.go

317 lines
8.5 KiB
Go

package adlin
import (
"context"
"crypto/ed25519"
"crypto/rand"
"crypto/sha512"
"encoding/base64"
"fmt"
"net"
"os/exec"
"strings"
"sync"
"time"
)
const StdNetmask = 80
var (
collector_secret ed25519.PrivateKey
)
func SetCollectorSecret(b []byte) {
collector_secret = ed25519.NewKeyFromSeed(b)
}
func GetCollectorPublic() ed25519.PublicKey {
return collector_secret.Public().(ed25519.PublicKey)
}
func StudentIP(idstd int64, overrideid int) net.IP {
return net.ParseIP(fmt.Sprintf("2a01:e0a:518:833:%x%x::", overrideid, idstd))
}
func StudentNet(idstd int64, overrideid int) *net.IPNet {
return &net.IPNet{
IP: StudentIP(idstd, overrideid),
Mask: net.CIDRMask(StdNetmask, 128),
}
}
type WGDump struct {
PubKey string
PSK string
Endpoint string
AllowedIPs string
LastHandS string
RX string
TX string
KeepAlive string
}
func (d *WGDump) GetPubKey() ([]byte, error) {
return base64.StdEncoding.DecodeString(d.PubKey)
}
var (
wgDumpCache_data map[string]*WGDump = nil
wgDumpCache_time time.Time
wgDumpCache_mutex sync.RWMutex
)
func _readWgDump() (wgd map[string]*WGDump, err error) {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
out, err := exec.CommandContext(ctx, "wg", "show", "wg-adlin", "dump").Output()
if err != nil {
return nil, err
}
wgd = map[string]*WGDump{}
for _, line := range strings.Split(string(out), "\n") {
cols := strings.Fields(line)
if len(cols) != 8 {
continue
}
wgd[cols[0]] = &WGDump{cols[0], cols[1], cols[2], cols[3], cols[4], cols[5], cols[6], cols[7]}
}
return
}
func readWgDump() (wgd map[string]*WGDump, err error) {
wgDumpCache_mutex.RLock()
defer wgDumpCache_mutex.RUnlock()
wgd = wgDumpCache_data
if time.Since(wgDumpCache_time) > time.Second*10 {
wgDumpCache_mutex.RUnlock()
wgDumpCache_mutex.Lock()
if time.Since(wgDumpCache_time) > time.Second*10 {
wgd, err = _readWgDump()
if err != nil {
wgDumpCache_mutex.Unlock()
wgDumpCache_mutex.RLock()
return
}
wgDumpCache_data = wgd
wgDumpCache_time = time.Now()
}
wgDumpCache_mutex.Unlock()
wgDumpCache_mutex.RLock()
}
return
}
type TunnelToken struct {
token []byte
TokenText string
IdStudent int64
PubKey []byte
Time time.Time
SuffixIP int
OverrideID int
// Version stores the TP number where the token is used
Version int
Dump *WGDump
}
func (tt *TunnelToken) GetStudentIP() string {
if tt.SuffixIP == 0 {
return fmt.Sprintf("%s%x", StudentIP(tt.IdStudent, tt.OverrideID).String(), 1)
} else {
return fmt.Sprintf("%s%x", StudentIP(tt.IdStudent, tt.OverrideID).String(), tt.SuffixIP)
}
}
func (tt *TunnelToken) GetServerIP(suffix int) string {
return fmt.Sprintf("%s%x", StudentIP(tt.IdStudent, tt.OverrideID).String(), suffix)
}
func (tt *TunnelToken) GenKeySign() []byte {
stdprivkey := ed25519.NewKeyFromSeed(tt.token[:ed25519.SeedSize])
stdpublic := []byte(stdprivkey.Public().(ed25519.PublicKey))
return ed25519.Sign(collector_secret, stdpublic)
}
func TokenFromText(token string) []byte {
sha := sha512.Sum512([]byte(token))
return sha[:]
}
func GetTunnelToken(token []byte) (t *TunnelToken, err error) {
t = new(TunnelToken)
err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time, suffixip, idoverride, version FROM student_tunnel_tokens WHERE token=? ORDER BY time DESC", token).Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.OverrideID, &t.Version)
if err == nil && t.PubKey != nil {
if wgd, errr := readWgDump(); errr == nil {
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
t.Dump = v
}
}
}
return
}
func (student *Student) NewTunnelToken(suffixip int) (t *TunnelToken, err error) {
tok := make([]byte, 7)
if _, err = rand.Read(tok); err != nil {
return
}
t = new(TunnelToken)
t.TokenText = strings.Replace(strings.Replace(strings.Replace(strings.Replace(strings.Replace(base64.RawStdEncoding.EncodeToString(tok), "/", ".", -1), "+", "_", -1), "O", "<", -1), "l", "$", -1), "I", ">", -1)
t.token = TokenFromText(t.TokenText)
t.IdStudent = student.Id
_, err = DBExec("INSERT INTO student_tunnel_tokens (token, token_text, id_student, time, suffixip, version) VALUES (?, ?, ?, ?, ?, 0)", t.token, t.TokenText, student.Id, time.Now(), suffixip)
return
}
func (student *Student) GetTunnelTokens() (ts []*TunnelToken, err error) {
if rows, errr := DBQuery("SELECT token, token_text, id_student, pubkey, time, suffixip, idoverride, version FROM student_tunnel_tokens WHERE id_student = ? ORDER BY time DESC", student.Id); errr != nil {
return nil, errr
} else if wgd, errr := readWgDump(); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
t := &TunnelToken{}
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.OverrideID, &t.Version); err != nil {
return
}
if t.PubKey != nil {
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
t.Dump = v
}
}
ts = append(ts, t)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func (student *Student) GetActivesTunnels() (ts []*TunnelToken, err error) {
if rows, errr := DBQuery("SELECT token, token_text, id_student, pubkey, time, suffixip, idoverride, version FROM student_tunnel_tokens WHERE id_student = ? ORDER BY time DESC", student.Id); errr != nil {
return nil, errr
} else if wgd, errr := readWgDump(); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
t := &TunnelToken{}
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.OverrideID, &t.Version); err != nil {
return
}
if t.PubKey != nil {
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
t.Dump = v
ts = append(ts, t)
}
}
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func (student *Student) GetDefaultTunnels() (ts []*TunnelToken, err error) {
t := &TunnelToken{IdStudent: student.Id}
ts = append(ts, t)
return
}
func (student *Student) GetActivesTunnelsPubKey() (ts []ed25519.PublicKey, err error) {
var activeTuns []*TunnelToken
activeTuns, err = student.GetActivesTunnels()
if err != nil {
return
}
for _, tun := range activeTuns {
if tun.Dump != nil {
pk := ed25519.NewKeyFromSeed(tun.token[:ed25519.SeedSize])
ts = append(ts, pk.Public().(ed25519.PublicKey))
}
}
return
}
func (student *Student) GetTunnelToken(token []byte) (t *TunnelToken, err error) {
t = new(TunnelToken)
err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time, suffixip, idoverride, version FROM student_tunnel_tokens WHERE token = ? AND id_student = ? ORDER BY time DESC", token, student.Id).Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.OverrideID, &t.Version)
if err == nil && t.PubKey != nil {
if wgd, errr := readWgDump(); errr == nil {
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
t.Dump = v
}
}
}
return
}
func (t *TunnelToken) Update() (int64, error) {
newtoken := TokenFromText(t.TokenText)
tm := time.Now()
if res, err := DBExec("UPDATE student_tunnel_tokens SET token = ?, token_text = ?, id_student = ?, pubkey = ?, time = ?, suffixip = ?, version = ? WHERE token = ?", newtoken, t.TokenText, t.IdStudent, t.PubKey, tm, t.SuffixIP, t.Version, t.token); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
t.token = newtoken
t.Time = tm
return nb, err
}
}
func (t *TunnelToken) Delete() (int64, error) {
if res, err := DBExec("DELETE FROM student_tunnel_tokens WHERE token = ? AND id_student = ?", t.token, t.IdStudent); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}
func GetStudentsTunnels() (ts []*TunnelToken, err error) {
if rows, errr := DBQuery("SELECT T.token, T.token_text, T.id_student, T.pubkey, T.time, T.suffixip, T.idoverride, T.version FROM student_tunnel_tokens T INNER JOIN (SELECT B.id_student, MAX(B.time) AS time, B.idoverride FROM student_tunnel_tokens B WHERE B.pubkey IS NOT NULL GROUP BY id_student, idoverride) L ON T.id_student = L.id_student AND T.time = L.time"); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
t := &TunnelToken{}
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.OverrideID, &t.Version); err != nil {
return
}
ts = append(ts, t)
}
err = rows.Err()
return
}
}