1
0
mirror of https://github.com/fumiama/go-nd-portal.git synced 2026-06-29 23:51:06 +08:00
This commit is contained in:
源文雨
2022-11-26 15:47:31 +08:00
parent 51de023bdb
commit 91808d70b9
13 changed files with 293 additions and 72 deletions

105
portal/portal.go Normal file
View File

@@ -0,0 +1,105 @@
package portal
import (
"crypto/hmac"
"crypto/md5"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"net"
"net/url"
"time"
"github.com/FloatTech/floatbox/web"
"github.com/sirupsen/logrus"
"github.com/fumiama/go-nd-portal/helper"
)
var (
ErrIllegalIPv4 = errors.New("illegal ipv4")
ErrUnexpectedChallengeResponse = errors.New("unexpected challenge response")
ErrUnexpectedLoginResponse = errors.New("unexpected login response")
)
type Portal struct {
nam string
pwd string
ip net.IP
}
type rsp struct {
Challenge string `json:"challenge"`
Error string `json:"error"`
}
func NewPortal(name, password string, ipv4 net.IP) (*Portal, error) {
if len(ipv4) != 4 {
return nil, ErrIllegalIPv4
}
return &Portal{
nam: name,
pwd: password,
ip: ipv4,
}, nil
}
func (p *Portal) GetChallenge() (string, error) {
u := fmt.Sprintf(PortalGetChallenge, "gondportal", url.QueryEscape(p.nam), p.ip, time.Now().UnixMilli())
logrus.Debugln("GET", u)
data, err := web.RequestDataWith(web.NewDefaultClient(), u, "GET", "", PortalHeaderUA)
if err != nil {
return "", err
}
logrus.Debugln("get challenge resp:", helper.BytesToString(data))
if len(data) < 12 {
return "", ErrUnexpectedChallengeResponse
}
var r rsp
err = json.Unmarshal(data[11:len(data)-1], &r)
if err != nil {
return "", err
}
if r.Error != "ok" {
return "", errors.New(r.Error)
}
logrus.Debugln("get challenge:", r.Challenge)
return r.Challenge, nil
}
func (p *Portal) PasswordHMd5(challenge string) string {
var buf [16]byte
h := hmac.New(md5.New, helper.StringToBytes(challenge))
_, _ = h.Write(helper.StringToBytes(p.pwd))
return hex.EncodeToString(h.Sum(buf[:0]))
}
func (p *Portal) Login(challenge string) error {
info := EncodeUserInfo(p.String(), challenge)
hmd5 := p.PasswordHMd5(challenge)
u := fmt.Sprintf(PortalLogin, "gondportal", url.QueryEscape(p.nam), hmd5, p.ip, p.CheckSum(challenge, hmd5, info), url.QueryEscape(info), time.Now().UnixMilli())
logrus.Debugln("GET", u)
data, err := web.RequestDataWith(web.NewDefaultClient(), u, "GET", "", PortalHeaderUA)
if err != nil {
return err
}
logrus.Debugln("get login resp:", helper.BytesToString(data))
if len(data) < 12 {
return ErrUnexpectedLoginResponse
}
var r rsp
err = json.Unmarshal(data[11:len(data)-1], &r)
if err != nil {
return err
}
logrus.Debugln("login rsp:", &r)
if r.Error != "ok" {
return errors.New(r.Error)
}
return nil
}
func (p *Portal) String() string {
return fmt.Sprintf(PortalUserInfo, p.nam, p.pwd, p.ip)
}

99
portal/server.go Normal file
View File

