Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow server to bind on zero port number #1185

Closed
joy4eg opened this issue May 2, 2023 · 9 comments
Closed

Allow server to bind on zero port number #1185

joy4eg opened this issue May 2, 2023 · 9 comments

Comments

@joy4eg
Copy link

joy4eg commented May 2, 2023

Currently, you must set port number each time either via config or via API.
Every so often that port could be busy, sometimes not, which adds additional cost to error handling and synchronizations. That becomes really complicated with ssmanager and when you add-remove users fast enough.

The proposal is that:

  • Allow to bind on port zero, and let the OS find the first free port for you (or fail fast in case if there are no open ports available)
  • Return that port number via API:
    • Add a new command like add_dynamic (so as not to break any backward compatibility)
    • As a response, return the port number: OK:{"port": 42}

Regarding UDP, I see two options here:

  • We could try to bind to the same port as TCP, and raise an error if something went wrong.
  • Add a flag to the new API command, like protocol (tcp, tcpudp, etc)

Thoughts?

@zonyitoo
Copy link
Collaborator

zonyitoo commented May 2, 2023

If you are building a multi-users management system, you should allocate ports in the application level.

First of all, the port for a specific user should be fixed all the time, because the "port" in user's configuration is fixed. So the "port" shouldn't be allocated dynamically from the OS. You should allocate a "free port" globally for this specific user.

Secondly, the port should be allocated globally, because there shouldn't have only 1 "machine" or "instance" of server in your system, so if users were transferred from one instance to another, the port should be unique globally.

If the server is your personal private server, then you could always set a fixed "free" port every time when the ssserver process starts.

Summary: Allow binding to port 0 is not necessary.

@joy4eg
Copy link
Author

joy4eg commented May 2, 2023

you should allocate ports in the application level.

That might not be possible. For instance, with userbase about ~200k you cannot pre-allocate a unique port for each user. That's why the “on-demand” approach fits better here, when you assign port only when the user really needs it.

@zonyitoo
Copy link
Collaborator

zonyitoo commented May 4, 2023

Well, since you have ~200k users, you must have a cluster of machines. For each users, you must provide a unique tuple (server, server_port) for him to put into his own local configuration.

In other words, the unique key of (server, server_port) must be unique globally in your system, which is what I said about "allocate ports in the application level".

Allow binding to port 0 is Ok to me, but I don't think you should rely on this strategy.

@joy4eg
Copy link
Author

joy4eg commented May 4, 2023

(server, server_port)

That's a suitable approach in general, when binding is static (i.e., assign once and forget.)

In my use case, users can migrate from one server to another frequently (each ~5-10min).
Some servers are very busy (i.e., no free slots/ports), some not. Having some kind of “queue” per server would introduce more problems than solve (without even taking into account the port wait time).

@zonyitoo
Copy link
Collaborator

zonyitoo commented May 4, 2023

Nope. The binding is just a record about which servers and ports are occupied, you can change whatever you want dynamically.

In other words: you can always know which ports are "free" in your system.

zonyitoo added a commit that referenced this issue May 4, 2023
    Build server with ServerBuilder, build() will create both TCP & UDP
    servers' listener, which will allow users to retrieve the actual
    listening address of the listeners.

    Basic feature support of #1185 .
@zonyitoo
Copy link
Collaborator

zonyitoo commented May 4, 2023

I just tried to run ssserver with port 0, TCP and UDP servers were listening to 2 different ports. I am not sure that this was what you expected.

@joy4eg
Copy link
Author

joy4eg commented May 4, 2023

I just tried to run ssserver with port 0, TCP and UDP servers were listening to 2 different ports. I am not sure that this was what you expected.

The idea was to bind TCP port on 0, then get the actual port number, and try to bind UDP on it.

Anyway, that sounds great. Thank you for your work!

@zonyitoo
Copy link
Collaborator

zonyitoo commented May 5, 2023

The current implementation doesn't work like that. Both TCP & UDP will try to bind() on port 0 and eventually they will bind to 2 different ports. So there is 2 options:

  1. Make a wrapper around shadowsocks_service::server::Server, retrieve the actual bind()ed addresses from server.tcp_server() and server.udp_server()
  2. Allocate a free port in the application level, and then run ssserver or ssmanager directly.

I still prefer the second solution, which is much more simplier.

@zonyitoo
Copy link
Collaborator

Already provided Builder interfaces for retriving bound socket addresses for all service instances.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants