2020-04-02 14:07:56 +00:00
package adlin
import (
"crypto/rand"
"crypto/sha512"
"encoding/base64"
"fmt"
"net"
"os/exec"
"strings"
2020-04-02 14:36:29 +00:00
"sync"
2020-04-02 14:07:56 +00:00
"time"
)
2021-03-03 17:44:26 +00:00
const StdNetmask = 80
2020-04-02 14:07:56 +00:00
func StudentIP ( idstd int64 ) net . IP {
return net . ParseIP ( fmt . Sprintf ( "2a01:e0a:2b:2252:%x::" , idstd ) )
}
2021-03-03 17:44:26 +00:00
func StudentNet ( idstd int64 ) * net . IPNet {
return & net . IPNet {
IP : StudentIP ( idstd ) ,
Mask : net . CIDRMask ( StdNetmask , 128 ) ,
}
}
2020-04-02 14:07:56 +00:00
type WGDump struct {
PubKey string
PSK string
Endpoint string
AllowedIPs string
LastHandS string
RX string
TX string
KeepAlive string
}
2020-04-02 14:36:29 +00:00
var (
2021-03-07 11:39:38 +00:00
wgDumpCache_data map [ string ] * WGDump = nil
2020-04-02 14:36:29 +00:00
wgDumpCache_time time . Time
wgDumpCache_mutex sync . RWMutex
)
2021-03-07 11:39:38 +00:00
func _readWgDump ( ) ( wgd map [ string ] * WGDump , err error ) {
2020-04-02 14:07:56 +00:00
out , errr := exec . Command ( "wg" , "show" , "wg-adlin" , "dump" ) . Output ( )
if errr != nil {
return nil , errr
}
2021-03-07 11:39:38 +00:00
wgd = map [ string ] * WGDump { }
2020-04-02 14:07:56 +00:00
for _ , line := range strings . Split ( string ( out ) , "\n" ) {
cols := strings . Fields ( line )
if len ( cols ) != 8 {
continue
}
2021-03-07 11:39:38 +00:00
wgd [ cols [ 0 ] ] = & WGDump { cols [ 0 ] , cols [ 1 ] , cols [ 2 ] , cols [ 3 ] , cols [ 4 ] , cols [ 5 ] , cols [ 6 ] , cols [ 7 ] }
2020-04-02 14:07:56 +00:00
}
return
}
2021-03-07 11:39:38 +00:00
func readWgDump ( ) ( wgd map [ string ] * WGDump , err error ) {
2020-04-02 14:36:29 +00:00
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 {
2020-04-25 21:51:47 +00:00
wgDumpCache_mutex . Unlock ( )
wgDumpCache_mutex . RLock ( )
2020-04-02 14:36:29 +00:00
return
}
wgDumpCache_data = wgd
wgDumpCache_time = time . Now ( )
}
wgDumpCache_mutex . Unlock ( )
wgDumpCache_mutex . RLock ( )
}
return wgd , nil
}
2020-04-02 14:07:56 +00:00
type TunnelToken struct {
token [ ] byte
TokenText string
IdStudent int64
PubKey [ ] byte
Time time . Time
SuffixIP int
// Version stores the TP number where the token is used
Version int
Dump * WGDump
}
2021-03-04 00:05:24 +00:00
func ( tt * TunnelToken ) GetStudentIP ( ) string {
2021-03-04 00:32:09 +00:00
if tt . SuffixIP == 0 {
return fmt . Sprintf ( "%s%x" , StudentIP ( tt . IdStudent ) . String ( ) , 1 )
} else {
return fmt . Sprintf ( "%s%x" , StudentIP ( tt . IdStudent ) . String ( ) , tt . SuffixIP )
}
2021-03-04 00:05:24 +00:00
}
2021-03-02 18:08:42 +00:00
func TokenFromText ( token string ) [ ] byte {
2020-04-02 14:07:56 +00:00
sha := sha512 . Sum512 ( [ ] byte ( token ) )
return sha [ : ]
}
2021-03-07 11:39:38 +00:00
func GetTunnelToken ( token [ ] byte ) ( t * TunnelToken , err error ) {
t = new ( TunnelToken )
2020-04-02 14:07:56 +00:00
err = DBQueryRow ( "SELECT token, token_text, id_student, pubkey, time, suffixip, 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 . Version )
if err == nil && t . PubKey != nil {
if wgd , errr := readWgDump ( ) ; errr == nil {
if v , ok := wgd [ base64 . StdEncoding . EncodeToString ( t . PubKey ) ] ; ok {
2021-03-07 11:39:38 +00:00
t . Dump = v
2020-04-02 14:07:56 +00:00
}
}
}
return
}
2021-03-07 11:39:38 +00:00
func ( student * Student ) NewTunnelToken ( suffixip int ) ( t * TunnelToken , err error ) {
2020-04-02 14:07:56 +00:00
tok := make ( [ ] byte , 7 )
if _ , err = rand . Read ( tok ) ; err != nil {
return
}
2021-03-07 11:39:38 +00:00
t = new ( TunnelToken )
2021-03-04 00:05:46 +00:00
t . TokenText = strings . Replace ( strings . Replace ( strings . Replace ( strings . Replace ( strings . Replace ( base64 . RawStdEncoding . EncodeToString ( tok ) , "/" , "." , - 1 ) , "+" , "_" , - 1 ) , "O" , "<" , - 1 ) , "l" , "$" , - 1 ) , "I" , ">" , - 1 )
2021-03-02 18:08:42 +00:00
t . token = TokenFromText ( t . TokenText )
2020-04-02 14:07:56 +00:00
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
}
2021-03-07 11:39:38 +00:00
func ( student * Student ) GetTunnelTokens ( ) ( ts [ ] * TunnelToken , err error ) {
2020-04-02 14:07:56 +00:00
if rows , errr := DBQuery ( "SELECT token, token_text, id_student, pubkey, time, suffixip, 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 ( ) {
2021-03-07 11:39:38 +00:00
t := & TunnelToken { }
2020-04-02 14:07:56 +00:00
if err = rows . Scan ( & t . token , & t . TokenText , & t . IdStudent , & t . PubKey , & t . Time , & t . SuffixIP , & t . Version ) ; err != nil {
return
}
if t . PubKey != nil {
if v , ok := wgd [ base64 . StdEncoding . EncodeToString ( t . PubKey ) ] ; ok {
2021-03-07 11:39:38 +00:00
t . Dump = v
2020-04-02 14:07:56 +00:00
}
}
ts = append ( ts , t )
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
2021-03-07 11:39:38 +00:00
func ( student * Student ) GetActivesTunnels ( ) ( ts [ ] * TunnelToken , err error ) {
2021-02-04 08:39:23 +00:00
if rows , errr := DBQuery ( "SELECT token, token_text, id_student, pubkey, time, suffixip, 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 ( ) {
2021-03-07 11:39:38 +00:00
t := & TunnelToken { }
2021-02-04 08:39:23 +00:00
if err = rows . Scan ( & t . token , & t . TokenText , & t . IdStudent , & t . PubKey , & t . Time , & t . SuffixIP , & t . Version ) ; err != nil {
return
}
if t . PubKey != nil {
if v , ok := wgd [ base64 . StdEncoding . EncodeToString ( t . PubKey ) ] ; ok {
2021-03-07 11:39:38 +00:00
t . Dump = v
2021-02-04 08:39:23 +00:00
ts = append ( ts , t )
}
}
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
2021-03-07 11:39:38 +00:00
func ( student * Student ) GetTunnelToken ( token [ ] byte ) ( t * TunnelToken , err error ) {
t = new ( TunnelToken )
2020-04-02 14:07:56 +00:00
err = DBQueryRow ( "SELECT token, token_text, id_student, pubkey, time, suffixip, 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 . Version )
if err == nil && t . PubKey != nil {
if wgd , errr := readWgDump ( ) ; errr == nil {
if v , ok := wgd [ base64 . StdEncoding . EncodeToString ( t . PubKey ) ] ; ok {
2021-03-07 11:39:38 +00:00
t . Dump = v
2020-04-02 14:07:56 +00:00
}
}
}
return
}
func ( t * TunnelToken ) Update ( ) ( int64 , error ) {
2021-03-02 18:08:42 +00:00
newtoken := TokenFromText ( t . TokenText )
2020-04-02 14:07:56 +00:00
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
}
}
2021-03-02 18:08:42 +00:00
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
}
}
2021-03-07 11:39:38 +00:00
func GetStudentsTunnels ( ) ( ts [ ] * TunnelToken , err error ) {
2020-04-02 14:07:56 +00:00
if rows , errr := DBQuery ( "SELECT T.token, T.token_text, T.id_student, T.pubkey, T.time, T.suffixip, T.version FROM student_tunnel_tokens T INNER JOIN (SELECT B.id_student, MAX(B.time) AS time FROM student_tunnel_tokens B WHERE B.pubkey IS NOT NULL GROUP BY id_student) 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 ( ) {
2021-03-07 11:39:38 +00:00
t := & TunnelToken { }
2020-04-02 14:07:56 +00:00
if err = rows . Scan ( & t . token , & t . TokenText , & t . IdStudent , & t . PubKey , & t . Time , & t . SuffixIP , & t . Version ) ; err != nil {
return
}
ts = append ( ts , t )
}
err = rows . Err ( )
return
}
}