diff --git a/core/EasyConnectClient.go b/core/EasyConnectClient.go
index a20aced..275a556 100644
--- a/core/EasyConnectClient.go
+++ b/core/EasyConnectClient.go
@@ -56,6 +56,19 @@ func (client *EasyConnectClient) AuthSMSCode(code string) ([]byte, error) {
return client.LoginByTwfId(twfId)
}
+func (client *EasyConnectClient) AuthTOTP(code string) ([]byte, error) {
+ if client.twfId == "" {
+ return nil, errors.New("TOTP Auth not required")
+ }
+
+ twfId, err := TOTPAuth(client.server, client.username, client.password, client.twfId, code)
+ if err != nil {
+ return nil, err
+ }
+
+ return client.LoginByTwfId(twfId)
+}
+
func (client *EasyConnectClient) LoginByTwfId(twfId string) ([]byte, error) {
agentToken, err := ECAgentToken(client.server, twfId)
if err != nil {
diff --git a/core/web_login.go b/core/web_login.go
index 98f8afa..999c8d1 100644
--- a/core/web_login.go
+++ b/core/web_login.go
@@ -20,7 +20,8 @@ import (
utls "github.com/refraction-networking/utls"
)
-var ERR_NEXT_AUTH_SMS = errors.New("SMS Code required.")
+var ERR_NEXT_AUTH_SMS = errors.New("SMS Code required")
+var ERR_NEXT_AUTH_TOTP = errors.New("Current user's TOTP bound")
func WebLogin(server string, username string, password string) (string, error) {
server = "https://" + server
@@ -138,6 +139,12 @@ func WebLogin(server string, username string, password string) (string, error) {
return twfId, ERR_NEXT_AUTH_SMS
}
+ // TOTP Authnication Process (Edited by JHong)
+ if strings.Contains(string(buf[:n]), "auth/token") || strings.Contains(string(buf[:n]), "totp") {
+ log.Print("TOTP Authnication required.")
+ return twfId, ERR_NEXT_AUTH_TOTP
+ }
+
if strings.Contains(string(buf[:n]), "-1") || !strings.Contains(string(buf[:n]), "") {
log.Print("No NextAuth found.")
} else {
@@ -198,6 +205,44 @@ func AuthSms(server string, username string, password string, twfId string, smsC
return twfId, nil
}
+// JHong Implementing.......
+func TOTPAuth(server string, username string, password string, twfId string, TOTPCode string) (string, error) {
+ c := &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+ }}
+
+ buf := make([]byte, 40960)
+
+ addr := "https://" + server + "/por/login_token.csp"
+ log.Printf("TOTP token Request: " + addr)
+ form := url.Values{
+ "svpn_inputtoken": {TOTPCode},
+ }
+
+ req, err := http.NewRequest("POST", addr, strings.NewReader(form.Encode()))
+ req.Header.Set("Cookie", "TWFID="+twfId)
+
+ resp, err := c.Do(req)
+ if err != nil {
+ debug.PrintStack()
+ return "", err
+ }
+
+ n, _ := resp.Body.Read(buf)
+ defer resp.Body.Close()
+
+ if !strings.Contains(string(buf[:n]), "suc") {
+ debug.PrintStack()
+ return "", errors.New("TOTP token verification FAILED: " + string(buf[:n]))
+ }
+
+ twfId = string(regexp.MustCompile(`(.*)`).FindSubmatch(buf[:n])[1])
+ log.Print("TOTP verification SUCCESS")
+
+ return twfId, nil
+}
+
func ECAgentToken(server string, twfId string) (string, error) {
dialConn, err := net.Dial("tcp", server)
defer dialConn.Close()
diff --git a/main.go b/main.go
index e9a76d3..fc5948d 100644
--- a/main.go
+++ b/main.go
@@ -11,7 +11,7 @@ import (
func main() {
// CLI args
host, port, username, password, socksBind, twfId := "", 0, "", "", "", ""
- flag.StringVar(&host, "server", "", "EasyConnect server address (e.g. vpn.nju.edu.cn)")
+ flag.StringVar(&host, "server", "", "EasyConnect server address (e.g. vpn.nju.edu.cn, sslvpn.sysu.edu.cn)")
flag.StringVar(&username, "username", "", "Your username")
flag.StringVar(&password, "password", "", "Your password")
flag.StringVar(&socksBind, "socks-bind", ":1080", "The addr socks5 server listens on (e.g. 0.0.0.0:1080)")
@@ -43,6 +43,12 @@ func main() {
fmt.Scan(&smsCode)
ip, err = client.AuthSMSCode(smsCode)
+ } else if err == core.ERR_NEXT_AUTH_TOTP {
+ fmt.Print(">>>Please enter your TOTP Auth code<<<:")
+ TOTPCode := ""
+ fmt.Scan(&TOTPCode)
+
+ ip, err = client.AuthTOTP(TOTPCode)
}
}