@@ -0,0 +1,99 @@
package portal
import (
"crypto/sha1"
"encoding/binary"
"encoding/hex"
"github.com/fumiama/go-nd-portal/base64"
"github.com/fumiama/go-nd-portal/helper"
)
const (
PortalServerIP = "10.253.0.237"
PortalDomain = "@dx-uestc"
PortalGetChallenge = "http://" + PortalServerIP + "/cgi-bin/get_challenge?callback=%s&username=%s" + PortalDomain + "&ip=%v&_=%d"
PortalLogin = "http://" + PortalServerIP + "/cgi-bin/srun_portal?callback=%s&action=login&username=%s" + PortalDomain + "&password={MD5}%s&ac_id=1&ip=%v&chksum=%s&info={SRBX1}%s&n=200&type=1&os=Windows+10&name=Windows&double_stack=0&_=%d"
)
const (
PortalHeaderUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.56"
)
const (
PortalUserInfo = `{"username":"%s` + PortalDomain + `","password":"%s","ip":"%v","acid":"1","enc_ver":"srun_bx1"}`
)
func EncodeUserInfo(info, challenge string) string {
if len(info) == 0 || len(challenge) == 0 || len(challenge)%4 != 0 {
return ""
}
sc := len(info)
if sc%4 != 0 {
sc = (sc/4 + 1) * 4
}
userinfo := make([]byte, sc)
copy(userinfo, info)
v := make([]uint32, sc/4, sc/4+1)
for i := 0; i < sc/4; i++ {
v[i] = binary.LittleEndian.Uint32(userinfo[i*4 : i*4+4])
}
v = append(v, uint32(len(info)))
sc = len(challenge)
if sc < 16 {
sc = 16
}
k := make([]uint32, sc/4)
token := helper.StringToBytes(challenge)
for i := 0; i < sc/4; i++ {
k[i] = binary.LittleEndian.Uint32(token[i*4 : i*4+4])
}
n := len(v) - 1
z := v[n]
d := uint32(0)
for q := 0; q < 6+52/(n+1); q++ {
d += uint32(0x86014019|0x183639A0) & uint32(0x8CE0D9BF|0x731F2640)
e := (d >> 2) & 3
for p := 0; p < n; p++ {
y := v[p+1]
m := (z >> 5) ^ (y << 2)
m += ((y >> 3) ^ (z << 4)) ^ (d ^ y)
m += k[(uint32(p)&3)^e] ^ z
v[p] += m & (0xEFB8D130 | 0x10472ECF)
z = v[p]
}
y := v[0]
m := (z >> 5) ^ (y << 2)
m += ((y >> 3) ^ (z << 4)) ^ (d ^ y)
m += k[uint32(n)&3^e] ^ z
v[n] += m & (0xBB390742 | 0x44C6F8BD)
z = v[n]
}
lv := make([]byte, len(v)*4)
for i := 0; i < len(v); i++ {
binary.LittleEndian.PutUint32(lv[i*4:i*4+4], v[i])
}
return base64.Base64Encoding.EncodeToString(lv)
}
func (p *Portal) CheckSum(challenge, hmd5, info string) string {
var buf [20]byte
h := sha1.New()
_, _ = h.Write(helper.StringToBytes(challenge))
_, _ = h.Write(helper.StringToBytes(p.nam))
_, _ = h.Write([]byte(PortalDomain))
_, _ = h.Write(helper.StringToBytes(challenge))
_, _ = h.Write(helper.StringToBytes(hmd5))
_, _ = h.Write(helper.StringToBytes(challenge))
_, _ = h.Write([]byte("1")) // ac_id
_, _ = h.Write(helper.StringToBytes(challenge))
_, _ = h.Write(helper.StringToBytes(p.ip.String()))
_, _ = h.Write(helper.StringToBytes(challenge))
_, _ = h.Write([]byte("200")) // n
_, _ = h.Write(helper.StringToBytes(challenge))
_, _ = h.Write([]byte("1")) // type
_, _ = h.Write(helper.StringToBytes(challenge))
_, _ = h.Write([]byte("{SRBX1}"))
_, _ = h.Write(helper.StringToBytes(info))
return hex.EncodeToString(h.Sum(buf[:0]))
}

82
portal/server_test.go Normal file
View File

