From c10ccdf2d709393abd7e84ec304b9ac957f857de Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月25日 16:35:21 +0800 Subject: [PATCH 01/43] Create LICENSE --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..02649d5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 Falseen + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 50734371a9992129d0af8390862da3da4eb2bc39 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月25日 17:49:08 +0800 Subject: [PATCH 02/43] update limit_clients use the new scoket only suport tcp --- Limit_Clients/socket.py | 151 ++++++++++++++++++---------------------- 1 file changed, 67 insertions(+), 84 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index b527311..8cbb675 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -19,7 +19,7 @@ # 功能:限制客户端数量(基于ip判断) # # 使用说明:1.将此文件放在ss根目录中即可生效,不用做其他设置(需要重新运行ss)。 -# 2.修改53、54行的 clean_time 和 ip_numbers 为你想要的数值。 +# 2.修改53、54行的 clean_time 和 client_num 为你想要的数值。 # 3.如果你的服务器有ipv6,并且你想让ip4和ip6分开限制,则可以设置 only_port 为 False 。 # # @@ -51,93 +51,61 @@ clean_time = 60 # 设置清理ip的时间间隔,在此时间内无连接的ip会被清理 -ip_numbers = 2 # 设置每个端口的允许通过的ip数量,即设置客户端ip数量 +client_num = 1 # 设置每个端口的允许通过的ip数量,即设置客户端ip数量 only_port = True # 设置是否只根据端口判断。如果为 True ,则只根据端口判断。如果为 False ,则会严格的根据 服务端ip+端口进行判断 # 动态path类方法 -def re_class_method(_class, method_name, re_method): +def new_class_method(_class, method_name, new_method): method = getattr(_class, method_name) info = sys.version_info if info[0]>= 3: setattr(_class, method_name, - types.MethodType(lambda *args, **kwds: re_method(method, *args, **kwds), _class)) + types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), _class)) else: setattr(_class, method_name, - types.MethodType(lambda *args, **kwds: re_method(method, *args, **kwds), None, _class)) + types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), None, _class)) # 动态path实例方法 -def re_self_method(self, method_name, re_method): +def new_self_method(self, method_name, new_method): method = getattr(self, method_name) - setattr(self, method_name, types.MethodType(lambda *args, **kwds: re_method(method, *args, **kwds), self, self)) + setattr(self, method_name, types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), self, self)) # 处理Tcp连接 -def re_accept(old_method, self, *args, **kwds): +def new_accept(orgin_method, self, *args, **kwds): + + def new_close(orgin_method, socket_self, *args, **kwds): + addr = socket_self.getpeername()[0] + fd = self_socket.fileno() + if len(self._client_list[addr]) == 1: + del self._client_list[addr] + logging.info("[socket] close and remove the socket %s" % (addr)) + else: + self._client_list[addr].remove(fd) + logging.info("[socket] close and remove the fd %s" % (addr)) + return orgin_method(*args, **kwds) while True: - - return_value = old_method(self, *args, **kwds) + return_value = orgin_method(self, *args, **kwds) self_socket = return_value[0] - - if only_port: - server_ip_port = '%s' % self.getsockname()[1] - else: - server_ip_port = '%s_%s' % (self.getsockname()[0], self.getsockname()[1]) - - client_ip = return_value[1][0] - - client_ip_list = [x[0].split('#')[0] for x in self._list_client_ip[server_ip_port]] - - if len(self._list_client_ip[server_ip_port]) == 0: - logging.debug("[re_socket] first add %s" % client_ip) - self._list_client_ip[server_ip_port].append(['%s#%s' % (client_ip, time.time()), self_socket]) - return return_value - - if client_ip in client_ip_list: - logging.debug("[re_socket] update socket in %s" % client_ip) - _ip_index = client_ip_list.index(client_ip) - self._list_client_ip[server_ip_port][_ip_index][0] = '%s#%s' % (client_ip, time.time()) - self._list_client_ip[server_ip_port][_ip_index].append(self_socket) + client_ip, client_port = return_value[1] + fd = self_socket.fileno() + if len(self._client_list) < client_num or client_ip in self._client_list: + new_self_method(return_value[0], 'close', new_close) + logging.info("[socket] add %s:%d" %(client_ip, client_port)) + if self._client_list.get(client_ip, None) == None: + self._client_list.update({client_ip:[]}) + self._client_list[client_ip].append(fd) return return_value - - else: - if len(self._list_client_ip[server_ip_port]) < ip_numbers: - logging.debug("[re_socket] add %s" % client_ip) - self._list_client_ip[server_ip_port].append(['%s#%s' % (client_ip, time.time()), self_socket]) - return return_value - - for x in [x for x in self._list_client_ip[server_ip_port]]: - is_closed = True - if time.time() - float(x[0].split('#')[1])> clean_time: - - for y in x[1:]: - try: - y.getpeername() # 判断连接是否关闭 - is_closed = False - break - except: # 如果抛出异常,则说明连接已经关闭,这时可以关闭套接字 - logging.debug("[re_socket] close and remove the time out socket 1/%s" % (len(x[1:]))) - x.remove(y) - - if not is_closed: - logging.debug('[re_socket] the %s still exists and update last_time' % str(x[1].getpeername()[0])) - _ip_index = client_ip_list.index(x[0].split('#')[0]) - self._list_client_ip[server_ip_port][_ip_index][0] = '%s#%s' % (x[0].split('#')[0], time.time()) - - else: - logging.info("[re_socket] remove time out ip and add new ip %s" % client_ip ) - self._list_client_ip[server_ip_port].remove(x) - self._list_client_ip[server_ip_port].append(['%s#%s' % (client_ip, time.time()), self_socket]) - return return_value - - if int(time.time()) % 5 == 0: - logging.debug("[re_socket] the port %s client more then the %s" % (server_ip_port, ip_numbers)) + server_addr, server_port = self.getsockname() + logging.info("[socket] the %s:%d client more then the %s" % (server_addr, server_port, client_num)) + self_socket.close() # 处理Udp连接 -def re_recvfrom(old_method, self, *args, **kwds): +def new_recvfrom(orgin_method, self, *args, **kwds): while True: - return_value = old_method(*args, **kwds) + return_value = orgin_method(*args, **kwds) self_socket = '' if only_port: server_ip_port = '%s' % self.getsockname()[1] @@ -147,18 +115,18 @@ def re_recvfrom(old_method, self, *args, **kwds): client_ip_list = [x[0].split('#')[0] for x in self._list_client_ip[server_ip_port]] if len(self._list_client_ip[server_ip_port]) == 0: - logging.debug("[re_socket] first add %s" % client_ip) + logging.debug("[socket] first add %s" % client_ip) self._list_client_ip[server_ip_port].append(['%s#%s' % (client_ip, time.time()), self_socket]) return return_value if client_ip in client_ip_list: - logging.debug("[re_socket] update socket in %s" % client_ip) + logging.debug("[socket] update socket in %s" % client_ip) _ip_index = client_ip_list.index(client_ip) self._list_client_ip[server_ip_port][_ip_index][0] = '%s#%s' % (client_ip, time.time()) return return_value else: - if len(self._list_client_ip[server_ip_port]) < ip_numbers: - logging.debug("[re_socket] add %s" % client_ip) + if len(self._list_client_ip[server_ip_port]) < client_num: + logging.debug("[socket] add %s" % client_ip) self._list_client_ip[server_ip_port].append(['%s#%s' % (client_ip, time.time()), self_socket]) return return_value @@ -172,38 +140,53 @@ def re_recvfrom(old_method, self, *args, **kwds): is_closed = False break except: # 如果抛出异常,则说明连接已经关闭,这时可以关闭套接字 - logging.debug("[re_socket] close and remove the time out socket 1/%s" % (len(x[1:]))) + logging.debug("[socket] close and remove the time out socket 1/%s" % (len(x[1:]))) x.remove(y) if not is_closed: - logging.debug('[re_socket] the %s still exists and update last_time' % str(x[1].getpeername()[0])) + logging.debug('[socket] the %s still exists and update last_time' % str(x[1].getpeername()[0])) _ip_index = client_ip_list.index(x[0].split('#')[0]) self._list_client_ip[server_ip_port][_ip_index][0] = '%s#%s' % (x[0].split('#')[0], time.time()) else: - logging.info("[re_socket] remove time out ip and add new ip %s" % client_ip ) + logging.info("[socket] remove time out ip and add new ip %s" % client_ip ) self._list_client_ip[server_ip_port].remove(x) self._list_client_ip[server_ip_port].append(['%s#%s' % (client_ip, time.time()), self_socket]) return return_value if int(time.time()) % 5 == 0: - logging.debug("[re_socket] the port %s client more then the %s" % (server_ip_port, ip_numbers)) + logging.debug("[socket] the port %s client more then the %s" % (server_ip_port, client_num)) new_tuple = [b'', return_value[1]] return_value = tuple(new_tuple) return return_value -def re_bind(old_method, self, *args, **kwds): - if only_port: - port = '%s' % args[0][1] - else: - port = '%s_%s' % (args[0][0], args[0][1]) - self._list_client_ip[port] = [] - re_self_method(self, 'recvfrom', re_recvfrom) - old_method(self, *args, **kwds) +def new_bind(orgin_method, self, *args, **kwds): + #new_self_method(self, 'recvfrom', new_recvfrom) + orgin_method(self, *args, **kwds) + + +class new_socket(socket.socket): + + def __init__(self, *args, **kwds): + super(new_socket, self).__init__(*args, **kwds) + # new_self_method(self, 'sendto', new_sendto) + # new_self_method(self, 'recvfrom', new_recvfrom) + self._client_list = {} + + def close(self, *args, **kwds): + super(new_socket, self).__init__(*args, **kwds) + + +class new_client_socket(socket.socket): + def __init__(self, *args, **kwds): + super(new_client_socket, self).__init__(*args, **kwds) -setattr(socket.socket, '_list_client_ip', {}) -re_class_method(socket.socket, 'bind', re_bind) -re_class_method(socket.socket, 'accept', re_accept) + def close(self ,*args, **kwds): + super(new_client_socket, self).__init__(*args, **kwds) +new_class_method(socket.socket, 'bind', new_bind) +new_class_method(socket.socket, 'accept', new_accept) +socket.socket = new_socket +socket._socketobject = new_client_socket \ No newline at end of file From 2fda2cc39ecd310725179a1c88f0eaec48962154 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月25日 18:07:49 +0800 Subject: [PATCH 03/43] update limit_clients remove fd --- Limit_Clients/socket.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 8cbb675..0f7393f 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -76,26 +76,24 @@ def new_accept(orgin_method, self, *args, **kwds): def new_close(orgin_method, socket_self, *args, **kwds): addr = socket_self.getpeername()[0] - fd = self_socket.fileno() - if len(self._client_list[addr]) == 1: + if self._client_list[addr] <= 1: del self._client_list[addr] - logging.info("[socket] close and remove the socket %s" % (addr)) + logging.info("[socket] remove the socket %s" % (addr)) else: - self._client_list[addr].remove(fd) - logging.info("[socket] close and remove the fd %s" % (addr)) + self._client_list[addr] -= 1 + logging.info("[socket] close the fd %s" % (addr)) return orgin_method(*args, **kwds) while True: return_value = orgin_method(self, *args, **kwds) self_socket = return_value[0] client_ip, client_port = return_value[1] - fd = self_socket.fileno() if len(self._client_list) < client_num or client_ip in self._client_list: new_self_method(return_value[0], 'close', new_close) logging.info("[socket] add %s:%d" %(client_ip, client_port)) if self._client_list.get(client_ip, None) == None: - self._client_list.update({client_ip:[]}) - self._client_list[client_ip].append(fd) + self._client_list.update({client_ip : 0}) + self._client_list[client_ip] += 1 return return_value server_addr, server_port = self.getsockname() logging.info("[socket] the %s:%d client more then the %s" % (server_addr, server_port, client_num)) From ca85e94f431a120f543a7fbe498dc62dfc154939 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月25日 21:14:31 +0800 Subject: [PATCH 04/43] add set_client_timeout --- Limit_Clients/socket.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 0f7393f..a02491b 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -19,7 +19,7 @@ # 功能:限制客户端数量(基于ip判断) # # 使用说明:1.将此文件放在ss根目录中即可生效,不用做其他设置(需要重新运行ss)。 -# 2.修改53、54行的 clean_time 和 client_num 为你想要的数值。 +# 2.修改53、54行的 recv_timeout 和 client_num 为你想要的数值。 # 3.如果你的服务器有ipv6,并且你想让ip4和ip6分开限制,则可以设置 only_port 为 False 。 # # @@ -50,9 +50,13 @@ sys.path.insert(0, path) -clean_time = 60 # 设置清理ip的时间间隔,在此时间内无连接的ip会被清理 -client_num = 1 # 设置每个端口的允许通过的ip数量,即设置客户端ip数量 -only_port = True # 设置是否只根据端口判断。如果为 True ,则只根据端口判断。如果为 False ,则会严格的根据 服务端ip+端口进行判断 + +set_close_timeout = True # 是否清理指定时间内无数据收发的连接, 如果为True则根据下面的两个选项进行清理,否则如果为 False 则不清理。 +recv_timeout = 10 # 设置清理连接的超时时间,在此时间内无连接或无数据 接收 的连接会被清理。 +send_timeout = 10 # 设置清理连接的超时时间,在此时间内无连接或无数据 发送 的连接会被清理。 +client_num = 1 # 设置每个端口的允许通过的ip数量,即设置客户端ip数量 +only_port = True # 设置是否只根据端口判断。如果为 True ,则只根据端口判断。如果为 False ,则会严格的根据 服务端ip+端口进行判断 + # 动态path类方法 def new_class_method(_class, method_name, new_method): @@ -75,13 +79,13 @@ def new_self_method(self, method_name, new_method): def new_accept(orgin_method, self, *args, **kwds): def new_close(orgin_method, socket_self, *args, **kwds): - addr = socket_self.getpeername()[0] + addr, port = socket_self.getpeername() if self._client_list[addr] <= 1: del self._client_list[addr] - logging.info("[socket] remove the socket %s" % (addr)) + logging.info("[socket] remove the client %s" % (addr)) else: self._client_list[addr] -= 1 - logging.info("[socket] close the fd %s" % (addr)) + logging.info("[socket] close the client socket %s:%d" % (addr, port)) return orgin_method(*args, **kwds) while True: @@ -94,6 +98,10 @@ def new_close(orgin_method, socket_self, *args, **kwds): if self._client_list.get(client_ip, None) == None: self._client_list.update({client_ip : 0}) self._client_list[client_ip] += 1 + if set_close_timeout: + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, recv_timeout) + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, send_timeout) + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 0) return return_value server_addr, server_port = self.getsockname() logging.info("[socket] the %s:%d client more then the %s" % (server_addr, server_port, client_num)) @@ -130,7 +138,7 @@ def new_recvfrom(orgin_method, self, *args, **kwds): for x in [x for x in self._list_client_ip[server_ip_port]]: is_closed = True - if time.time() - float(x[0].split('#')[1])> clean_time: + if time.time() - float(x[0].split('#')[1])> recv_timeout: for y in x[1:]: try: From 24e873c212ba496a437f9dcf801f0f051a055e97 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月25日 21:32:27 +0800 Subject: [PATCH 05/43] remove socket.SO_SNDTIMEO --- Limit_Clients/socket.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index a02491b..8038b57 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -52,8 +52,7 @@ set_close_timeout = True # 是否清理指定时间内无数据收发的连接, 如果为True则根据下面的两个选项进行清理,否则如果为 False 则不清理。 -recv_timeout = 10 # 设置清理连接的超时时间,在此时间内无连接或无数据 接收 的连接会被清理。 -send_timeout = 10 # 设置清理连接的超时时间,在此时间内无连接或无数据 发送 的连接会被清理。 +recv_timeout = 1000 # 设置清理连接的超时时间,单位毫秒,在此时间内无连接或无数据 接收 的连接会被清理。 client_num = 1 # 设置每个端口的允许通过的ip数量,即设置客户端ip数量 only_port = True # 设置是否只根据端口判断。如果为 True ,则只根据端口判断。如果为 False ,则会严格的根据 服务端ip+端口进行判断 @@ -100,8 +99,6 @@ def new_close(orgin_method, socket_self, *args, **kwds): self._client_list[client_ip] += 1 if set_close_timeout: self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, recv_timeout) - self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, send_timeout) - self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 0) return return_value server_addr, server_port = self.getsockname() logging.info("[socket] the %s:%d client more then the %s" % (server_addr, server_port, client_num)) From 62a60192eb8b5b2eb0ebc91506ba4220b2295fd4 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月26日 05:50:38 +0800 Subject: [PATCH 06/43] update udp and tcp --- Limit_Clients/socket.py | 182 +++++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 87 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 8038b57..ebff462 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # -# Copyright 2015 Falseen +# Copyright 2017 Falseen # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,16 +18,11 @@ # # 功能:限制客户端数量(基于ip判断) # -# 使用说明:1.将此文件放在ss根目录中即可生效,不用做其他设置(需要重新运行ss)。 -# 2.修改53、54行的 recv_timeout 和 client_num 为你想要的数值。 -# 3.如果你的服务器有ipv6,并且你想让ip4和ip6分开限制,则可以设置 only_port 为 False 。 +# 使用说明:1.将此文件放在程序根目录中即可生效,不用做其他设置(需要重新运行ss)。 +# 2.修改 recv_timeout 和 limit_clients_num 为你想要的数值。 +# 3.如果你的服务器有ipv6或有多个ip,并且你想让这些ip分开限制,则可以设置 only_port 为 False 。 # # -# 原理:默认情况下,ss在运行的时候会引用系统中的socket文件,但是把这个socket文件放到ss目录之后,ss就会引用这个我们自定义的socket文件。 -# 然后在这个文件中再引用真正的socket包,并在原socket的基础上加以修改,最终ss调用的就是经过我们修改的socket文件了。 -# -# 所以理论上任何引用了socket包的python程序都可以用这个文件来达到限制连接ip数量的目的。 -# from __future__ import absolute_import, division, print_function, \ @@ -45,19 +40,28 @@ path = sys.path[0] sys.path.pop(0) -import socket # 导入真正的socket包 +# import real socket +import socket sys.path.insert(0, path) +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +set_close_timeout = False # 是否清理指定时间内无数据收发的连接,仅对TCP有效,一般情况下socket会自动关闭正常或异常断开的连接。 + # 如果为True则根据下面的超时时间进行清理,如果为 False 则根据socket的超时时间来清理。 + +recv_timeout = 4000 # 设置清理连接的超时时间,单位毫秒。在此时间内无连接或无数据 接收 的连接会被清理。 + # 主要针对 udp,如果 set_close_timeout 为True的话也对tcp生效,但对于tcp不是强制性的。 + +limit_clients_num = 1 # 设置每个端口的允许通过的ip数量,即客户端的ip数量 +only_port = True # 设置是否只根据端口判断。如果为 True ,则只根据端口判断。如果为 False ,则会严格的根据服务端ip+端口进行判断 + +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -set_close_timeout = True # 是否清理指定时间内无数据收发的连接, 如果为True则根据下面的两个选项进行清理,否则如果为 False 则不清理。 -recv_timeout = 1000 # 设置清理连接的超时时间,单位毫秒,在此时间内无连接或无数据 接收 的连接会被清理。 -client_num = 1 # 设置每个端口的允许通过的ip数量,即设置客户端ip数量 -only_port = True # 设置是否只根据端口判断。如果为 True ,则只根据端口判断。如果为 False ,则会严格的根据 服务端ip+端口进行判断 -# 动态path类方法 +# 动态patch类方法 def new_class_method(_class, method_name, new_method): method = getattr(_class, method_name) info = sys.version_info @@ -68,7 +72,8 @@ def new_class_method(_class, method_name, new_method): setattr(_class, method_name, types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), None, _class)) -# 动态path实例方法 + +# 动态patch实例方法 def new_self_method(self, method_name, new_method): method = getattr(self, method_name) setattr(self, method_name, types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), self, self)) @@ -77,110 +82,107 @@ def new_self_method(self, method_name, new_method): # 处理Tcp连接 def new_accept(orgin_method, self, *args, **kwds): + # 自定义 close 方法,让其在关闭的时候从列表中清理掉自身的 socket 或 ip。 def new_close(orgin_method, socket_self, *args, **kwds): addr, port = socket_self.getpeername() - if self._client_list[addr] <= 1: - del self._client_list[addr] - logging.info("[socket] remove the client %s" % (addr)) - else: - self._client_list[addr] -= 1 - logging.info("[socket] close the client socket %s:%d" % (addr, port)) + server_addrs = self._server_addrs + client_list = self._all_client_list[server_addrs] + if client_list.get(addr, None) != None: + last_up_time = client_list[addr]["last_up_time"] + if client_list[addr]["client_num"] <= 1 and time.time() - last_up_time> recv_timeout: + del client_list[addr] + logging.info("[socket] remove the client %s" % (addr)) + else: + client_list[addr]["client_num"] -= 1 + self._all_client_list[server_addrs].update(client_list) + # logging.debug("[socket] close the client socket %s:%d" % (addr, port)) + return orgin_method(*args, **kwds) while True: return_value = orgin_method(self, *args, **kwds) self_socket = return_value[0] client_ip, client_port = return_value[1] - if len(self._client_list) < client_num or client_ip in self._client_list: - new_self_method(return_value[0], 'close', new_close) - logging.info("[socket] add %s:%d" %(client_ip, client_port)) - if self._client_list.get(client_ip, None) == None: - self._client_list.update({client_ip : 0}) - self._client_list[client_ip] += 1 + server_addrs = self._server_addrs + client_list = self._all_client_list.get(server_addrs, {}) + if len(client_list) < limit_clients_num or client_ip in client_list: + new_self_method(self_socket, 'close', new_close) + # logging.debug("[socket] add %s:%d" %(client_ip, client_port)) + if client_list.get(client_ip, None) == None: + client_list.update({client_ip : {"client_num":0, "last_up_time":0}}) + client_list[client_ip]["client_num"] += 1 + self._all_client_list[server_addrs].update(client_list) if set_close_timeout: self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, recv_timeout) return return_value - server_addr, server_port = self.getsockname() - logging.info("[socket] the %s:%d client more then the %s" % (server_addr, server_port, client_num)) - self_socket.close() + else: + for k,v in self._all_client_list[server_addrs].copy().items(): + last_up_time = v["last_up_time"] + if time.time() - last_up_time> recv_timeout and v["client_num"] <= 1: + del self._all_client_list[server_addrs][k] + if set_close_timeout: + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, recv_timeout) + new_self_method(self_socket, 'close', new_close) + return return_value + if time.time() - self.last_log_time> 10: + self.last_log_time = time.time() + logging.error("[socket] the %s client more then the %d" % (server_addrs, limit_clients_num)) + self_socket.close() + # 处理Udp连接 def new_recvfrom(orgin_method, self, *args, **kwds): while True: return_value = orgin_method(*args, **kwds) - self_socket = '' - if only_port: - server_ip_port = '%s' % self.getsockname()[1] - else: - server_ip_port = '%s_%s' % (self.getsockname()[0], self.getsockname()[1]) - client_ip = return_value[1][0] - client_ip_list = [x[0].split('#')[0] for x in self._list_client_ip[server_ip_port]] - - if len(self._list_client_ip[server_ip_port]) == 0: - logging.debug("[socket] first add %s" % client_ip) - self._list_client_ip[server_ip_port].append(['%s#%s' % (client_ip, time.time()), self_socket]) - return return_value - - if client_ip in client_ip_list: - logging.debug("[socket] update socket in %s" % client_ip) - _ip_index = client_ip_list.index(client_ip) - self._list_client_ip[server_ip_port][_ip_index][0] = '%s#%s' % (client_ip, time.time()) + server_addrs = self._server_addrs + client_ip, client_port = return_value[1] + client_list = self._all_client_list.get(server_addrs, {}) + + if len(client_list) < limit_clients_num or client_ip in client_list: + if client_list.get(client_ip, None) == None: + client_list.update({client_ip : {"client_num":0, "last_up_time":0}}) + client_list[client_ip]["last_up_time"] = time.time() + self._all_client_list[server_addrs].update(client_list) + # logging.debug("[socket] update last_up_time for %s" % client_ip) return return_value else: - if len(self._list_client_ip[server_ip_port]) < client_num: - logging.debug("[socket] add %s" % client_ip) - self._list_client_ip[server_ip_port].append(['%s#%s' % (client_ip, time.time()), self_socket]) - return return_value - - for x in [x for x in self._list_client_ip[server_ip_port]]: - is_closed = True - if time.time() - float(x[0].split('#')[1])> recv_timeout: - - for y in x[1:]: - try: - y.getpeername() # 判断连接是否关闭 - is_closed = False - break - except: # 如果抛出异常,则说明连接已经关闭,这时可以关闭套接字 - logging.debug("[socket] close and remove the time out socket 1/%s" % (len(x[1:]))) - x.remove(y) - - if not is_closed: - logging.debug('[socket] the %s still exists and update last_time' % str(x[1].getpeername()[0])) - _ip_index = client_ip_list.index(x[0].split('#')[0]) - self._list_client_ip[server_ip_port][_ip_index][0] = '%s#%s' % (x[0].split('#')[0], time.time()) - - else: - logging.info("[socket] remove time out ip and add new ip %s" % client_ip ) - self._list_client_ip[server_ip_port].remove(x) - self._list_client_ip[server_ip_port].append(['%s#%s' % (client_ip, time.time()), self_socket]) - return return_value - - if int(time.time()) % 5 == 0: - logging.debug("[socket] the port %s client more then the %s" % (server_ip_port, client_num)) + for k,v in self._all_client_list[server_addrs].copy().items(): + last_up_time = v["last_up_time"] + if time.time() - last_up_time> recv_timeout and v["client_num"] <= 1: + del self._all_client_list[server_addrs][k] + # logging.debug("[socket] update last_up_time for %s" % client_ip) + client_list[client_ip]["last_up_time"] = time.time() + self._all_client_list[server_addrs].update(client_list) + return return_value + + if time.time() - self.last_log_time> 10: + self.last_log_time = time.time() + logging.error("[socket] the port %s client more then the %d" % (server_addrs, client_num)) new_tuple = [b'', return_value[1]] return_value = tuple(new_tuple) return return_value def new_bind(orgin_method, self, *args, **kwds): - #new_self_method(self, 'recvfrom', new_recvfrom) + if only_port: + server_addrs = '*:%s' % args[0][1] + else: + server_addrs = '%s:%s' % (args[0][0], args[0][1]) + self._server_addrs = server_addrs + self._all_client_list.update({server_addrs:{}}) + new_self_method(self, 'recvfrom', new_recvfrom) orgin_method(self, *args, **kwds) +# 自定义 socket 类,可以自定义实例方法或属性。 class new_socket(socket.socket): def __init__(self, *args, **kwds): super(new_socket, self).__init__(*args, **kwds) - # new_self_method(self, 'sendto', new_sendto) - # new_self_method(self, 'recvfrom', new_recvfrom) - self._client_list = {} - - def close(self, *args, **kwds): - super(new_socket, self).__init__(*args, **kwds) +# 为 accept 生成的socket对象创建一个单独的类,目的是给其动态地绑定 close 方法。 class new_client_socket(socket.socket): def __init__(self, *args, **kwds): @@ -189,7 +191,13 @@ def __init__(self, *args, **kwds): def close(self ,*args, **kwds): super(new_client_socket, self).__init__(*args, **kwds) + + +# 添加类属性, 此属性是全局的,所有socket对象都共享此属性。 +setattr(socket.socket, '_all_client_list', {}) +setattr(socket.socket, 'last_log_time', 0) + new_class_method(socket.socket, 'bind', new_bind) new_class_method(socket.socket, 'accept', new_accept) +socket._socketobject = new_client_socket socket.socket = new_socket -socket._socketobject = new_client_socket \ No newline at end of file From e664d22c325d751e67efe047d98b52d0d390e600 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月26日 05:54:29 +0800 Subject: [PATCH 07/43] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61000ce..9d48cff 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ PySocket ,一个通过猴子补丁(monkey patch)动态修改 socket 的项 * **proxy**:基本跟shadowscks一样,只是去掉了hook的代码。 -* **Limit_Clients**:限制客户端数量(基于ip),这是以前的代码,很久没更新了。随后可能会更新。 +* **Limit_Clients**:限制客户端数量(基于ip),支持tcp和udp,基本上完美了。 * **orgin_socket**:原始的socket,放在这里只是为了方便查看代码。毕竟 _socket.pyd 是加密的。 From ece58e89ab26f661291fbe40803b7db741c522fd Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月26日 06:04:42 +0800 Subject: [PATCH 08/43] mv the new_socket --- Limit_Clients/socket.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index ebff462..b5dd6d5 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -79,26 +79,27 @@ def new_self_method(self, method_name, new_method): setattr(self, method_name, types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), self, self)) +# 自定义 close 方法,让其在关闭的时候从列表中清理掉自身的 socket 或 ip。 +def new_close(orgin_method, self_socket, *args, **kwds): + addr, port = self_socket.getpeername() + self = self_socket + server_addrs = self._server_addrs + client_list = self._all_client_list[server_addrs] + if client_list.get(addr, None) != None: + last_up_time = client_list[addr]["last_up_time"] + if client_list[addr]["client_num"] <= 1 and time.time() - last_up_time> recv_timeout: + del client_list[addr] + logging.info("[socket] remove the client %s" % (addr)) + else: + client_list[addr]["client_num"] -= 1 + self._all_client_list[server_addrs].update(client_list) + # logging.debug("[socket] close the client socket %s:%d" % (addr, port)) + return orgin_method(*args, **kwds) + + # 处理Tcp连接 def new_accept(orgin_method, self, *args, **kwds): - # 自定义 close 方法,让其在关闭的时候从列表中清理掉自身的 socket 或 ip。 - def new_close(orgin_method, socket_self, *args, **kwds): - addr, port = socket_self.getpeername() - server_addrs = self._server_addrs - client_list = self._all_client_list[server_addrs] - if client_list.get(addr, None) != None: - last_up_time = client_list[addr]["last_up_time"] - if client_list[addr]["client_num"] <= 1 and time.time() - last_up_time> recv_timeout: - del client_list[addr] - logging.info("[socket] remove the client %s" % (addr)) - else: - client_list[addr]["client_num"] -= 1 - self._all_client_list[server_addrs].update(client_list) - # logging.debug("[socket] close the client socket %s:%d" % (addr, port)) - - return orgin_method(*args, **kwds) - while True: return_value = orgin_method(self, *args, **kwds) self_socket = return_value[0] @@ -106,6 +107,7 @@ def new_close(orgin_method, socket_self, *args, **kwds): server_addrs = self._server_addrs client_list = self._all_client_list.get(server_addrs, {}) if len(client_list) < limit_clients_num or client_ip in client_list: + self_socket._server_addrs = self._server_addrs new_self_method(self_socket, 'close', new_close) # logging.debug("[socket] add %s:%d" %(client_ip, client_port)) if client_list.get(client_ip, None) == None: @@ -122,6 +124,7 @@ def new_close(orgin_method, socket_self, *args, **kwds): del self._all_client_list[server_addrs][k] if set_close_timeout: self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, recv_timeout) + self_socket._server_addrs = self._server_addrs new_self_method(self_socket, 'close', new_close) return return_value if time.time() - self.last_log_time> 10: From bc845b0a8fcf2837ea4547480022df59d6375185 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月26日 06:08:38 +0800 Subject: [PATCH 09/43] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d48cff..a68a591 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ PySocket ,一个通过猴子补丁(monkey patch)动态修改 socket 的项 * **proxy**:基本跟shadowscks一样,只是去掉了hook的代码。 -* **Limit_Clients**:限制客户端数量(基于ip),支持tcp和udp,基本上完美了。 +* **Limit_Clients**:限制客户端数量(基于ip),支持tcp和udp,修改了close方法,在关闭连接的时候清理client list,基本上完美了。 * **orgin_socket**:原始的socket,放在这里只是为了方便查看代码。毕竟 _socket.pyd 是加密的。 From 108577712881f29283d1a01bd7d80f2b030af96f Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月26日 06:11:49 +0800 Subject: [PATCH 10/43] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a68a591..eaee270 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ PySocket ,一个通过猴子补丁(monkey patch)动态修改 socket 的项 * **shadowsocks**: 给服务端添加前置代理的功能(原则上也适用于客户端),支持 http、socks4、socks5 代理。并且通过hook的方式去掉了ss的dns查询,ss在接收到数据之后会直接把域名和请求一起发给代理。 -* **proxy**:基本跟shadowscks一样,只是去掉了hook的代码。 +* **proxy**:基本跟shadowsocks一样,只是去掉了hook的代码。 * **Limit_Clients**:限制客户端数量(基于ip),支持tcp和udp,修改了close方法,在关闭连接的时候清理client list,基本上完美了。 From 2afe4862a27dab9886e4a1bb5d2d3608b13afb73 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月26日 06:16:26 +0800 Subject: [PATCH 11/43] fix new_close --- Limit_Clients/socket.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index b5dd6d5..ae9af8e 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -92,8 +92,9 @@ def new_close(orgin_method, self_socket, *args, **kwds): logging.info("[socket] remove the client %s" % (addr)) else: client_list[addr]["client_num"] -= 1 - self._all_client_list[server_addrs].update(client_list) # logging.debug("[socket] close the client socket %s:%d" % (addr, port)) + self._all_client_list[server_addrs].update(client_list) + return orgin_method(*args, **kwds) From 88e1767ec7d64e410df35887a8126d8e021bc4ee Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月26日 18:48:24 +0800 Subject: [PATCH 12/43] mv new_close to new_class and fix some bug --- Limit_Clients/socket.py | 85 ++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index ae9af8e..eb55516 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -48,12 +48,13 @@ #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -set_close_timeout = False # 是否清理指定时间内无数据收发的连接,仅对TCP有效,一般情况下socket会自动关闭正常或异常断开的连接。 - # 如果为True则根据下面的超时时间进行清理,如果为 False 则根据socket的超时时间来清理。 +set_close_timeout = True # 是否清理指定时间内无数据收发的连接,仅对TCP有效,socket会自动关闭正常或异常断开的连接, 所以一般不用设置。 + # 如果为True则根据下面的超时时间进行清理,如果为 False 则根据socket的超时时间来清理(5分钟左右)。 -recv_timeout = 4000 # 设置清理连接的超时时间,单位毫秒。在此时间内无连接或无数据 接收 的连接会被清理。 - # 主要针对 udp,如果 set_close_timeout 为True的话也对tcp生效,但对于tcp不是强制性的。 +recv_timeout = 1000 # 设置 tcp 清理连接的超时时间,单位毫秒(1000毫秒等于一分钟)。 + # 在此时间内无连接或无数据 接收 的连接会被清理。只针对 tcp。 +recvfrom_timeout = 60 # 设置 udp 清理连接的超时时间,单位秒。在此时间内无连接或无数据 接收 的连接会被清理。只针对 udp。 limit_clients_num = 1 # 设置每个端口的允许通过的ip数量,即客户端的ip数量 only_port = True # 设置是否只根据端口判断。如果为 True ,则只根据端口判断。如果为 False ,则会严格的根据服务端ip+端口进行判断 @@ -79,25 +80,6 @@ def new_self_method(self, method_name, new_method): setattr(self, method_name, types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), self, self)) -# 自定义 close 方法,让其在关闭的时候从列表中清理掉自身的 socket 或 ip。 -def new_close(orgin_method, self_socket, *args, **kwds): - addr, port = self_socket.getpeername() - self = self_socket - server_addrs = self._server_addrs - client_list = self._all_client_list[server_addrs] - if client_list.get(addr, None) != None: - last_up_time = client_list[addr]["last_up_time"] - if client_list[addr]["client_num"] <= 1 and time.time() - last_up_time> recv_timeout: - del client_list[addr] - logging.info("[socket] remove the client %s" % (addr)) - else: - client_list[addr]["client_num"] -= 1 - # logging.debug("[socket] close the client socket %s:%d" % (addr, port)) - self._all_client_list[server_addrs].update(client_list) - - return orgin_method(*args, **kwds) - - # 处理Tcp连接 def new_accept(orgin_method, self, *args, **kwds): @@ -109,7 +91,7 @@ def new_accept(orgin_method, self, *args, **kwds): client_list = self._all_client_list.get(server_addrs, {}) if len(client_list) < limit_clients_num or client_ip in client_list: self_socket._server_addrs = self._server_addrs - new_self_method(self_socket, 'close', new_close) + self_socket.close = self_socket.new_close # logging.debug("[socket] add %s:%d" %(client_ip, client_port)) if client_list.get(client_ip, None) == None: client_list.update({client_ip : {"client_num":0, "last_up_time":0}}) @@ -121,16 +103,18 @@ def new_accept(orgin_method, self, *args, **kwds): else: for k,v in self._all_client_list[server_addrs].copy().items(): last_up_time = v["last_up_time"] - if time.time() - last_up_time> recv_timeout and v["client_num"] <= 1: - del self._all_client_list[server_addrs][k] + if time.time() - last_up_time> recvfrom_timeout and v["client_num"] < 1: + logging.info("[socket] remove the client %s" % (k)) + del client_list[k] if set_close_timeout: self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, recv_timeout) + self._all_client_list[server_addrs].update(client_list) self_socket._server_addrs = self._server_addrs - new_self_method(self_socket, 'close', new_close) + self_socket.close = self_socket.new_close return return_value if time.time() - self.last_log_time> 10: self.last_log_time = time.time() - logging.error("[socket] the %s client more then the %d" % (server_addrs, limit_clients_num)) + logging.error("[socket] the server_addrs %s client more then the %d" % (server_addrs, limit_clients_num)) self_socket.close() @@ -153,30 +137,36 @@ def new_recvfrom(orgin_method, self, *args, **kwds): else: for k,v in self._all_client_list[server_addrs].copy().items(): last_up_time = v["last_up_time"] - if time.time() - last_up_time> recv_timeout and v["client_num"] <= 1: - del self._all_client_list[server_addrs][k] - # logging.debug("[socket] update last_up_time for %s" % client_ip) + if time.time() - last_up_time> recvfrom_timeout and v["client_num"] < 1: + logging.info("[socket] remove the client %s" % (k)) + del client_list[k] + # logging.debug("[socket] add %s:%d" %(client_ip, client_port)) client_list[client_ip]["last_up_time"] = time.time() self._all_client_list[server_addrs].update(client_list) return return_value if time.time() - self.last_log_time> 10: self.last_log_time = time.time() - logging.error("[socket] the port %s client more then the %d" % (server_addrs, client_num)) + logging.error("[socket] the server_addrs %s client more then the %d" % (server_addrs, limit_clients_num)) new_tuple = [b'', return_value[1]] return_value = tuple(new_tuple) return return_value def new_bind(orgin_method, self, *args, **kwds): - if only_port: - server_addrs = '*:%s' % args[0][1] - else: - server_addrs = '%s:%s' % (args[0][0], args[0][1]) - self._server_addrs = server_addrs - self._all_client_list.update({server_addrs:{}}) - new_self_method(self, 'recvfrom', new_recvfrom) + + # 如果是udp的绑定,则绑定地址一般是0。那这个socket就一定不是和客户端通信的。 + # 此处主要是考虑到shadowsocks服务端在访问非客户端的时候会对udp进行绑定。 + if args[0][1] != 0: + if only_port: + server_addrs = '*:%s' % args[0][1] + else: + server_addrs = '%s:%s' % (args[0][0], args[0][1]) + self._server_addrs = server_addrs + self._all_client_list.update({server_addrs:{}}) + new_self_method(self, 'recvfrom', new_recvfrom) orgin_method(self, *args, **kwds) + # 自定义 socket 类,可以自定义实例方法或属性。 @@ -195,6 +185,21 @@ def __init__(self, *args, **kwds): def close(self ,*args, **kwds): super(new_client_socket, self).__init__(*args, **kwds) + # 自定义 close 方法,让其在关闭的时候从列表中清理掉自身的 socket 或 ip。 + def new_close(self, *args, **kwds): + addr, port = self.getpeername() + server_addrs = self._server_addrs + client_list = self._all_client_list[server_addrs] + if client_list.get(addr, None) != None: + last_up_time = client_list[addr]["last_up_time"] + if client_list[addr]["client_num"] <= 1 and time.time() - last_up_time> recvfrom_timeout: + del client_list[addr] + logging.info("[socket] remove the client %s" % (addr)) + else: + client_list[addr]["client_num"] -= 1 + # logging.debug("[socket] close the client socket %s:%d" % (addr, port)) + self._all_client_list[server_addrs].update(client_list) + return super(new_client_socket, self).__init__(*args, **kwds) # 添加类属性, 此属性是全局的,所有socket对象都共享此属性。 @@ -203,5 +208,5 @@ def close(self ,*args, **kwds): new_class_method(socket.socket, 'bind', new_bind) new_class_method(socket.socket, 'accept', new_accept) -socket._socketobject = new_client_socket socket.socket = new_socket +socket._socketobject = new_client_socket \ No newline at end of file From ec89d5a635083259c30f69125a46d5d6136007af Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月26日 19:40:20 +0800 Subject: [PATCH 13/43] update set_close_timeout --- Limit_Clients/socket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index eb55516..8487326 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -48,7 +48,7 @@ #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -set_close_timeout = True # 是否清理指定时间内无数据收发的连接,仅对TCP有效,socket会自动关闭正常或异常断开的连接, 所以一般不用设置。 +set_close_timeout = False # 是否清理指定时间内无数据收发的连接,仅对TCP有效,socket会自动关闭正常或异常断开的连接, 所以一般不用设置。 # 如果为True则根据下面的超时时间进行清理,如果为 False 则根据socket的超时时间来清理(5分钟左右)。 recv_timeout = 1000 # 设置 tcp 清理连接的超时时间,单位毫秒(1000毫秒等于一分钟)。 From 18fbb051125e1aa7841b2dc10e436da1b9795d85 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月26日 22:03:19 +0800 Subject: [PATCH 14/43] fix close --- Limit_Clients/socket.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 8487326..d9a0528 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -51,7 +51,7 @@ set_close_timeout = False # 是否清理指定时间内无数据收发的连接,仅对TCP有效,socket会自动关闭正常或异常断开的连接, 所以一般不用设置。 # 如果为True则根据下面的超时时间进行清理,如果为 False 则根据socket的超时时间来清理(5分钟左右)。 -recv_timeout = 1000 # 设置 tcp 清理连接的超时时间,单位毫秒(1000毫秒等于一分钟)。 +recv_timeout = 4000 # 设置 tcp 清理连接的超时时间,单位毫秒(1000毫秒等于一分钟)。 # 在此时间内无连接或无数据 接收 的连接会被清理。只针对 tcp。 recvfrom_timeout = 60 # 设置 udp 清理连接的超时时间,单位秒。在此时间内无连接或无数据 接收 的连接会被清理。只针对 udp。 @@ -189,7 +189,7 @@ def close(self ,*args, **kwds): def new_close(self, *args, **kwds): addr, port = self.getpeername() server_addrs = self._server_addrs - client_list = self._all_client_list[server_addrs] + client_list = super(new_client_socket, self)._all_client_list[server_addrs] if client_list.get(addr, None) != None: last_up_time = client_list[addr]["last_up_time"] if client_list[addr]["client_num"] <= 1 and time.time() - last_up_time> recvfrom_timeout: @@ -198,7 +198,7 @@ def new_close(self, *args, **kwds): else: client_list[addr]["client_num"] -= 1 # logging.debug("[socket] close the client socket %s:%d" % (addr, port)) - self._all_client_list[server_addrs].update(client_list) + super(new_client_socket, self)._all_client_list[server_addrs].update(client_list) return super(new_client_socket, self).__init__(*args, **kwds) From 251dac4c18d41b77e055917f4a714bba99d4217f Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月27日 00:26:13 +0800 Subject: [PATCH 15/43] fix some error --- Limit_Clients/socket.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index d9a0528..8f4e57b 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -104,8 +104,6 @@ def new_accept(orgin_method, self, *args, **kwds): for k,v in self._all_client_list[server_addrs].copy().items(): last_up_time = v["last_up_time"] if time.time() - last_up_time> recvfrom_timeout and v["client_num"] < 1: - logging.info("[socket] remove the client %s" % (k)) - del client_list[k] if set_close_timeout: self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, recv_timeout) self._all_client_list[server_addrs].update(client_list) @@ -155,8 +153,8 @@ def new_recvfrom(orgin_method, self, *args, **kwds): def new_bind(orgin_method, self, *args, **kwds): - # 如果是udp的绑定,则绑定地址一般是0。那这个socket就一定不是和客户端通信的。 - # 此处主要是考虑到shadowsocks服务端在访问非客户端的时候会对udp进行绑定。 + # 如果绑定地址是0,那这个 socket 就一定不是和客户端通信的。 + # 此处主要是考虑到shadowsocks服务端在做流量转发的时候会对本地的socket进行绑定。 if args[0][1] != 0: if only_port: server_addrs = '*:%s' % args[0][1] From a5046d1037f0bc08a099c80b4cb41050e6e43e2d Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月27日 01:31:55 +0800 Subject: [PATCH 16/43] update dns forward for ss --- shadowsocks/dns_forward/socket.py | 146 ++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 shadowsocks/dns_forward/socket.py diff --git a/shadowsocks/dns_forward/socket.py b/shadowsocks/dns_forward/socket.py new file mode 100644 index 0000000..b5d23ee --- /dev/null +++ b/shadowsocks/dns_forward/socket.py @@ -0,0 +1,146 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2017 Falseen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from __future__ import absolute_import, division, print_function, \ + with_statement, nested_scopes + +import sys + +del sys.modules['socket'] + +import sys +import time +import logging +import types +import functools + +path = sys.path[0] +sys.path.pop(0) + +# import real socket +import socket +import struct +import binascii + +sys.path.insert(0, path) + + +PROXY_TYPE = "socks5" +PROXY_ADDR = "127.0.0.1" +PROXY_PORT = 1080 +SOCKS5_REQUEST_DATA = b"\x05\x01\x00" +BUF_SIZE = 32 * 1024 + + +def to_bytes(s): + if bytes != str: + if type(s) == str: + return s.encode('utf-8') + return s + + +def to_str(s): + if bytes != str: + if type(s) == bytes: + return s.decode('utf-8') + return s + + +def pack_addr(address): + address_str = to_str(address) + address = to_bytes(address) + for family in (socket.AF_INET, socket.AF_INET6): + try: + r = socket.inet_pton(family, address_str) + if family == socket.AF_INET6: + return b'\x04' + r + else: + return b'\x01' + r + except (TypeError, ValueError, OSError, IOError): + pass + if len(address)> 255: + address = address[:255] # TODO + return b'\x03' + chr(len(address)) + address + + + +# 动态patch类方法 +def new_class_method(_class, method_name, new_method): + method = getattr(_class, method_name) + info = sys.version_info + if info[0]>= 3: + setattr(_class, method_name, + types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), _class)) + else: + setattr(_class, method_name, + types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), None, _class)) + + +# 动态patch实例方法 +def new_self_method(self, method_name, new_method): + method = getattr(self, method_name) + setattr(self, method_name, types.MethodType(lambda *args, ** + kwds: new_method(method, *args, **kwds), self, self)) + + +def set_self_blocking(function): + + @functools.wraps(function) + def wrapper(*args, **kwargs): + self = args[0] + try: + _is_blocking = self.gettimeout() + # if not blocking then set blocking + if _is_blocking == 0: + self.setblocking(True) + return function(*args, **kwargs) + except Exception as e: + print(e) + raise + finally: + # set orgin blcoking + if _is_blocking == 0: + self.setblocking(False) + return wrapper + + +# make a new socket class +class new_socket(socket.socket): + + def __init__(self, *args, **kwds): + super(new_socket, self).__init__(*args, **kwds) + # new_self_method(self, 'sendto', new_sendto) + # new_self_method(self, 'recvfrom', new_recvfrom) + + def sendto(real_method, self, *args, **kwds): + data, dst_addrs = args + dst_addr, dst_port = dst_addrs + if dst_port == 53: + self._is_proxy = True + UDP_SOCKS5_HEADER = b"\x00\x00\x00" + new_dst_addr = "8.8.8.8" + new_dst_port = 53 + args = (data, (new_dst_addr, new_dst_port)) + return_value = super(new_socket, self).sendto(*args, **kwds) + return return_value + + + +# replace socket class to new_socket +socket._socketobject = new_socket +print(" replace socket class to new_socket") From 7bd555c1f7ed8fc2c62a92bfc4998195585eeb74 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月27日 02:07:12 +0800 Subject: [PATCH 17/43] add recvfrom --- shadowsocks/dns_forward/socket.py | 93 +++++++++---------------------- 1 file changed, 27 insertions(+), 66 deletions(-) diff --git a/shadowsocks/dns_forward/socket.py b/shadowsocks/dns_forward/socket.py index b5d23ee..7607eae 100644 --- a/shadowsocks/dns_forward/socket.py +++ b/shadowsocks/dns_forward/socket.py @@ -40,42 +40,18 @@ sys.path.insert(0, path) -PROXY_TYPE = "socks5" -PROXY_ADDR = "127.0.0.1" -PROXY_PORT = 1080 -SOCKS5_REQUEST_DATA = b"\x05\x01\x00" -BUF_SIZE = 32 * 1024 +#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -def to_bytes(s): - if bytes != str: - if type(s) == str: - return s.encode('utf-8') - return s +# 原始地址和端口 +orgin_addr = "114.114.114.114" +orgin_port = 53 +# 修改后的地址和端口 +new_dst_addr = "114.114.115.115" +new_dst_port = 53 -def to_str(s): - if bytes != str: - if type(s) == bytes: - return s.decode('utf-8') - return s - - -def pack_addr(address): - address_str = to_str(address) - address = to_bytes(address) - for family in (socket.AF_INET, socket.AF_INET6): - try: - r = socket.inet_pton(family, address_str) - if family == socket.AF_INET6: - return b'\x04' + r - else: - return b'\x01' + r - except (TypeError, ValueError, OSError, IOError): - pass - if len(address)> 255: - address = address[:255] # TODO - return b'\x03' + chr(len(address)) + address +#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -98,25 +74,24 @@ def new_self_method(self, method_name, new_method): kwds: new_method(method, *args, **kwds), self, self)) -def set_self_blocking(function): +def new_recvfrom(real_method, self, *args, **kwds): + data, src_addrs = real_method(*args, **kwds) + src_addr, src_port = src_addrs + if src_port == new_dst_port and src_addr == new_dst_addr: + # logging.info("fix %s:%d to %s:%d" % (src_addr, src_port, orgin_addr, orgin_port)) + return data, (orgin_addr, orgin_port) + return data, src_addrs + - @functools.wraps(function) - def wrapper(*args, **kwargs): - self = args[0] - try: - _is_blocking = self.gettimeout() - # if not blocking then set blocking - if _is_blocking == 0: - self.setblocking(True) - return function(*args, **kwargs) - except Exception as e: - print(e) - raise - finally: - # set orgin blcoking - if _is_blocking == 0: - self.setblocking(False) - return wrapper +def new_sendto(orgin_method ,self, *args, **kwds): + data, dst_addrs = args + dst_addr, dst_port = dst_addrs + if dst_port == orgin_port and dst_addr == orgin_addr : + # logging.info("forward %s:%d to %s:%d" % (dst_addr, dst_port, new_dst_addr, new_dst_port)) + new_self_method(self, 'recvfrom', new_recvfrom) + args = (data, (new_dst_addr, new_dst_port)) + return_value = orgin_method(*args, **kwds) + return return_value # make a new socket class @@ -124,23 +99,9 @@ class new_socket(socket.socket): def __init__(self, *args, **kwds): super(new_socket, self).__init__(*args, **kwds) - # new_self_method(self, 'sendto', new_sendto) - # new_self_method(self, 'recvfrom', new_recvfrom) - - def sendto(real_method, self, *args, **kwds): - data, dst_addrs = args - dst_addr, dst_port = dst_addrs - if dst_port == 53: - self._is_proxy = True - UDP_SOCKS5_HEADER = b"\x00\x00\x00" - new_dst_addr = "8.8.8.8" - new_dst_port = 53 - args = (data, (new_dst_addr, new_dst_port)) - return_value = super(new_socket, self).sendto(*args, **kwds) - return return_value + new_self_method(self, 'sendto', new_sendto) # replace socket class to new_socket -socket._socketobject = new_socket -print(" replace socket class to new_socket") +socket.socket = new_socket From 3fd1361cb4b2e8aa17a5b228974fb123fed67f9f Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月27日 03:31:07 +0800 Subject: [PATCH 18/43] fix a error for recvfrom and fix last_log_time --- Limit_Clients/socket.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 8f4e57b..d6fdc33 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -52,7 +52,7 @@ # 如果为True则根据下面的超时时间进行清理,如果为 False 则根据socket的超时时间来清理(5分钟左右)。 recv_timeout = 4000 # 设置 tcp 清理连接的超时时间,单位毫秒(1000毫秒等于一分钟)。 - # 在此时间内无连接或无数据 接收 的连接会被清理。只针对 tcp。 + # 在此时间内无连接或无数据 接收 的连接会被清理。只针对 tcp,从测试来看,似乎没什么作用。 recvfrom_timeout = 60 # 设置 udp 清理连接的超时时间,单位秒。在此时间内无连接或无数据 接收 的连接会被清理。只针对 udp。 limit_clients_num = 1 # 设置每个端口的允许通过的ip数量,即客户端的ip数量 @@ -106,13 +106,15 @@ def new_accept(orgin_method, self, *args, **kwds): if time.time() - last_up_time> recvfrom_timeout and v["client_num"] < 1: if set_close_timeout: self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, recv_timeout) + logging.info("[socket] remove the client %s" % (k)) + del client_list[k] self._all_client_list[server_addrs].update(client_list) self_socket._server_addrs = self._server_addrs self_socket.close = self_socket.new_close return return_value - if time.time() - self.last_log_time> 10: - self.last_log_time = time.time() + if time.time() - self.last_log_time[0]> 10: logging.error("[socket] the server_addrs %s client more then the %d" % (server_addrs, limit_clients_num)) + self.last_log_time[0] = time.time() self_socket.close() @@ -139,13 +141,13 @@ def new_recvfrom(orgin_method, self, *args, **kwds): logging.info("[socket] remove the client %s" % (k)) del client_list[k] # logging.debug("[socket] add %s:%d" %(client_ip, client_port)) - client_list[client_ip]["last_up_time"] = time.time() + client_list.update({client_ip : {"client_num":0, "last_up_time":time.time()}}) self._all_client_list[server_addrs].update(client_list) return return_value - if time.time() - self.last_log_time> 10: - self.last_log_time = time.time() + if time.time() - self.last_log_time[0]> 10: logging.error("[socket] the server_addrs %s client more then the %d" % (server_addrs, limit_clients_num)) + self.last_log_time[0] = time.time() new_tuple = [b'', return_value[1]] return_value = tuple(new_tuple) return return_value @@ -187,7 +189,7 @@ def close(self ,*args, **kwds): def new_close(self, *args, **kwds): addr, port = self.getpeername() server_addrs = self._server_addrs - client_list = super(new_client_socket, self)._all_client_list[server_addrs] + client_list = self._all_client_list[server_addrs] if client_list.get(addr, None) != None: last_up_time = client_list[addr]["last_up_time"] if client_list[addr]["client_num"] <= 1 and time.time() - last_up_time> recvfrom_timeout: @@ -196,13 +198,13 @@ def new_close(self, *args, **kwds): else: client_list[addr]["client_num"] -= 1 # logging.debug("[socket] close the client socket %s:%d" % (addr, port)) - super(new_client_socket, self)._all_client_list[server_addrs].update(client_list) - return super(new_client_socket, self).__init__(*args, **kwds) + self._all_client_list[server_addrs].update(client_list) + return super(new_client_socket, self).close(*args, **kwds) # 添加类属性, 此属性是全局的,所有socket对象都共享此属性。 setattr(socket.socket, '_all_client_list', {}) -setattr(socket.socket, 'last_log_time', 0) +setattr(socket.socket, 'last_log_time', [0]) new_class_method(socket.socket, 'bind', new_bind) new_class_method(socket.socket, 'accept', new_accept) From 3b3af0be903e3e98ca107f0a5fb0ec1bd03c2865 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月27日 03:33:54 +0800 Subject: [PATCH 19/43] Update socket.py --- Limit_Clients/socket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index d6fdc33..f8fef07 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -18,7 +18,7 @@ # # 功能:限制客户端数量(基于ip判断) # -# 使用说明:1.将此文件放在程序根目录中即可生效,不用做其他设置(需要重新运行ss)。 +# 使用说明:1.将此文件放在程序根目录中即可生效,不用做其他设置(需要重新运行程序)。 # 2.修改 recv_timeout 和 limit_clients_num 为你想要的数值。 # 3.如果你的服务器有ipv6或有多个ip,并且你想让这些ip分开限制,则可以设置 only_port 为 False 。 # @@ -209,4 +209,4 @@ def new_close(self, *args, **kwds): new_class_method(socket.socket, 'bind', new_bind) new_class_method(socket.socket, 'accept', new_accept) socket.socket = new_socket -socket._socketobject = new_client_socket \ No newline at end of file +socket._socketobject = new_client_socket From e8ea0d8988996edc12bd775ada4b72efd26ed627 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月27日 14:22:35 +0800 Subject: [PATCH 20/43] fix tcp accept --- Limit_Clients/socket.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index f8fef07..fb2d68b 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -108,6 +108,9 @@ def new_accept(orgin_method, self, *args, **kwds): self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, recv_timeout) logging.info("[socket] remove the client %s" % (k)) del client_list[k] + if client_list.get(client_ip, None) == None: + client_list.update({client_ip : {"client_num":0, "last_up_time":0}}) + client_list[client_ip]["client_num"] += 1 self._all_client_list[server_addrs].update(client_list) self_socket._server_addrs = self._server_addrs self_socket.close = self_socket.new_close From 9f3d19c790c3672f45eccf0ab5f3485283a56c60 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月27日 21:48:19 +0800 Subject: [PATCH 21/43] update readme for shadowsocks --- shadowsocks/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/shadowsocks/README.md b/shadowsocks/README.md index bb244bd..6def71d 100644 --- a/shadowsocks/README.md +++ b/shadowsocks/README.md @@ -2,14 +2,13 @@ ## 功能: -通过猴子补丁的方式给 socket 动态地添加一些增强功能,比如限制客户端数量、前置代理什么的。 -**使用的时候只要把对应文件夹中的 socket.py 文件放到程序的目录即可生效,不用修改任何源码。** 注意:这是python代码,因此只支持python版的shadowsocks。 -* **shadowosocks**: 给服务端添加前置代理(原则上也适用于客户端),支持 http、socks4、socks5 代理。并且通过hook的方式去掉了ss的dns查询,ss在接收到数据之后会直接把域名和请求一起发给代理。 +给服务端添加前置代理(原则上也适用于客户端),支持 http、socks4、socks5 代理。并且通过hook的方式去掉了ss的dns查询,ss在接收到数据之后会直接把域名和请求一起发给代理。 + +**使用的时候修改 socket.py 文件中 PROXY_TYPE、PROXY_ADDR、PROXY_PORT 等字段为你的代理地址,然后放到 shadowsocks 根目录即可生效。不用修改任何源码。** -使用的时候修改 socket.py 文件中 PROXY_TYPE、PROXY_ADDR、PROXY_PORT 等字段为你的代理地址,然后放到 shadowsocks 根目录即可生效。 如果不想 hook shadowsocks的代码的话,把文件中末尾的代码删除即可,如下: ```python @@ -26,5 +25,8 @@ shadowsocks.asyncdns.DNSResolver.resolve = new_resolve ``` + +* **dns_forward 文件夹** : 简单的端口转发,可以把服务端访问(从客户端接收到的请求)某个ip的流量强制转到另外一个ip。比如当手机端开启udp转发的时候,可以把手机端访问 8.8.8.8:53的流量转移到 114.114.114.114:53,或是内网的dns。这个功能主要是为那些服务端搭建在国内、把ss当vpn使用的人准备的。 + ## TODO 增加类似acl的功能,过滤一些本地私有地址或其他地址。 From 536c7bb5b9730cc7a2d8ae7fcd9d497f1637ee5a Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年2月28日 19:37:14 +0800 Subject: [PATCH 22/43] try fix some error for python3 --- Limit_Clients/socket.py | 11 ++++++++--- shadowsocks/dns_forward/socket.py | 7 +++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index fb2d68b..eb8ea0f 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -77,7 +77,11 @@ def new_class_method(_class, method_name, new_method): # 动态patch实例方法 def new_self_method(self, method_name, new_method): method = getattr(self, method_name) - setattr(self, method_name, types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), self, self)) + info = sys.version_info + if info[0]>= 3: + setattr(self, method_name, types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), self)) + else: + setattr(self, method_name, types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), self, self)) # 处理Tcp连接 @@ -211,5 +215,6 @@ def new_close(self, *args, **kwds): new_class_method(socket.socket, 'bind', new_bind) new_class_method(socket.socket, 'accept', new_accept) -socket.socket = new_socket -socket._socketobject = new_client_socket +if not sys.version_info[0]>= 3: + socket._socketobject = new_client_socket +socket.socket = new_socket \ No newline at end of file diff --git a/shadowsocks/dns_forward/socket.py b/shadowsocks/dns_forward/socket.py index 7607eae..7399619 100644 --- a/shadowsocks/dns_forward/socket.py +++ b/shadowsocks/dns_forward/socket.py @@ -70,8 +70,11 @@ def new_class_method(_class, method_name, new_method): # 动态patch实例方法 def new_self_method(self, method_name, new_method): method = getattr(self, method_name) - setattr(self, method_name, types.MethodType(lambda *args, ** - kwds: new_method(method, *args, **kwds), self, self)) + info = sys.version_info + if info[0]>= 3: + setattr(self, method_name, types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), self)) + else: + setattr(self, method_name, types.MethodType(lambda *args, **kwds: new_method(method, *args, **kwds), self, self)) def new_recvfrom(real_method, self, *args, **kwds): From d6689906a82695f88caffe7dfc882cb4c24c6d9c Mon Sep 17 00:00:00 2001 From: Falseen Date: Wed, 1 Mar 2017 19:36:10 +0800 Subject: [PATCH 23/43] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eaee270..8319fd1 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ PySocket ,一个通过猴子补丁(monkey patch)动态修改 socket 的项 * **proxy**:基本跟shadowsocks一样,只是去掉了hook的代码。 -* **Limit_Clients**:限制客户端数量(基于ip),支持tcp和udp,修改了close方法,在关闭连接的时候清理client list,基本上完美了。 +* **Limit_Clients**:限制客户端数量(基于ip),支持tcp和udp,修改了close方法,在关闭连接的时候清理client list,基本上完美了。(目前python3下还有一点小问题,暂时还没想好怎么去解决) * **orgin_socket**:原始的socket,放在这里只是为了方便查看代码。毕竟 _socket.pyd 是加密的。 From 7febb5074771bb493b52db6f73cb654b84fa9326 Mon Sep 17 00:00:00 2001 From: Falseen Date: Sun, 5 Mar 2017 19:30:07 +0800 Subject: [PATCH 24/43] update README.md --- shadowsocks/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/README.md b/shadowsocks/README.md index 6def71d..b168a5b 100644 --- a/shadowsocks/README.md +++ b/shadowsocks/README.md @@ -26,7 +26,7 @@ shadowsocks.asyncdns.DNSResolver.resolve = new_resolve ``` -* **dns_forward 文件夹** : 简单的端口转发,可以把服务端访问(从客户端接收到的请求)某个ip的流量强制转到另外一个ip。比如当手机端开启udp转发的时候,可以把手机端访问 8.8.8.8:53的流量转移到 114.114.114.114:53,或是内网的dns。这个功能主要是为那些服务端搭建在国内、把ss当vpn使用的人准备的。 +* **dns_forward 文件夹** : 简单的udp端口转发,可以把服务端访问(从客户端接收到的请求)某个ip的流量强制转到另外一个ip。比如当手机端开启udp转发的时候,可以把手机端访问 8.8.8.8:53的流量转移到 114.114.114.114:53,或是内网的dns。 ## TODO 增加类似acl的功能,过滤一些本地私有地址或其他地址。 From cbab69389ec537ebaebc8bdf367e107f4bddceb5 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年3月19日 12:20:45 +0800 Subject: [PATCH 25/43] update logging.debug --- Limit_Clients/socket.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index eb8ea0f..676bf5e 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -96,7 +96,7 @@ def new_accept(orgin_method, self, *args, **kwds): if len(client_list) < limit_clients_num or client_ip in client_list: self_socket._server_addrs = self._server_addrs self_socket.close = self_socket.new_close - # logging.debug("[socket] add %s:%d" %(client_ip, client_port)) + logging.debug("[socket] add %s:%d" %(client_ip, client_port)) if client_list.get(client_ip, None) == None: client_list.update({client_ip : {"client_num":0, "last_up_time":0}}) client_list[client_ip]["client_num"] += 1 @@ -120,7 +120,7 @@ def new_accept(orgin_method, self, *args, **kwds): self_socket.close = self_socket.new_close return return_value if time.time() - self.last_log_time[0]> 10: - logging.error("[socket] the server_addrs %s client more then the %d" % (server_addrs, limit_clients_num)) + logging.error("[socket] the server_addrs %s client more than the %d" % (server_addrs, limit_clients_num)) self.last_log_time[0] = time.time() self_socket.close() @@ -139,7 +139,7 @@ def new_recvfrom(orgin_method, self, *args, **kwds): client_list.update({client_ip : {"client_num":0, "last_up_time":0}}) client_list[client_ip]["last_up_time"] = time.time() self._all_client_list[server_addrs].update(client_list) - # logging.debug("[socket] update last_up_time for %s" % client_ip) + logging.debug("[socket] update last_up_time for %s" % client_ip) return return_value else: for k,v in self._all_client_list[server_addrs].copy().items(): @@ -147,13 +147,13 @@ def new_recvfrom(orgin_method, self, *args, **kwds): if time.time() - last_up_time> recvfrom_timeout and v["client_num"] < 1: logging.info("[socket] remove the client %s" % (k)) del client_list[k] - # logging.debug("[socket] add %s:%d" %(client_ip, client_port)) + logging.debug("[socket] add %s:%d" %(client_ip, client_port)) client_list.update({client_ip : {"client_num":0, "last_up_time":time.time()}}) self._all_client_list[server_addrs].update(client_list) return return_value if time.time() - self.last_log_time[0]> 10: - logging.error("[socket] the server_addrs %s client more then the %d" % (server_addrs, limit_clients_num)) + logging.error("[socket] the server_addrs %s client more than the %d" % (server_addrs, limit_clients_num)) self.last_log_time[0] = time.time() new_tuple = [b'', return_value[1]] return_value = tuple(new_tuple) @@ -204,7 +204,7 @@ def new_close(self, *args, **kwds): logging.info("[socket] remove the client %s" % (addr)) else: client_list[addr]["client_num"] -= 1 - # logging.debug("[socket] close the client socket %s:%d" % (addr, port)) + logging.debug("[socket] close the client socket %s:%d" % (addr, port)) self._all_client_list[server_addrs].update(client_list) return super(new_client_socket, self).close(*args, **kwds) From b90407945d976d466426a445e317dde728c0628a Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年3月19日 14:37:19 +0800 Subject: [PATCH 26/43] try fix some error for limit_clients --- Limit_Clients/socket.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 676bf5e..3803e1f 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -93,7 +93,9 @@ def new_accept(orgin_method, self, *args, **kwds): client_ip, client_port = return_value[1] server_addrs = self._server_addrs client_list = self._all_client_list.get(server_addrs, {}) - if len(client_list) < limit_clients_num or client_ip in client_list: + if client_ip in client_list: + return return_value + elif len(client_list) < limit_clients_num: self_socket._server_addrs = self._server_addrs self_socket.close = self_socket.new_close logging.debug("[socket] add %s:%d" %(client_ip, client_port)) From df873e65befc298b989d76ec6ca96439b9fe7702 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年3月19日 22:56:23 +0800 Subject: [PATCH 27/43] Revert "try fix some error for limit_clients" This reverts commit b90407945d976d466426a445e317dde728c0628a. --- Limit_Clients/socket.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 3803e1f..676bf5e 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -93,9 +93,7 @@ def new_accept(orgin_method, self, *args, **kwds): client_ip, client_port = return_value[1] server_addrs = self._server_addrs client_list = self._all_client_list.get(server_addrs, {}) - if client_ip in client_list: - return return_value - elif len(client_list) < limit_clients_num: + if len(client_list) < limit_clients_num or client_ip in client_list: self_socket._server_addrs = self._server_addrs self_socket.close = self_socket.new_close logging.debug("[socket] add %s:%d" %(client_ip, client_port)) From ee87807e1d154dd89dac09d0fd47a3b8726855a6 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年3月19日 23:12:33 +0800 Subject: [PATCH 28/43] update socket log --- Limit_Clients/socket.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 676bf5e..5ea8f04 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -96,7 +96,7 @@ def new_accept(orgin_method, self, *args, **kwds): if len(client_list) < limit_clients_num or client_ip in client_list: self_socket._server_addrs = self._server_addrs self_socket.close = self_socket.new_close - logging.debug("[socket] add %s:%d" %(client_ip, client_port)) + logging.debug("[socket] add client %s:%d" %(client_ip, client_port)) if client_list.get(client_ip, None) == None: client_list.update({client_ip : {"client_num":0, "last_up_time":0}}) client_list[client_ip]["client_num"] += 1 @@ -147,13 +147,13 @@ def new_recvfrom(orgin_method, self, *args, **kwds): if time.time() - last_up_time> recvfrom_timeout and v["client_num"] < 1: logging.info("[socket] remove the client %s" % (k)) del client_list[k] - logging.debug("[socket] add %s:%d" %(client_ip, client_port)) + logging.debug("[socket] add client %s:%d" %(client_ip, client_port)) client_list.update({client_ip : {"client_num":0, "last_up_time":time.time()}}) self._all_client_list[server_addrs].update(client_list) return return_value if time.time() - self.last_log_time[0]> 10: - logging.error("[socket] the server_addrs %s client more than the %d" % (server_addrs, limit_clients_num)) + logging.error("[socket] the server_addrs %s client more than %d" % (server_addrs, limit_clients_num)) self.last_log_time[0] = time.time() new_tuple = [b'', return_value[1]] return_value = tuple(new_tuple) From 7d7c2672c89e77e3ed300a1b01aa16b727f2fc9d Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年3月20日 02:23:51 +0800 Subject: [PATCH 29/43] add white_list and black_list for limit_clients and fix some error as recv_timeout https://github.com/falseen/PySocket/issues/4#issuecomment-287619220 https://github.com/falseen/PySocket/issues/5 --- Limit_Clients/socket.py | 111 +++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 5ea8f04..cfe23f9 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -36,6 +36,7 @@ import time import logging import types +import struct path = sys.path[0] sys.path.pop(0) @@ -49,18 +50,30 @@ #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ set_close_timeout = False # 是否清理指定时间内无数据收发的连接,仅对TCP有效,socket会自动关闭正常或异常断开的连接, 所以一般不用设置。 - # 如果为True则根据下面的超时时间进行清理,如果为 False 则根据socket的超时时间来清理(5分钟左右)。 + # 如果为True则根据下面的 recv_timeout 进行清理,如果为 False 则根据socket的默认超时时间来清理(5分钟左右)。 -recv_timeout = 4000 # 设置 tcp 清理连接的超时时间,单位毫秒(1000毫秒等于一分钟)。 - # 在此时间内无连接或无数据 接收 的连接会被清理。只针对 tcp,从测试来看,似乎没什么作用。 +recv_timeout = 120 # 配合上面的选项设置 tcp 清理连接的超时时间,单位秒。在此时间内无连接或无数据 收发 的连接会被清理。 + # 只针对 tcp。建议不要设置为0。一般客户端在关闭的时候会主动关闭连接,此选项主要是应对客户端的非正常关闭。 + # 比如说突然断网之类的。 -recvfrom_timeout = 60 # 设置 udp 清理连接的超时时间,单位秒。在此时间内无连接或无数据 接收 的连接会被清理。只针对 udp。 +recvfrom_timeout = 30 # 设置 udp 清理连接的超时时间,单位秒。在此时间内无连接或无数据 接收 的连接会被清理。只针对 udp。 limit_clients_num = 1 # 设置每个端口的允许通过的ip数量,即客户端的ip数量 -only_port = True # 设置是否只根据端口判断。如果为 True ,则只根据端口判断。如果为 False ,则会严格的根据服务端ip+端口进行判断 +only_port = True # 设置是否只根据端口判断。如果为 True ,则只根据端口判断。如果为 False ,则会严格的根据服务端ip+端口进行判断。 + # 此功能主要适用于服务端有多个ip的情况,比如同时拥有ipv4和ipv6的ip。 + +# 白名单,在此名单内的端口不受限制,可以留空。格式 white_list = [80, 443] 端口间用半角逗号分隔,注意一定要是数字,不能加引号。 +white_list = [] + +# 黑名单,在此名单内的端口会受到限制。配置方式同上,可以留空。如果白名单和黑名单都留空,则默认限制所有端口。 +black_list = [] #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +send_timeout = recv_timeout +limit_all_clients = False +if not black_list and not white_list: + limit_all_clients = True # 动态patch类方法 def new_class_method(_class, method_name, new_method): @@ -88,7 +101,7 @@ def new_self_method(self, method_name, new_method): def new_accept(orgin_method, self, *args, **kwds): while True: - return_value = orgin_method(self, *args, **kwds) + return_value = orgin_method(*args, **kwds) self_socket = return_value[0] client_ip, client_port = return_value[1] server_addrs = self._server_addrs @@ -102,14 +115,17 @@ def new_accept(orgin_method, self, *args, **kwds): client_list[client_ip]["client_num"] += 1 self._all_client_list[server_addrs].update(client_list) if set_close_timeout: - self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, recv_timeout) + # set recv_timeout and send_timeout , struct.pack("II", some_num_secs, some_num_microsecs) + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, struct.pack("II", recv_timeout, 0)) + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, struct.pack("II", send_timeout, 0)) return return_value else: for k,v in self._all_client_list[server_addrs].copy().items(): last_up_time = v["last_up_time"] if time.time() - last_up_time> recvfrom_timeout and v["client_num"] < 1: if set_close_timeout: - self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, recv_timeout) + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, struct.pack("II", recv_timeout, 0)) + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, struct.pack("II", send_timeout, 0)) logging.info("[socket] remove the client %s" % (k)) del client_list[k] if client_list.get(client_ip, None) == None: @@ -120,58 +136,60 @@ def new_accept(orgin_method, self, *args, **kwds): self_socket.close = self_socket.new_close return return_value if time.time() - self.last_log_time[0]> 10: - logging.error("[socket] the server_addrs %s client more than the %d" % (server_addrs, limit_clients_num)) + logging.error("[socket] the server_addrs %s client more than %d" % (server_addrs, limit_clients_num)) self.last_log_time[0] = time.time() self_socket.close() # 处理Udp连接 def new_recvfrom(orgin_method, self, *args, **kwds): - - while True: - return_value = orgin_method(*args, **kwds) - server_addrs = self._server_addrs - client_ip, client_port = return_value[1] - client_list = self._all_client_list.get(server_addrs, {}) - - if len(client_list) < limit_clients_num or client_ip in client_list: - if client_list.get(client_ip, None) == None: - client_list.update({client_ip : {"client_num":0, "last_up_time":0}}) - client_list[client_ip]["last_up_time"] = time.time() - self._all_client_list[server_addrs].update(client_list) - logging.debug("[socket] update last_up_time for %s" % client_ip) - return return_value - else: - for k,v in self._all_client_list[server_addrs].copy().items(): - last_up_time = v["last_up_time"] - if time.time() - last_up_time> recvfrom_timeout and v["client_num"] < 1: - logging.info("[socket] remove the client %s" % (k)) - del client_list[k] - logging.debug("[socket] add client %s:%d" %(client_ip, client_port)) - client_list.update({client_ip : {"client_num":0, "last_up_time":time.time()}}) - self._all_client_list[server_addrs].update(client_list) - return return_value - - if time.time() - self.last_log_time[0]> 10: - logging.error("[socket] the server_addrs %s client more than %d" % (server_addrs, limit_clients_num)) - self.last_log_time[0] = time.time() - new_tuple = [b'', return_value[1]] - return_value = tuple(new_tuple) + return_value = orgin_method(*args, **kwds) + server_addrs = self._server_addrs + client_ip, client_port = return_value[1] + client_list = self._all_client_list.get(server_addrs, {}) + + if len(client_list) < limit_clients_num or client_ip in client_list: + if client_list.get(client_ip, None) == None: + client_list.update({client_ip : {"client_num":0, "last_up_time":0}}) + client_list[client_ip]["last_up_time"] = time.time() + self._all_client_list[server_addrs].update(client_list) + logging.debug("[socket] update last_up_time for %s" % client_ip) return return_value + else: + for k,v in self._all_client_list[server_addrs].copy().items(): + last_up_time = v["last_up_time"] + if time.time() - last_up_time> recvfrom_timeout and v["client_num"] < 1: + logging.info("[socket] remove the client %s" % (k)) + del client_list[k] + logging.debug("[socket] add client %s:%d" %(client_ip, client_port)) + client_list.update({client_ip : {"client_num":0, "last_up_time":time.time()}}) + self._all_client_list[server_addrs].update(client_list) + return return_value + + if time.time() - self.last_log_time[0]> 10: + logging.error("[socket] the server_addrs %s client more than %d" % (server_addrs, limit_clients_num)) + self.last_log_time[0] = time.time() + new_tuple = [b'', return_value[1]] + return_value = tuple(new_tuple) + return return_value def new_bind(orgin_method, self, *args, **kwds): + server_addres, server_port = args[0] # 如果绑定地址是0,那这个 socket 就一定不是和客户端通信的。 # 此处主要是考虑到shadowsocks服务端在做流量转发的时候会对本地的socket进行绑定。 if args[0][1] != 0: - if only_port: - server_addrs = '*:%s' % args[0][1] - else: - server_addrs = '%s:%s' % (args[0][0], args[0][1]) - self._server_addrs = server_addrs - self._all_client_list.update({server_addrs:{}}) - new_self_method(self, 'recvfrom', new_recvfrom) + if server_port in black_list or server_port not in white_list or limit_all_clients: + if only_port: + server_addrs = '*:%s' % server_port + else: + server_addrs = '%s:%s' % (server_addres, server_port) + self._server_addrs = server_addrs + self._all_client_list.update({server_addrs:{}}) + logging.debug("[socket] bind the new new_accept new_recvfrom") + new_self_method(self, 'accept', new_accept) + new_self_method(self, 'recvfrom', new_recvfrom) orgin_method(self, *args, **kwds) @@ -214,7 +232,6 @@ def new_close(self, *args, **kwds): setattr(socket.socket, 'last_log_time', [0]) new_class_method(socket.socket, 'bind', new_bind) -new_class_method(socket.socket, 'accept', new_accept) if not sys.version_info[0]>= 3: socket._socketobject = new_client_socket socket.socket = new_socket \ No newline at end of file From 9e16934c34db846c16dd50af237aa507e9bc8ac7 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年3月20日 02:23:51 +0800 Subject: [PATCH 30/43] update note for recv_timeout --- Limit_Clients/socket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index cfe23f9..b41190c 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -53,8 +53,8 @@ # 如果为True则根据下面的 recv_timeout 进行清理,如果为 False 则根据socket的默认超时时间来清理(5分钟左右)。 recv_timeout = 120 # 配合上面的选项设置 tcp 清理连接的超时时间,单位秒。在此时间内无连接或无数据 收发 的连接会被清理。 - # 只针对 tcp。建议不要设置为0。一般客户端在关闭的时候会主动关闭连接,此选项主要是应对客户端的非正常关闭。 - # 比如说突然断网之类的。 + # 只针对 tcp。一般客户端在关闭的时候会主动关闭连接,此选项主要是应对客户端的非正常关闭。 + # 比如说突然断网之类的。建议不要设置为0或120以下,因为 tcp keepalive的默认时间一般是120秒。 recvfrom_timeout = 30 # 设置 udp 清理连接的超时时间,单位秒。在此时间内无连接或无数据 接收 的连接会被清理。只针对 udp。 limit_clients_num = 1 # 设置每个端口的允许通过的ip数量,即客户端的ip数量 From 67af776f723df2af51bfd6d427da3949a63ccfce Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年3月20日 02:23:51 +0800 Subject: [PATCH 31/43] try fix some error for limit_clients udp --- Limit_Clients/socket.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index b41190c..3683244 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -189,7 +189,8 @@ def new_bind(orgin_method, self, *args, **kwds): self._all_client_list.update({server_addrs:{}}) logging.debug("[socket] bind the new new_accept new_recvfrom") new_self_method(self, 'accept', new_accept) - new_self_method(self, 'recvfrom', new_recvfrom) + if self.type == socket.SOCK_DGRAM: + new_self_method(self, 'recvfrom', new_recvfrom) orgin_method(self, *args, **kwds) From 31b9cc5fd65ec8a72f4c899b7ac21a32f70bb0c2 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年3月20日 14:40:28 +0800 Subject: [PATCH 32/43] add diy port limit --- Limit_Clients/socket.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 3683244..f961625 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -19,7 +19,7 @@ # 功能:限制客户端数量(基于ip判断) # # 使用说明:1.将此文件放在程序根目录中即可生效,不用做其他设置(需要重新运行程序)。 -# 2.修改 recv_timeout 和 limit_clients_num 为你想要的数值。 +# 2.修改 recv_timeout 和 Limit_Clients_Num 为你想要的数值。 # 3.如果你的服务器有ipv6或有多个ip,并且你想让这些ip分开限制,则可以设置 only_port 为 False 。 # # @@ -57,15 +57,18 @@ # 比如说突然断网之类的。建议不要设置为0或120以下,因为 tcp keepalive的默认时间一般是120秒。 recvfrom_timeout = 30 # 设置 udp 清理连接的超时时间,单位秒。在此时间内无连接或无数据 接收 的连接会被清理。只针对 udp。 -limit_clients_num = 1 # 设置每个端口的允许通过的ip数量,即客户端的ip数量 + +Limit_Clients_Num = 1 # 设置每个端口的默认允许通过的ip数量,即客户端的ip数量。 + # 此为默认值,如果黑名单中没有定义,则按此选项的值来设置。 + only_port = True # 设置是否只根据端口判断。如果为 True ,则只根据端口判断。如果为 False ,则会严格的根据服务端ip+端口进行判断。 # 此功能主要适用于服务端有多个ip的情况,比如同时拥有ipv4和ipv6的ip。 # 白名单,在此名单内的端口不受限制,可以留空。格式 white_list = [80, 443] 端口间用半角逗号分隔,注意一定要是数字,不能加引号。 white_list = [] -# 黑名单,在此名单内的端口会受到限制。配置方式同上,可以留空。如果白名单和黑名单都留空,则默认限制所有端口。 -black_list = [] +# 黑名单,在此名单内的端口会受到限制, 可以留空。格式 端口:ip数量, black_list = {80:1} 如果白名单和黑名单都留空,则默认限制所有端口。 +black_list = {} #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -106,7 +109,7 @@ def new_accept(orgin_method, self, *args, **kwds): client_ip, client_port = return_value[1] server_addrs = self._server_addrs client_list = self._all_client_list.get(server_addrs, {}) - if len(client_list) < limit_clients_num or client_ip in client_list: + if len(client_list) < self._limit_clients_num or client_ip in client_list: self_socket._server_addrs = self._server_addrs self_socket.close = self_socket.new_close logging.debug("[socket] add client %s:%d" %(client_ip, client_port)) @@ -136,7 +139,7 @@ def new_accept(orgin_method, self, *args, **kwds): self_socket.close = self_socket.new_close return return_value if time.time() - self.last_log_time[0]> 10: - logging.error("[socket] the server_addrs %s client more than %d" % (server_addrs, limit_clients_num)) + logging.error("[socket] the server_addrs %s client more than %d" % (server_addrs, self._limit_clients_num)) self.last_log_time[0] = time.time() self_socket.close() @@ -148,7 +151,7 @@ def new_recvfrom(orgin_method, self, *args, **kwds): client_ip, client_port = return_value[1] client_list = self._all_client_list.get(server_addrs, {}) - if len(client_list) < limit_clients_num or client_ip in client_list: + if len(client_list) < self._limit_clients_num or client_ip in client_list: if client_list.get(client_ip, None) == None: client_list.update({client_ip : {"client_num":0, "last_up_time":0}}) client_list[client_ip]["last_up_time"] = time.time() @@ -167,7 +170,7 @@ def new_recvfrom(orgin_method, self, *args, **kwds): return return_value if time.time() - self.last_log_time[0]> 10: - logging.error("[socket] the server_addrs %s client more than %d" % (server_addrs, limit_clients_num)) + logging.error("[socket] the server_addrs %s client more than %d" % (server_addrs, self._limit_clients_num)) self.last_log_time[0] = time.time() new_tuple = [b'', return_value[1]] return_value = tuple(new_tuple) @@ -187,6 +190,7 @@ def new_bind(orgin_method, self, *args, **kwds): server_addrs = '%s:%s' % (server_addres, server_port) self._server_addrs = server_addrs self._all_client_list.update({server_addrs:{}}) + self._limit_clients_num = black_list.get(server_port, Limit_Clients_Num) logging.debug("[socket] bind the new new_accept new_recvfrom") new_self_method(self, 'accept', new_accept) if self.type == socket.SOCK_DGRAM: From 609c5260c78937ee94488ebc0b22ea5f698e6dad Mon Sep 17 00:00:00 2001 From: Falseen Date: 2017年3月20日 14:47:16 +0800 Subject: [PATCH 33/43] update diy port limit --- Limit_Clients/socket.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index f961625..f6d6b81 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -60,14 +60,15 @@ Limit_Clients_Num = 1 # 设置每个端口的默认允许通过的ip数量,即客户端的ip数量。 # 此为默认值,如果黑名单中没有定义,则按此选项的值来设置。 - + only_port = True # 设置是否只根据端口判断。如果为 True ,则只根据端口判断。如果为 False ,则会严格的根据服务端ip+端口进行判断。 # 此功能主要适用于服务端有多个ip的情况,比如同时拥有ipv4和ipv6的ip。 # 白名单,在此名单内的端口不受限制,可以留空。格式 white_list = [80, 443] 端口间用半角逗号分隔,注意一定要是数字,不能加引号。 white_list = [] -# 黑名单,在此名单内的端口会受到限制, 可以留空。格式 端口:ip数量, black_list = {80:1} 如果白名单和黑名单都留空,则默认限制所有端口。 +# 黑名单,在此名单内的端口会受到限制, 可以留空。 +# 格式 端口:ip数量, black_list = {80:1, 443:2} 如果白名单和黑名单都留空,则默认限制所有端口。 black_list = {} #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ From 23e295cf1b36600252c00f89cd2010483f60fec1 Mon Sep 17 00:00:00 2001 From: Falseen Date: Fri, 7 Apr 2017 18:49:07 +0800 Subject: [PATCH 34/43] update set_close_timeout --- Limit_Clients/socket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index f6d6b81..7ded230 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -49,12 +49,12 @@ #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -set_close_timeout = False # 是否清理指定时间内无数据收发的连接,仅对TCP有效,socket会自动关闭正常或异常断开的连接, 所以一般不用设置。 +set_close_timeout = True # 是否清理指定时间内无数据收发的连接,仅对TCP有效,socket会自动关闭正常或异常断开的连接, 所以一般不用设置。 # 如果为True则根据下面的 recv_timeout 进行清理,如果为 False 则根据socket的默认超时时间来清理(5分钟左右)。 recv_timeout = 120 # 配合上面的选项设置 tcp 清理连接的超时时间,单位秒。在此时间内无连接或无数据 收发 的连接会被清理。 # 只针对 tcp。一般客户端在关闭的时候会主动关闭连接,此选项主要是应对客户端的非正常关闭。 - # 比如说突然断网之类的。建议不要设置为0或120以下,因为 tcp keepalive的默认时间一般是120秒。 + # 比如说突然断网之类的。建议不要设置为0或120以下,因为 tcp keepalive 的默认时间一般是120秒。 recvfrom_timeout = 30 # 设置 udp 清理连接的超时时间,单位秒。在此时间内无连接或无数据 接收 的连接会被清理。只针对 udp。 From 05b5d6015f964076fd748842cbb7639e59b7a11e Mon Sep 17 00:00:00 2001 From: Falseen Date: Fri, 7 Apr 2017 18:58:49 +0800 Subject: [PATCH 35/43] update todo --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 8319fd1..b7a6785 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,7 @@ PySocket ,一个通过猴子补丁(monkey patch)动态修改 socket 的项 默认情况下,python程序在运行的时候会导入系统中的 socket 文件,但是把这个socket文件放到程序的目录之后, 它就会引用这个我们自定义的socket文件。 然后我们再在这个文件中再导入真正的socket包,并在原 socket 的基础上加以修改,最终程序调用的就是经过我们修改的socket文件了。 **最后我想说的是,python 真的是世界上最好的语言,太自由了!!!** + +## TODO +**Limit_Clients** +* 用recv方法替换掉现有的close方法,根据上次接收到的时间来清理不活动的连接。 \ No newline at end of file From 74a05be2e51b925da2c5a09ae2c99b2ca50b675b Mon Sep 17 00:00:00 2001 From: Falseen Date: Fri, 7 Apr 2017 19:12:09 +0800 Subject: [PATCH 36/43] update readme --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b7a6785..076b569 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ PySocket ,一个通过猴子补丁(monkey patch)动态修改 socket 的项 通过猴子补丁的方式给 socket 动态地添加一些增强功能,比如限制客户端数量、前置代理什么的。 **使用的时候只要把对应文件夹中的 socket.py 文件放到程序的目录即可生效,不用修改任何源码。** +**注意:** +* 对于通过pip安装的程序,需要放到执行文件所在的文件夹,但不建议这么做,可能会影响其他程序。建议不要用pip安装。 +* 对于shadowsocks来说,需要把socket.py文件放到根目录(即shadowsocks目录),而不能放到 shadowsocks/shadowsocks 目录。具体原因不明,有时间再好好查一下。 + ## 说明: 项目中每个文件夹代表不同的功能。 @@ -29,5 +33,6 @@ PySocket ,一个通过猴子补丁(monkey patch)动态修改 socket 的项 **最后我想说的是,python 真的是世界上最好的语言,太自由了!!!** ## TODO -**Limit_Clients** -* 用recv方法替换掉现有的close方法,根据上次接收到的时间来清理不活动的连接。 \ No newline at end of file +* 用recv方法替换掉现有的close方法,根据上次接收到的时间来清理不活动的连接。 + +* 用hook的方式修改socket,示例:`pysocket python test.py` \ No newline at end of file From 87f8e973034a952c92b3fbe06d1a13a09c79a6ec Mon Sep 17 00:00:00 2001 From: Falseen Date: Fri, 7 Apr 2017 19:12:09 +0800 Subject: [PATCH 37/43] fix some error for ssr https://github.com/falseen/PySocket/issues/9 --- Limit_Clients/socket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 7ded230..0acedab 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -180,7 +180,7 @@ def new_recvfrom(orgin_method, self, *args, **kwds): def new_bind(orgin_method, self, *args, **kwds): - server_addres, server_port = args[0] + server_addres, server_port = args[0][:2] # 如果绑定地址是0,那这个 socket 就一定不是和客户端通信的。 # 此处主要是考虑到shadowsocks服务端在做流量转发的时候会对本地的socket进行绑定。 if args[0][1] != 0: From 06146e4aa583e77834041f10016d8f7aa3bf21e0 Mon Sep 17 00:00:00 2001 From: Falseen Date: Sat, 8 Apr 2017 18:53:59 +0800 Subject: [PATCH 38/43] fix some error for ssr again https://github.com/falseen/PySocket/issues/9 --- Limit_Clients/socket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index 0acedab..ef1923b 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -107,7 +107,7 @@ def new_accept(orgin_method, self, *args, **kwds): while True: return_value = orgin_method(*args, **kwds) self_socket = return_value[0] - client_ip, client_port = return_value[1] + client_ip, client_port = return_value[1][:2] server_addrs = self._server_addrs client_list = self._all_client_list.get(server_addrs, {}) if len(client_list) < self._limit_clients_num or client_ip in client_list: @@ -149,7 +149,7 @@ def new_accept(orgin_method, self, *args, **kwds): def new_recvfrom(orgin_method, self, *args, **kwds): return_value = orgin_method(*args, **kwds) server_addrs = self._server_addrs - client_ip, client_port = return_value[1] + client_ip, client_port = return_value[1][:2] client_list = self._all_client_list.get(server_addrs, {}) if len(client_list) < self._limit_clients_num or client_ip in client_list: From 500c351a2aaa159eb94b68401364c69ecaa210bb Mon Sep 17 00:00:00 2001 From: Falseen Date: Mon, 4 Dec 2017 17:42:56 +0800 Subject: [PATCH 39/43] fix some error for UDP https://github.com/falseen/PySocket/issues/12 --- proxy/socket.py | 20 ++++++++++++++------ shadowsocks/socket.py | 20 ++++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/proxy/socket.py b/proxy/socket.py index 5a5c090..5c11d7c 100644 --- a/proxy/socket.py +++ b/proxy/socket.py @@ -395,6 +395,8 @@ def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, *arg self.proxy_peername = None self._timeout = None + self._is_client = True + self.proxy_udp_host = ("0.0.0.0",0) def _readall(self, file, count): """ @@ -456,6 +458,11 @@ def bind(self, *pos, **kw): Implements proxy connection for UDP sockets, which happens during the bind() phase. """ + # socket client + if pos[0][1] == 0: + self._is_client = True + else: + self._is_client = False proxy_type, proxy_addr, proxy_port, rdns, username, password = self.proxy if not proxy_type or self.type != socket.SOCK_DGRAM: return _orig_socket.bind(self, *pos, **kw) @@ -490,12 +497,13 @@ def bind(self, *pos, **kw): # but some proxies return a private IP address (10.x.y.z) host, _ = proxy _, port = relay - super(socksocket, self).connect((host, port)) + #super(socksocket, self).connect((host, port)) super(socksocket, self).settimeout(self._timeout) self.proxy_sockname = ("0.0.0.0", 0) # Unknown + self.proxy_udp_host = (host, port) def sendto(self, bytes, *args, **kwargs): - if self.type != socket.SOCK_DGRAM or self.proxy[0] == HTTP: + if self.type != socket.SOCK_DGRAM or self.proxy[0] == HTTP or not self._is_client: return super(socksocket, self).sendto(bytes, *args, **kwargs) if not self._proxyconn: self.bind(("", 0)) @@ -509,19 +517,19 @@ def sendto(self, bytes, *args, **kwargs): STANDALONE = b"\x00" header.write(STANDALONE) self._write_SOCKS5_address(address, header) - + super(socksocket, self).connect(self.proxy_udp_host) sent = super(socksocket, self).send( header.getvalue() + bytes, *flags, **kwargs) return sent - header.tell() def send(self, bytes, flags=0, **kwargs): - if self.type == socket.SOCK_DGRAM and self.proxy[0] != HTTP: - return self.sendto(bytes, flags, self.proxy_peername, **kwargs) + if self.type == socket.SOCK_DGRAM and self.proxy[0] != HTTP and self._is_client: + return self.sendto(bytes, flags, self.proxy_udp_host, **kwargs) else: return super(socksocket, self).send(bytes, flags, **kwargs) def recvfrom(self, bufsize, flags=0): - if self.type != socket.SOCK_DGRAM or self.proxy[0] == HTTP: + if self.type != socket.SOCK_DGRAM or self.proxy[0] == HTTP or not self._is_client: return super(socksocket, self).recvfrom(bufsize, flags) if not self._proxyconn: self.bind(("", 0)) diff --git a/shadowsocks/socket.py b/shadowsocks/socket.py index a462976..d33a369 100644 --- a/shadowsocks/socket.py +++ b/shadowsocks/socket.py @@ -395,6 +395,8 @@ def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, *arg self.proxy_peername = None self._timeout = None + self._is_client = True + self.proxy_udp_host = ("0.0.0.0",0) def _readall(self, file, count): """ @@ -456,6 +458,11 @@ def bind(self, *pos, **kw): Implements proxy connection for UDP sockets, which happens during the bind() phase. """ + # socket client + if pos[0][1] == 0: + self._is_client = True + else: + self._is_client = False proxy_type, proxy_addr, proxy_port, rdns, username, password = self.proxy if not proxy_type or self.type != socket.SOCK_DGRAM: return _orig_socket.bind(self, *pos, **kw) @@ -490,12 +497,13 @@ def bind(self, *pos, **kw): # but some proxies return a private IP address (10.x.y.z) host, _ = proxy _, port = relay - super(socksocket, self).connect((host, port)) + #super(socksocket, self).connect((host, port)) super(socksocket, self).settimeout(self._timeout) self.proxy_sockname = ("0.0.0.0", 0) # Unknown + self.proxy_udp_host = (host, port) def sendto(self, bytes, *args, **kwargs): - if self.type != socket.SOCK_DGRAM or self.proxy[0] == HTTP: + if self.type != socket.SOCK_DGRAM or self.proxy[0] == HTTP or not self._is_client: return super(socksocket, self).sendto(bytes, *args, **kwargs) if not self._proxyconn: self.bind(("", 0)) @@ -509,19 +517,19 @@ def sendto(self, bytes, *args, **kwargs): STANDALONE = b"\x00" header.write(STANDALONE) self._write_SOCKS5_address(address, header) - + super(socksocket, self).connect(self.proxy_udp_host) sent = super(socksocket, self).send( header.getvalue() + bytes, *flags, **kwargs) return sent - header.tell() def send(self, bytes, flags=0, **kwargs): - if self.type == socket.SOCK_DGRAM and self.proxy[0] != HTTP: - return self.sendto(bytes, flags, self.proxy_peername, **kwargs) + if self.type == socket.SOCK_DGRAM and self.proxy[0] != HTTP and self._is_client: + return self.sendto(bytes, flags, self.proxy_udp_host, **kwargs) else: return super(socksocket, self).send(bytes, flags, **kwargs) def recvfrom(self, bufsize, flags=0): - if self.type != socket.SOCK_DGRAM or self.proxy[0] == HTTP: + if self.type != socket.SOCK_DGRAM or self.proxy[0] == HTTP or not self._is_client: return super(socksocket, self).recvfrom(bufsize, flags) if not self._proxyconn: self.bind(("", 0)) From add19d5bcc3ee42c78d3165606e4134fc8ce97d9 Mon Sep 17 00:00:00 2001 From: Falseen Date: Wed, 6 Dec 2017 16:24:12 +0800 Subject: [PATCH 40/43] fix some error for TCP client bind https://github.com/falseen/PySocket/issues/12 --- proxy/socket.py | 14 ++++++++++++-- shadowsocks/socket.py | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/proxy/socket.py b/proxy/socket.py index 5c11d7c..d19831c 100644 --- a/proxy/socket.py +++ b/proxy/socket.py @@ -459,13 +459,23 @@ def bind(self, *pos, **kw): which happens during the bind() phase. """ # socket client - if pos[0][1] == 0: + bind_host, bind_port = pos[0] + proxy = self._proxy_addr() + if bind_port == 0: self._is_client = True else: self._is_client = False proxy_type, proxy_addr, proxy_port, rdns, username, password = self.proxy - if not proxy_type or self.type != socket.SOCK_DGRAM: + if not proxy_type or not self._is_client: return _orig_socket.bind(self, *pos, **kw) + elif self.type != socket.SOCK_DGRAM and self._is_client: + if "127.0.0.1" in proxy or u"127.0.0.1" in proxy: + _pos_list = list(pos) + _pos_list[0] = ("127.0.0.1", bind_port) + _pos = tuple(_pos_list) + else: + _pos = pos + return _orig_socket.bind(self, *_pos, **kw) elif self.proxy[0] == HTTP: return _orig_socket.bind(self, *pos, **kw) diff --git a/shadowsocks/socket.py b/shadowsocks/socket.py index d33a369..74d1a57 100644 --- a/shadowsocks/socket.py +++ b/shadowsocks/socket.py @@ -459,13 +459,23 @@ def bind(self, *pos, **kw): which happens during the bind() phase. """ # socket client - if pos[0][1] == 0: + bind_host, bind_port = pos[0] + proxy = self._proxy_addr() + if bind_port == 0: self._is_client = True else: self._is_client = False proxy_type, proxy_addr, proxy_port, rdns, username, password = self.proxy - if not proxy_type or self.type != socket.SOCK_DGRAM: + if not proxy_type or not self._is_client: return _orig_socket.bind(self, *pos, **kw) + elif self.type != socket.SOCK_DGRAM and self._is_client: + if "127.0.0.1" in proxy or u"127.0.0.1" in proxy: + _pos_list = list(pos) + _pos_list[0] = ("127.0.0.1", bind_port) + _pos = tuple(_pos_list) + else: + _pos = pos + return _orig_socket.bind(self, *_pos, **kw) elif self.proxy[0] == HTTP: return _orig_socket.bind(self, *pos, **kw) From bca032ef2e61b58eb99e3ee51cc01b42503051b4 Mon Sep 17 00:00:00 2001 From: Woody Huang Date: Mon, 8 Jan 2018 18:04:49 +0800 Subject: [PATCH 41/43] 1. fix bugs when setting timeout for socket 2. fix bugs casued by abnormal output of socket.getpeername Signed-off-by: Woody Huang --- Limit_Clients/socket.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index ef1923b..b3484d1 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -120,16 +120,16 @@ def new_accept(orgin_method, self, *args, **kwds): self._all_client_list[server_addrs].update(client_list) if set_close_timeout: # set recv_timeout and send_timeout , struct.pack("II", some_num_secs, some_num_microsecs) - self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, struct.pack("II", recv_timeout, 0)) - self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, struct.pack("II", send_timeout, 0)) + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, struct.pack("LL", recv_timeout, 0)) + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, struct.pack("LL", send_timeout, 0)) return return_value else: for k,v in self._all_client_list[server_addrs].copy().items(): last_up_time = v["last_up_time"] if time.time() - last_up_time> recvfrom_timeout and v["client_num"] < 1: if set_close_timeout: - self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, struct.pack("II", recv_timeout, 0)) - self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, struct.pack("II", send_timeout, 0)) + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, struct.pack("LL", recv_timeout, 0)) + self_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, struct.pack("LL", send_timeout, 0)) logging.info("[socket] remove the client %s" % (k)) del client_list[k] if client_list.get(client_ip, None) == None: @@ -218,7 +218,7 @@ def close(self ,*args, **kwds): # 自定义 close 方法,让其在关闭的时候从列表中清理掉自身的 socket 或 ip。 def new_close(self, *args, **kwds): - addr, port = self.getpeername() + addr, port = self.getpeername()[0:2] server_addrs = self._server_addrs client_list = self._all_client_list[server_addrs] if client_list.get(addr, None) != None: From eb17fae71620dbc1068ba4dee4f2579f11af9a22 Mon Sep 17 00:00:00 2001 From: Woody Huang Date: 2018年1月10日 11:53:13 +0800 Subject: [PATCH 42/43] fix bug when replacing bind method of socket Signed-off-by: Woody Huang --- Limit_Clients/socket.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index b3484d1..fc22b6b 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -212,6 +212,7 @@ class new_client_socket(socket.socket): def __init__(self, *args, **kwds): super(new_client_socket, self).__init__(*args, **kwds) + new_self_method(self, "bind", new_bind) def close(self ,*args, **kwds): super(new_client_socket, self).__init__(*args, **kwds) @@ -237,7 +238,7 @@ def new_close(self, *args, **kwds): setattr(socket.socket, '_all_client_list', {}) setattr(socket.socket, 'last_log_time', [0]) -new_class_method(socket.socket, 'bind', new_bind) +# new_class_method(socket.socket, 'bind', new_bind) if not sys.version_info[0]>= 3: socket._socketobject = new_client_socket socket.socket = new_socket \ No newline at end of file From 12454461b3ae79663a82b3dbc7365a8fe5496074 Mon Sep 17 00:00:00 2001 From: Falseen Date: 2018年1月10日 17:26:25 +0800 Subject: [PATCH 43/43] fix bug when replacing "bind" method of py3 socket --- Limit_Clients/socket.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/Limit_Clients/socket.py b/Limit_Clients/socket.py index fc22b6b..378fdd3 100644 --- a/Limit_Clients/socket.py +++ b/Limit_Clients/socket.py @@ -73,6 +73,7 @@ #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +PYTHON_VERSION = sys.version_info[0] send_timeout = recv_timeout limit_all_clients = False @@ -196,27 +197,25 @@ def new_bind(orgin_method, self, *args, **kwds): new_self_method(self, 'accept', new_accept) if self.type == socket.SOCK_DGRAM: new_self_method(self, 'recvfrom', new_recvfrom) - orgin_method(self, *args, **kwds) + if PYTHON_VERSION>= 3: + orgin_method(*args, **kwds) + else: + orgin_method(self, *args, **kwds) # 自定义 socket 类,可以自定义实例方法或属性。 +# 为 accept 生成的socket对象创建一个单独的类,目的是给其动态地绑定 close 方法。 class new_socket(socket.socket): def __init__(self, *args, **kwds): super(new_socket, self).__init__(*args, **kwds) - - -# 为 accept 生成的socket对象创建一个单独的类,目的是给其动态地绑定 close 方法。 -class new_client_socket(socket.socket): - - def __init__(self, *args, **kwds): - super(new_client_socket, self).__init__(*args, **kwds) - new_self_method(self, "bind", new_bind) + if PYTHON_VERSION>= 3: + new_class_method(self, 'bind', new_bind) def close(self ,*args, **kwds): - super(new_client_socket, self).__init__(*args, **kwds) - + super(new_socket, self).__init__(*args, **kwds) + # 自定义 close 方法,让其在关闭的时候从列表中清理掉自身的 socket 或 ip。 def new_close(self, *args, **kwds): addr, port = self.getpeername()[0:2] @@ -231,14 +230,16 @@ def new_close(self, *args, **kwds): client_list[addr]["client_num"] -= 1 logging.debug("[socket] close the client socket %s:%d" % (addr, port)) self._all_client_list[server_addrs].update(client_list) - return super(new_client_socket, self).close(*args, **kwds) + return super(new_socket, self).close(*args, **kwds) + # 添加类属性, 此属性是全局的,所有socket对象都共享此属性。 setattr(socket.socket, '_all_client_list', {}) setattr(socket.socket, 'last_log_time', [0]) -# new_class_method(socket.socket, 'bind', new_bind) -if not sys.version_info[0]>= 3: - socket._socketobject = new_client_socket -socket.socket = new_socket \ No newline at end of file +# python2 +if not PYTHON_VERSION>= 3: + new_class_method(socket.socket, 'bind', new_bind) + socket._socketobject = new_socket +socket.socket = new_socket

AltStyle によって変換されたページ (->オリジナル) /