diff --git a/sub/subService.go b/sub/subService.go index 100d98d3..d650da0c 100644 --- a/sub/subService.go +++ b/sub/subService.go @@ -755,7 +755,10 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, ex } } - encPart := fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password) + encPart := fmt.Sprintf("%s:%s", method, clients[clientIndex].Password) + if method[0] == '2' { + encPart = fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password) + } link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port) url, _ := url.Parse(link) q := url.Query() diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js index 23df0364..d8dc9f72 100644 --- a/web/assets/js/model/xray.js +++ b/web/assets/js/model/xray.js @@ -16,9 +16,10 @@ const VmessMethods = { }; const SSMethods = { - CHACHA20_POLY1305: 'chacha20-poly1305', AES_256_GCM: 'aes-256-gcm', AES_128_GCM: 'aes-128-gcm', + CHACHA20_POLY1305: 'chacha20-poly1305', + XCHACHA20_POLY1305: 'xchacha20-poly1305', BLAKE3_AES_128_GCM: '2022-blake3-aes-128-gcm', BLAKE3_AES_256_GCM: '2022-blake3-aes-256-gcm', BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305', @@ -1040,7 +1041,10 @@ class Inbound extends XrayCommonClass { } } get isSSMultiUser() { - return [SSMethods.BLAKE3_AES_128_GCM,SSMethods.BLAKE3_AES_256_GCM].includes(this.method); + return this.method != SSMethods.BLAKE3_CHACHA20_POLY1305; + } + get isSS2022(){ + return this.method.substring(0,4) === "2022"; } get serverName() { @@ -1470,9 +1474,11 @@ class Inbound extends XrayCommonClass { break; } - let clientPassword = this.isSSMultiUser ? ':' + settings.shadowsockses[clientIndex].password : ''; + let password = new Array(); + if (this.isSSMultiUser) password.push(settings.shadowsockses[clientIndex].password); + if (this.isSS2022) password.push(settings.password); - let link = `ss://${safeBase64(settings.method + ':' + settings.password + clientPassword)}@${address}:${this.port}`; + let link = `ss://${safeBase64(settings.method + ':' + password.join(':'))}@${address}:${this.port}`; const url = new URL(link); for (const [key, value] of params) { url.searchParams.set(key, value) @@ -2097,8 +2103,9 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings { }; Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass { - constructor(password=RandomUtil.randomShadowsocksPassword(), email=RandomUtil.randomLowerAndNum(8),limitIp=0, totalGB=0, expiryTime=0, enable=true, tgId='', subId=RandomUtil.randomLowerAndNum(16)) { + constructor(method='', password=RandomUtil.randomShadowsocksPassword(), email=RandomUtil.randomLowerAndNum(8),limitIp=0, totalGB=0, expiryTime=0, enable=true, tgId='', subId=RandomUtil.randomLowerAndNum(16)) { super(); + this.method = method; this.password = password; this.email = email; this.limitIp = limitIp; @@ -2111,6 +2118,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass { toJson() { return { + method: this.method, password: this.password, email: this.email, limitIp: this.limitIp, @@ -2124,6 +2132,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass { static fromJson(json = {}) { return new Inbound.ShadowsocksSettings.Shadowsocks( + json.method, json.password, json.email, json.limitIp, diff --git a/web/html/xui/client_modal.html b/web/html/xui/client_modal.html index a1d4dce8..e7c23098 100644 --- a/web/html/xui/client_modal.html +++ b/web/html/xui/client_modal.html @@ -70,7 +70,7 @@ case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.Vmess()); case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS()); case Protocols.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan()); - case Protocols.SHADOWSOCKS: return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks()); + case Protocols.SHADOWSOCKS: return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks(clients[0].method)); default: return null; } }, diff --git a/web/html/xui/form/protocol/shadowsocks.html b/web/html/xui/form/protocol/shadowsocks.html index 8e16b143..486b372f 100644 --- a/web/html/xui/form/protocol/shadowsocks.html +++ b/web/html/xui/form/protocol/shadowsocks.html @@ -115,7 +115,7 @@ [[ method ]] - + diff --git a/web/html/xui/inbound_modal.html b/web/html/xui/inbound_modal.html index 65988b14..a10692cc 100644 --- a/web/html/xui/inbound_modal.html +++ b/web/html/xui/inbound_modal.html @@ -110,6 +110,15 @@ if (this.inModal.inbound.settings.shadowsockses.length ==0){ this.inModal.inbound.settings.shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()]; } + if (["aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305"].includes(this.inModal.inbound.settings.method)) { + this.inModal.inbound.settings.shadowsockses.forEach(client => { + client.method = this.inModal.inbound.settings.method; + }) + } else { + this.inModal.inbound.settings.shadowsockses.forEach(client => { + client.method = ""; + }) + } } else { if (this.inModal.inbound.settings.shadowsockses.length > 0){ this.inModal.inbound.settings.shadowsockses = []; diff --git a/web/service/inbound.go b/web/service/inbound.go index b77b2209..1a350a40 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -327,14 +327,15 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, inboundJson, err2 := json.MarshalIndent(oldInbound.GenXrayInboundConfig(), "", " ") if err2 != nil { logger.Debug("Unable to marshal updated inbound config:", err2) - } - - err2 = s.xrayApi.AddInbound(inboundJson) - if err1 == nil { - logger.Debug("Updated inbound added by api:", oldInbound.Tag) - } else { - logger.Debug("Unable to update inbound by api:", err2) needRestart = true + } else { + err2 = s.xrayApi.AddInbound(inboundJson) + if err2 == nil { + logger.Debug("Updated inbound added by api:", oldInbound.Tag) + } else { + logger.Debug("Unable to update inbound by api:", err2) + needRestart = true + } } } } @@ -461,15 +462,21 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) { if len(client.Email) > 0 { s.AddClientStat(tx, data.Id, &client) if client.Enable { + cipher := "" + if oldInbound.Protocol == "shadowsocks" { + cipher = oldSettings["method"].(string) + } err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{ "email": client.Email, "id": client.ID, "flow": client.Flow, "password": client.Password, + "cipher": cipher, }) if err1 == nil { logger.Debug("Client added by api:", client.Email) } else { + logger.Debug("Error in adding client by api:", err1) needRestart = true } } @@ -536,15 +543,18 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool, return false, err } needRestart := false - s.xrayApi.Init(p.GetAPIPort()) if len(email) > 0 { - err = s.xrayApi.RemoveUser(oldInbound.Tag, email) - if err == nil { + s.xrayApi.Init(p.GetAPIPort()) + err1 := s.xrayApi.RemoveUser(oldInbound.Tag, email) + if err1 == nil { logger.Debug("Client deleted by api:", email) needRestart = false + } else { + logger.Debug("Unable to del client by api:", err1) + needRestart = true } + s.xrayApi.Close() } - s.xrayApi.Close() return needRestart, db.Save(oldInbound).Error } @@ -650,26 +660,35 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin } } needRestart := false - s.xrayApi.Init(p.GetAPIPort()) if len(oldEmail) > 0 { + s.xrayApi.Init(p.GetAPIPort()) s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail) if clients[0].Enable { + cipher := "" + if oldInbound.Protocol == "shadowsocks" { + cipher = oldSettings["method"].(string) + } err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{ "email": clients[0].Email, "id": clients[0].ID, "flow": clients[0].Flow, "password": clients[0].Password, + "cipher": cipher, }) if err1 == nil { logger.Debug("Client edited by api:", clients[0].Email) - needRestart = false + } else { + logger.Debug("Error in adding client by api:", err1) + needRestart = true } } else { logger.Debug("Client disabled by api:", clients[0].Email) - needRestart = false } + s.xrayApi.Close() + } else { + logger.Debug("Client old email not found") + needRestart = true } - s.xrayApi.Close() return needRestart, tx.Save(oldInbound).Error } @@ -723,6 +742,11 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e return err } + // Avoid empty slice error + if len(dbClientTraffics) == 0 { + return nil + } + dbClientTraffics, err = s.adjustTraffics(tx, dbClientTraffics) if err != nil { return err @@ -814,10 +838,11 @@ func (s *InboundService) DisableInvalidInbounds() (bool, int64, error) { } s.xrayApi.Init(p.GetAPIPort()) for _, tag := range tags { - err = s.xrayApi.DelInbound(tag) + err1 := s.xrayApi.DelInbound(tag) if err == nil { logger.Debug("Inbound disabled by api:", tag) } else { + logger.Debug("Error in disabling inbound by api:", err1) needRestart = true } } @@ -853,10 +878,11 @@ func (s *InboundService) DisableInvalidClients() (bool, int64, error) { } s.xrayApi.Init(p.GetAPIPort()) for _, result := range results { - err = s.xrayApi.RemoveUser(result.Tag, result.Email) - if err == nil { + err1 := s.xrayApi.RemoveUser(result.Tag, result.Email) + if err1 == nil { logger.Debug("Client disabled by api:", result.Email) } else { + logger.Debug("Error in disabling client by api:", err1) needRestart = true } } @@ -1254,15 +1280,26 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (bool, e for _, client := range clients { if client.Email == clientEmail { s.xrayApi.Init(p.GetAPIPort()) + cipher := "" + if string(inbound.Protocol) == "shadowsocks" { + var oldSettings map[string]interface{} + err = json.Unmarshal([]byte(inbound.Settings), &oldSettings) + if err != nil { + return false, err + } + cipher = oldSettings["method"].(string) + } err1 := s.xrayApi.AddUser(string(inbound.Protocol), inbound.Tag, map[string]interface{}{ "email": client.Email, "id": client.ID, "flow": client.Flow, "password": client.Password, + "cipher": cipher, }) if err1 == nil { logger.Debug("Client enabled due to reset traffic:", clientEmail) } else { + logger.Debug("Error in enabling client by api:", err1) needRestart = true } s.xrayApi.Close() diff --git a/web/service/xray.go b/web/service/xray.go index 668d6952..4a9bf7b2 100644 --- a/web/service/xray.go +++ b/web/service/xray.go @@ -116,7 +116,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) { } } for key := range c { - if key != "email" && key != "id" && key != "password" && key != "flow" { + if key != "email" && key != "id" && key != "password" && key != "flow" && key != "method" { delete(c, key) } if c["flow"] == "xtls-rprx-vision-udp443" { diff --git a/xray/api.go b/xray/api.go index f39361f2..56406df0 100644 --- a/xray/api.go +++ b/xray/api.go @@ -63,10 +63,12 @@ func (x *XrayAPI) AddInbound(inbound []byte) error { err := json.Unmarshal(inbound, conf) if err != nil { logger.Debug("Failed to unmarshal inbound:", err) + return err } config, err := conf.Build() if err != nil { logger.Debug("Failed to build inbound Detur:", err) + return err } inboundConfig := command.AddInboundRequest{Inbound: config}