@@ -0,0 +1,82 @@
package portal
import (
"crypto/sha1"
"encoding/binary"
"encoding/hex"
"net"
"testing"
"github.com/stretchr/testify/assert"
"github.com/fumiama/go-nd-portal/helper"
)
func TestDecodeInfo(t *testing.T) {
info := `{"username":"2000010101001@dx-uestc","password":"12345678","ip":"1.2.3.4","acid":"1","enc_ver":"srun_bx1"}`
sc := len(info)
if sc%4 != 0 {
sc = (sc/4 + 1) * 4
}
userinfo := make([]byte, sc)
copy(userinfo, info)
v := make([]uint32, sc/4, sc/4+1)
for i := 0; i < sc/4; i++ {
v[i] = binary.LittleEndian.Uint32(userinfo[i*4 : i*4+4])
}
v = append(v, uint32(len(info)))
assert.Equal(t, []uint32{1937056379, 1634628197, 975332717, 808464930, 808529968, 808529969, 1681928496, 1702178168, 576943219, 1634738732, 1870099315, 975332466, 858927394, 926299444, 573317688, 975335529, 841888034, 875442990, 1629629474, 577005923, 573645370, 1852121644, 1702256483, 574235250, 1853190771, 829973087, 32034, 106}, v)
}
func TestDecodeKey(t *testing.T) {
challenge := "c312a4194d4310695b71d92ac3c740198a14a7a280022f89408edec4e932d1e5"
sc := len(challenge)
k := make([]uint32, sc/4)
token := helper.StringToBytes(challenge)
for i := 0; i < sc/4; i++ {
k[i] = binary.LittleEndian.Uint32(token[i*4 : i*4+4])
}
assert.Equal(t, []uint32{842085219, 959525985, 859071540, 959852593, 825713205, 1630681444, 929248099, 959524916, 875651384, 845231969, 842018872, 959997490, 1698181172, 878929252, 842217829, 895824228}, k)
}
func TestEncodeUserInfo(t *testing.T) {
u, err := NewPortal("2001010101001", "1234567890", net.IPv4(113, 54, 148, 243).To4())
if err != nil {
t.Fatal(err)
}
t.Log(u.String())
r := EncodeUserInfo(u.String(), "d26466d4036507dadb17e87e23358126e0210cb289d19151f59bcfcefdcf345e")
assert.Equal(t, "CfVnZ9mvKmdgvm/ivovlPibZL6RLAWcx+nBTaYmWH3kmThco+eO4LVsCPFceSmM9PyI0UcMgLE7bmpfY9pr0EWnWdTncXrbW29Aydp+lw6QjxKMgNzgYd7uopiPbIyKpxvJZDHsGw5xh8rMEeq3JXrD2vex27xeI", r)
}
func TestHMd5(t *testing.T) {
u, err := NewPortal("2001010101001", "1234567890", net.IPv4(113, 54, 148, 243).To4())
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "91062ae815fb02d9d15aec834aafffd4", u.PasswordHMd5("d26466d4036507dadb17e87e23358126e0210cb289d19151f59bcfcefdcf345e"))
}
func TestSha1(t *testing.T) {
h := sha1.New()
h.Write([]byte("123456"))
assert.Equal(t, "7c4a8d09ca3762af61e59520943dc26494f8941b", hex.EncodeToString(h.Sum(nil)))
}
func TestCheckSum(t *testing.T) {
u, err := NewPortal("2001010101001", "1234567890", net.IPv4(113, 54, 148, 243).To4())
if err != nil {
t.Fatal(err)
}
t.Log(u.String())
challenge := "d26466d4036507dadb17e87e23358126e0210cb289d19151f59bcfcefdcf345e"
s := u.CheckSum(
challenge,
u.PasswordHMd5(challenge),
EncodeUserInfo(
u.String(),
challenge,
),
)
assert.Equal(t, "64e8913b6019df98e3b807343b8785856909d745", s)
}