Commit ed702fe5 authored by Yury's avatar Yury

pypi package + states

parent 78768122
Pipeline #85 passed with stages
in 10 minutes and 13 seconds
stages:
- unittests
- build
tests:
stage: unittests
script:
- apk update
- apk add --update --no-cache --virtual .build-deps alpine-sdk python3-dev musl-dev postgresql-dev libffi-dev
- pip3 install -U setuptools pip
- pip3 install --no-cache-dir -r requirements.txt
- python3 -m unittest discover tests *_test.py
pypi_build:
stage: build
script:
- apk update
- apk add --update --no-cache --virtual .build-deps alpine-sdk python3-dev musl-dev postgresql-dev libffi-dev
- pip3 install -U setuptools pip twine
- python3 generate_setup.py
- python3 setup.py sdist
- twine upload dist/*
\ No newline at end of file
MIT License
Copyright (c) 2018 Yurzs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
# Socks5 async proxy server
Saturn
TBD
\ No newline at end of file
import datetime
version = datetime.datetime.now(tz=datetime.timezone.utc).strftime('%Y.%m.%d.%H%M')
try:
with open('README.md', 'r', encoding='utf-8') as f:
long_description = f.read()
except FileNotFoundError:
long_description = ''
with open('setup.txt', 'r') as text:
with open('setup.py', 'w') as setup:
txt = text.read()
setup.write(txt.format(version=version, long_description=long_description))
\ No newline at end of file
......@@ -2,3 +2,4 @@ from . import protocol
from . import dispatcher
from . import engine
from . import socks
from . import auth
\ No newline at end of file
from . import dict
from . import gssapi
from .core import Authenticator
\ No newline at end of file
class Authenticator:
method = 2
def __new__(cls, *args, **kwargs):
if not hasattr(cls, 'instance'):
cls.instance = super().__new__(cls)
return cls.instance
def __init__(self, **data):
self.users = data
async def authenticate(self, data):
try:
login = data[2:2 + data[1]] # index 1 specifies length of login
password = data[3 + data[1]:3 + data[1] + data[2 + data[1]]] # index 2+data[1] specifies length of password
except IndexError:
print(f'INDEX ERROR {data}')
return False
if self.users.get(login.decode()) == password.decode():
return True
return False
AUTHENTICATION_METHODS = ['saturn.auth.dict']
SATURN_AUTH_DICT = {
'test_user': 'Test_password'
}
\ No newline at end of file
from saturn.socks import SocksTcpReply, SocksTcpRequest, SocksHello
from saturn import state
from saturn.socks import SocksTcpRequest, SocksHello, SocksAuthenticate
class Dispatcher:
......@@ -7,17 +8,20 @@ class Dispatcher:
self.loop = loop
self.server_protocol = protocol
self.client_transport = None
self.authenticated = False
self.connected = False
self.state = state.NotAuthenticated()
async def handle(self, data):
if self.connected:
if (not isinstance(self.state, state.Connected) or not isinstance(self.state, state.Authenticated)) and \
data[0] == 5 and len(data) == data[1] + 2:
self.state = state.NotAuthenticated()
if isinstance(self.state, state.Connected):
self.client_transport.write(data)
return
if not self.authenticated:
self.authenticated = True
return SocksHello(data).reply(self.server)
request = SocksTcpRequest(self, data)
if request.cmd == 1:
result = bytes(await request.connect())
return result
\ No newline at end of file
elif isinstance(self.state, state.NotAuthenticated):
return SocksHello(self, data).reply(self.server)
elif isinstance(self.state, state.WaitingAuthenticationData):
return await SocksAuthenticate(self, data).authenticate()
elif isinstance(self.state, state.Authenticated):
request = SocksTcpRequest(self, data)
if request.cmd == 1:
result = bytes(await request.connect())
return result
import asyncio
from saturn import protocol
from saturn import protocol, config
class Server:
def __init__(self, host, port,
tcp=True, udp=False,
auth=False,
auth_gssapi=False,
auth_user_pass=False):
tcp=True, udp=False):
self.host = host
self.port = port
self.tcp = tcp
self.udp = udp
self.auth_methods = []
self.auth_methods.append(0) if not auth else ''
self.auth_methods.append(1) if auth_gssapi else ''
self.auth_methods.append(2) if auth_user_pass else ''
def init_auth_methods(self):
for method in config.AUTHENTICATION_METHODS:
m = __import__(method, globals=globals(), fromlist=[''])
self.auth_methods.append(m.Authenticator(**getattr(config, method.upper().replace('.', '_'), {})))
@property
def server_auth_methods(self):
return [x.method for x in self.auth_methods]
async def auth(self, method, *args, **kwargs):
for m in self.auth_methods:
if m.method == method:
return await m.authenticate(*args, **kwargs)
def start(self):
loop = asyncio.new_event_loop()
self.init_auth_methods()
if self.tcp:
loop.create_task(protocol.Socks5TcpServer(self, loop).start_server(self.host, self.port))
loop.run_forever()
if __name__ == '__main__':
server = Server('0.0.0.0', 8080)
server = Server('0.0.0.0', 8081)
server.start()
\ No newline at end of file
......@@ -11,7 +11,6 @@ class TcpClient(asyncio.Protocol):
self.on_connect.set_result(True)
def data_received(self, data):
print(data)
self.dispatcher.server_protocol.transport.write(data)
def connection_lost(self, exc):
......
......@@ -14,6 +14,7 @@ class Socks5TcpServer(asyncio.Protocol):
self.dispatcher = Dispatcher(self.server, self.loop, self)
def data_received(self, data):
print(data)
asyncio.Task(self.async_data_handler(data))
def connection_lost(self, exc) -> None:
......
from ipaddress import IPv6Address, IPv4Address
from saturn import state
from saturn.protocol.client_tcp import TcpClient
class SocksHello:
class SocksPacket:
def __init__(self, data):
self.ver = data[0]
assert self.ver == 5
assert data[0] == 5
self.ver = 5
class SocksHello(SocksPacket):
def __init__(self, dispatcher, data):
super().__init__(data)
self.dispatcher = dispatcher
self.nmethods = data[1]
self.methods = [x for x in data[2:2 + self.nmethods]]
def reply(self, server):
return self.ver.to_bytes(1, byteorder='big') + int.to_bytes(0, 1, byteorder='big')
for m in self.dispatcher.server.server_auth_methods:
if m in self.methods:
self.dispatcher.state = state.WaitingAuthenticationData(method=m)
return self.ver.to_bytes(1, byteorder='big') + int.to_bytes(m, 1, byteorder='big')
return self.ver.to_bytes(1, byteorder='big') + int.to_bytes(5, 1, byteorder='big')
class SocksAuthenticate:
def __init__(self, dispatcher, data):
self.data = data
self.dispatcher = dispatcher
self.server = dispatcher.server
async def authenticate(self):
if await self.server.auth(self.dispatcher.state.method, self.data):
self.dispatcher.state = state.Authenticated()
return int(1).to_bytes(1, byteorder='big') + int(0).to_bytes(1, byteorder='big')
return int(1).to_bytes(1, byteorder='big') + int(1).to_bytes(1, byteorder='big')
class SocksTcpRequest:
def __init__(self, dispatcher, data):
self.dispatcher = dispatcher
assert data[0] == 5
assert data[0] == 5, f'EXCEPTION! : {data}'
self.ver = data[0]
self.cmd = data[1]
self.rsv = data[2]
......@@ -24,7 +48,7 @@ class SocksTcpRequest:
if self.atyp == 1:
self.dst_addr = IPv4Address(data[4:-2])
elif self.atyp == 3:
self.dst_addr = data[5:5+data[4]].decode()
self.dst_addr = data[5:5 + data[4]].decode()
elif self.atyp == 4:
self.dst_addr = IPv6Address(data[4:-2])
self.dst_port = int.from_bytes(data[-2:], byteorder='big')
......@@ -36,9 +60,9 @@ class SocksTcpRequest:
str(self.dst_addr), self.dst_port)
self.dispatcher.connected = True
await on_connect
self.dispatcher.state = state.Connected()
return SocksTcpReply(self.dispatcher, 5, 0, 0, 1, int(IPv4Address('192.168.1.45')), 8080)
async def bind(self):
pass
......@@ -58,8 +82,8 @@ class SocksTcpReply:
def __bytes__(self):
return self.ver.to_bytes(1, byteorder='big') + \
self.rep.to_bytes(1, byteorder='big') + \
self.rsv.to_bytes(1, byteorder='big') + \
self.atyp.to_bytes(1, byteorder='big') + \
self.bind_addr.to_bytes(4, byteorder='big') + \
self.bind_port.to_bytes(2, byteorder='big')
\ No newline at end of file
self.rep.to_bytes(1, byteorder='big') + \
self.rsv.to_bytes(1, byteorder='big') + \
self.atyp.to_bytes(1, byteorder='big') + \
self.bind_addr.to_bytes(4, byteorder='big') + \
self.bind_port.to_bytes(2, byteorder='big')
class State:
level = -1
class NotAuthenticated(State):
level = 0
class WaitingAuthenticationData(State):
def __init__(self, method):
self.method = method
level = 1
class Authenticated(State):
level = 2
class Connected(State):
level = 3
from setuptools import setup, find_packages
setup(
name='saturn_proxy_server', # How you named your package folder (MyLib)
packages=find_packages(exclude=['*.tests', '*.tests.*', 'tests.*', 'tests']), # Chose the same as "name"
version='2019.09.23.1936', # Start with a small number and increase it with every change you make
license='MIT', # Chose a license from here: https://help.github.com/articles/licensing-a-repository
long_description='''# Socks5 async proxy server
Saturn
TBD''',
long_description_content_type='text/markdown',
description='Socks5 async proxy server', # Give a short description about your library
author='Yury (Yurzs)', # Type in your name
author_email='dev@best-service.online', # Type in your E-Mail
url='https://git.best-service.online/yurzs/saturn', # Provide either the link to your github or to your website
keywords=['Saturn', 'Socks5', 'proxy'], # Keywords that define your package best
classifiers=[
'Development Status :: 3 - Alpha',
# Chose either "3 - Alpha", "4 - Beta" or "5 - Production/Stable" as the current state of your package
'Intended Audience :: Developers', # Define that your audience are developers
'Topic :: Software Development :: Build Tools',
'License :: OSI Approved :: MIT License', # Again, pick a license
'Programming Language :: Python :: 3', # Specify which pyhton versions that you want to support
'Programming Language :: Python :: 3.7',
],
)
\ No newline at end of file
from setuptools import setup, find_packages
setup(
name='saturn_proxy_server', # How you named your package folder (MyLib)
packages=find_packages(exclude=['*.tests', '*.tests.*', 'tests.*', 'tests']), # Chose the same as "name"
version='{version}', # Start with a small number and increase it with every change you make
license='MIT', # Chose a license from here: https://help.github.com/articles/licensing-a-repository
long_description='''{long_description}''',
long_description_content_type='text/markdown',
description='Socks5 async proxy server', # Give a short description about your library
author='Yury (Yurzs)', # Type in your name
author_email='dev@best-service.online', # Type in your E-Mail
url='https://git.best-service.online/yurzs/saturn', # Provide either the link to your github or to your website
keywords=['Saturn', 'Socks5', 'proxy'], # Keywords that define your package best
classifiers=[
'Development Status :: 3 - Alpha',
# Chose either "3 - Alpha", "4 - Beta" or "5 - Production/Stable" as the current state of your package
'Intended Audience :: Developers', # Define that your audience are developers
'Topic :: Software Development :: Build Tools',
'License :: OSI Approved :: MIT License', # Again, pick a license
'Programming Language :: Python :: 3', # Specify which pyhton versions that you want to support
'Programming Language :: Python :: 3.7',
],
)
\ No newline at end of file
import socks
s = socks.socksocket()
s.set_proxy(socks.SOCKS5, "localhost", 8080)
s.connect(("vk.com", 443))
s.sendall(b"GET / HTTP/1.1 ...")
print(s.recv(4096))
\ No newline at end of file
for _ in range(500):
s = socks.socksocket()
s.set_proxy(socks.SOCKS5, "localhost", 8081, username='test_user', password='Test_password')
s.connect(("127.0.0.1", 8080))
s.sendall(b"GET / HTTP/1.1 ...")
print(s.recv(4096))
\ No newline at end of file
import unittest
class ImportTest(unittest.TestCase):
def test(self):
saturn = __import__('saturn', globals=globals(), fromlist=[''])
self.assertTrue(hasattr(saturn, 'engine'))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment