-
Notifications
You must be signed in to change notification settings - Fork 228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Some Improvements (Outlined Below) #30
Open
nvv1d
wants to merge
7
commits into
GFW-knocker:main
Choose a base branch
from
nvv1d:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
407b832
Update pyprox_tcp.py
nvv1d a5d5d17
Create config.ini
nvv1d dc94d62
Update pyprox_tcp.py
nvv1d 2d58aba
Update pyprox_tcp.py
nvv1d 55a2ea1
Update pyprox_tcp.py
nvv1d 9787d6c
Update pyprox_tcp.py
nvv1d 0787386
Update config.ini
nvv1d File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[settings] | ||
listen_PORT = 2500 | ||
Cloudflare_IP = 194.36.55.89, 195.245.221.245, 195.245.221.245, 195.245.221.245, 195.245.221.245, 195.245.221.245, 45.131.6.224, 176.126.206.97, 185.72.49.127, 207.189.149.181, 45.14.174.17, 185.207.92.5, 203.32.120.29, 104.20.190.33, 45.131.6.131, 185.201.139.152, 45.131.208.5, 203.34.28.236, 203.13.32.50, 195.245.221.214, 45.12.31.113 | ||
Cloudflare_port = 443 | ||
L_fragment = 77 | ||
fragment_sleep = 0.2 | ||
my_socket_timeout = 60 | ||
first_time_sleep = 0.01 | ||
accept_time_sleep = 0.01 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,147 +2,135 @@ | |
|
||
import socket | ||
import threading | ||
from pathlib import Path | ||
import os | ||
import copy | ||
from concurrent.futures import ThreadPoolExecutor | ||
import time | ||
import datetime | ||
import os | ||
import sys | ||
import argparse | ||
import logging | ||
from logging.handlers import TimedRotatingFileHandler | ||
|
||
import configparser | ||
import random | ||
|
||
# Check if the OS is Linux and set the maximum number of open files to 128000 | ||
if os.name == 'posix': | ||
print('os is linux') | ||
import resource # ( -> pip install python-resources ) | ||
# set linux max_num_open_socket from 1024 to 128k | ||
import resource | ||
resource.setrlimit(resource.RLIMIT_NOFILE, (127000, 128000)) | ||
|
||
# Function to send data in fragments | ||
def send_data_in_fragment(data, sock): | ||
for i in range(0, len(data), L_fragment): | ||
fragment_data = data[i:i+L_fragment] | ||
logging.debug(f'[SEND] {len(fragment_data)} bytes') | ||
sock.sendall(fragment_data) | ||
time.sleep(fragment_sleep) | ||
logging.debug('[SEND] ----------finish------------') | ||
|
||
|
||
listen_PORT = 2500 # pyprox listening to 127.0.0.1:listen_PORT | ||
|
||
Cloudflare_IP = '162.159.135.42' # plos.org (can be any dirty cloudflare ip) | ||
Cloudflare_port = 443 | ||
|
||
L_fragment = 77 # length of fragments of Client Hello packet (L_fragment Byte in each chunk) | ||
fragment_sleep = 0.2 # sleep between each fragment to make GFW-cache full so it forget previous chunks. LOL. | ||
|
||
|
||
|
||
# ignore description below , its for old code , just leave it intact. | ||
my_socket_timeout = 60 # default for google is ~21 sec , recommend 60 sec unless you have low ram and need close soon | ||
first_time_sleep = 0.01 # speed control , avoid server crash if huge number of users flooding (default 0.1) | ||
accept_time_sleep = 0.01 # avoid server crash on flooding request -> max 100 sockets per second | ||
|
||
|
||
|
||
class ThreadedServer(object): | ||
def __init__(self, host, port): | ||
self.host = host | ||
self.port = port | ||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||
self.sock.bind((self.host, self.port)) | ||
|
||
def listen(self): | ||
self.sock.listen(128) # up to 128 concurrent unaccepted socket queued , the more is refused untill accepting those. | ||
while True: | ||
client_sock , client_addr = self.sock.accept() | ||
client_sock.settimeout(my_socket_timeout) | ||
|
||
#print('someone connected') | ||
time.sleep(accept_time_sleep) # avoid server crash on flooding request | ||
thread_up = threading.Thread(target = self.my_upstream , args =(client_sock,) ) | ||
thread_up.daemon = True #avoid memory leak by telling os its belong to main program , its not a separate program , so gc collect it when thread finish | ||
thread_up.start() | ||
|
||
|
||
def my_upstream(self, client_sock): | ||
first_flag = True | ||
backend_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
# Function to handle upstream traffic from the client to the backend server | ||
def my_upstream(client_sock): | ||
first_flag = True | ||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as backend_sock: | ||
backend_sock.settimeout(my_socket_timeout) | ||
while True: | ||
try: | ||
if( first_flag == True ): | ||
if first_flag: | ||
first_flag = False | ||
|
||
time.sleep(first_time_sleep) # speed control + waiting for packet to fully recieve | ||
time.sleep(first_time_sleep) | ||
data = client_sock.recv(16384) | ||
#print('len data -> ',str(len(data))) | ||
#print('user talk :') | ||
|
||
if data: | ||
backend_sock.connect((Cloudflare_IP,Cloudflare_port)) | ||
thread_down = threading.Thread(target = self.my_downstream , args = (backend_sock , client_sock) ) | ||
if data: | ||
backend_ip = get_next_backend_ip() | ||
print(f'Using backend IP: {backend_ip}') # Print the selected backend IP | ||
backend_sock.connect((backend_ip, Cloudflare_port)) | ||
thread_down = threading.Thread(target=my_downstream, args=(backend_sock, client_sock)) | ||
thread_down.daemon = True | ||
thread_down.start() | ||
# backend_sock.sendall(data) | ||
send_data_in_fragment(data,backend_sock) | ||
|
||
else: | ||
send_data_in_fragment(data, backend_sock) | ||
else: | ||
raise Exception('cli syn close') | ||
|
||
else: | ||
data = client_sock.recv(4096) | ||
if data: | ||
backend_sock.sendall(data) | ||
else: | ||
raise Exception('cli pipe close') | ||
|
||
except Exception as e: | ||
#print('upstream : '+ repr(e) ) | ||
time.sleep(2) # wait two second for another thread to flush | ||
logging.debug(f'[UPSTREAM] {repr(e)}') | ||
time.sleep(2) | ||
client_sock.close() | ||
backend_sock.close() | ||
return False | ||
|
||
|
||
|
||
|
||
def my_downstream(self, backend_sock , client_sock): | ||
first_flag = True | ||
while True: | ||
try: | ||
if( first_flag == True ): | ||
first_flag = False | ||
data = backend_sock.recv(16384) | ||
if data: | ||
client_sock.sendall(data) | ||
else: | ||
raise Exception('backend pipe close at first') | ||
|
||
# Function to handle downstream traffic from the backend server to the client | ||
def my_downstream(backend_sock, client_sock): | ||
first_flag = True | ||
while True: | ||
try: | ||
if first_flag: | ||
first_flag = False | ||
data = backend_sock.recv(16384) | ||
if data: | ||
client_sock.sendall(data) | ||
else: | ||
data = backend_sock.recv(4096) | ||
if data: | ||
client_sock.sendall(data) | ||
else: | ||
raise Exception('backend pipe close') | ||
|
||
except Exception as e: | ||
#print('downstream '+backend_name +' : '+ repr(e)) | ||
time.sleep(2) # wait two second for another thread to flush | ||
backend_sock.close() | ||
client_sock.close() | ||
return False | ||
|
||
|
||
def send_data_in_fragment(data , sock): | ||
|
||
for i in range(0, len(data), L_fragment): | ||
fragment_data = data[i:i+L_fragment] | ||
print('send ',len(fragment_data),' bytes') | ||
|
||
# sock.send(fragment_data) | ||
sock.sendall(fragment_data) | ||
|
||
time.sleep(fragment_sleep) | ||
|
||
print('----------finish------------') | ||
|
||
|
||
print ("Now listening at: 127.0.0.1:"+str(listen_PORT)) | ||
ThreadedServer('',listen_PORT).listen() | ||
|
||
|
||
|
||
|
||
raise Exception('backend pipe close at first') | ||
else: | ||
data = backend_sock.recv(4096) | ||
if data: | ||
client_sock.sendall(data) | ||
else: | ||
raise Exception('backend pipe close') | ||
except Exception as e: | ||
logging.debug(f'[DOWNSTREAM] {repr(e)}') | ||
time.sleep(2) | ||
client_sock.close() | ||
return False | ||
|
||
# Function to parse command line arguments | ||
def parse_args(): | ||
parser = argparse.ArgumentParser(description='Python Proxy') | ||
parser.add_argument('--config', type=str, default='config.ini', help='Path to the configuration file') | ||
return parser.parse_args() | ||
|
||
# Function to load configuration from a file | ||
def load_config(config_path): | ||
config = configparser.ConfigParser() | ||
config.read(config_path) | ||
|
||
global listen_PORT, Cloudflare_IPs, Cloudflare_port, L_fragment, fragment_sleep, my_socket_timeout, first_time_sleep, accept_time_sleep | ||
listen_PORT = int(config.get('settings', 'listen_PORT')) | ||
Cloudflare_IPs = [ip.strip() for ip in config.get('settings', 'Cloudflare_IP').split(',')] | ||
Cloudflare_port = int(config.get('settings', 'Cloudflare_port')) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cloudflare_IPs = itertools.cycle(Cloudflare_IPs) |
||
L_fragment = int(config.get('settings', 'L_fragment')) | ||
fragment_sleep = float(config.get('settings', 'fragment_sleep')) | ||
my_socket_timeout = int(config.get('settings', 'my_socket_timeout')) | ||
first_time_sleep = float(config.get('settings', 'first_time_sleep')) | ||
accept_time_sleep = float(config.get('settings', 'accept_time_sleep')) | ||
|
||
# Function to get the next backend IP using round-robin load balancing | ||
def get_next_backend_ip(): | ||
global Cloudflare_IPs | ||
selected_ip = random.choice(Cloudflare_IPs) | ||
Cloudflare_IPs = Cloudflare_IPs[1:] + [selected_ip] | ||
return selected_ip | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. def get_next_backend_ip(): |
||
|
||
# Main function to start the proxy server | ||
def main(): | ||
args = parse_args() | ||
load_config(args.config) | ||
|
||
print(f'Proxy server listening on 127.0.0.1:{listen_PORT}') | ||
|
||
|
||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_sock: | ||
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||
server_sock.bind(('', listen_PORT)) | ||
server_sock.listen(128) | ||
|
||
with ThreadPoolExecutor(max_workers=128) as executor: | ||
while True: | ||
client_sock, client_addr = server_sock.accept() | ||
client_sock.settimeout(my_socket_timeout) | ||
time.sleep(accept_time_sleep) | ||
executor.submit(my_upstream, client_sock) | ||
|
||
if __name__ == "__main__": | ||
main() |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import itertools