Skip to content

Commit

Permalink
full multiuser shadowsocks
Browse files Browse the repository at this point in the history
full multiuser shadowsocks +
fix logs after api changes

Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
  • Loading branch information
MHSanaei and alireza0 committed Jul 27, 2023
1 parent 4cfed17 commit 145ea1e
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 27 deletions.
5 changes: 4 additions & 1 deletion sub/subService.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
19 changes: 14 additions & 5 deletions web/assets/js/model/xray.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -2111,6 +2118,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {

toJson() {
return {
method: this.method,
password: this.password,
email: this.email,
limitIp: this.limitIp,
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion web/html/xui/client_modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
},
Expand Down
2 changes: 1 addition & 1 deletion web/html/xui/form/protocol/shadowsocks.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='{{ i18n "password" }}'>
<a-form-item v-if="inbound.isSS2022" label='{{ i18n "password" }}'>
<a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
<a-input v-model.trim="inbound.settings.password" style="width: 250px;"></a-input>
</a-form-item>
Expand Down
9 changes: 9 additions & 0 deletions web/html/xui/inbound_modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
Expand Down
73 changes: 55 additions & 18 deletions web/service/inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
}
Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion web/service/xray.go
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down
2 changes: 2 additions & 0 deletions xray/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down

1 comment on commit 145ea1e

@pifagorr
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greetings! How many users is one server designed for? Let's say I now have a customer base of 10,000 people. Yesterday they started a mass move to your place, but the problem is that a quarter of people have no connection at all. At the same time, I added only 400 people. Shadow 2022 aes protocol. Port 443. It also works for many users, but the same instagram lags. The rails are loaded through one, the stories are the same. Messengers also sometimes hang and are endlessly updated. Is this an admin or client issue? The application uses v2box and foxray on ios. All clients are located in Russia. xray 1.8.1. If you need server logs, I can drop them for research.

Please sign in to comment.