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}