%global _empty_manifest_terminate_build 0
Name:		python-siosocks
Version:	0.3.0
Release:	1
Summary:	sans-io socks proxy client/server with couple io backends
License:	MIT
URL:		https://github.com/pohmelie/siosocks
Source0:	https://mirrors.nju.edu.cn/pypi/web/packages/d8/63/29311fe4bed90e6dfd902a87c6d0fb77244d5abfb9b876344693b40d5c53/siosocks-0.3.0.tar.gz
BuildArch:	noarch

Requires:	python3-pytest
Requires:	python3-pytest-cov
Requires:	python3-pytest-asyncio
Requires:	python3-trio
Requires:	python3-pytest-trio
Requires:	python3-trio

%description
asyncio | + | +
trio | + | +
socket | | +
Feel free to make it bigger :wink:
# Usage
End user implementations mimic «parent» library api.
## Client
- asyncio: [`open_connection`](https://docs.python.org/3/library/asyncio-stream.html#asyncio.open_connection)
- trio: [`open_tcp_stream`](https://trio.readthedocs.io/en/stable/reference-io.html#trio.open_tcp_stream)
Extra keyword-only arguments:
- `socks_host`: string
- `socks_port`: integer
- `socks_version`: integer (4 or 5)
- `username`: optional string (default: `None`)
- `password`: optional string (default: `None`)
- `encoding`: optional string (default: `"utf-8"`)
- `socks4_extras`: optional dictionary
- `socks5_extras`: optional dictionary
Extras:
- socks4
    - `user_id`: string (default: `""`)
- socks5
    - None at this moment, added for uniform api
## Server
End user implementations mimic «parent» library server request handlers.
- asyncio: [`start_server`](https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server)
- trio: [`serve_tcp`](https://trio.readthedocs.io/en/stable/reference-io.html#trio.serve_tcp)
- socketserver: [`ThreadingTCPServer`](https://docs.python.org/3/library/socketserver.html#socketserver.ThreadingTCPServer)
You should use [`partial`](https://docs.python.org/3/library/functools.html#functools.partial) to bind socks specific arguments:
- `allowed_versions`: set of integers (default: `{4, 5}`)
- `username`: optional string (default: `None`)
- `password`: optional string (default: `None`)
- `strict_security_policy`: boolean, if `True` exception will be raised if authentication required and 4 is in allowed versions set (default: `True`)
- `encoding`: optional string (default: `"utf-8"`)
Nothing to say more. Typical usage can be found at [`__main__.py`](https://github.com/pohmelie/siosocks/blob/master/siosocks/__main__.py)
# Examples
## High-level
This section will use `asyncio` as backend, since it is main target/reason for `siosocks`
### Client
``` python
import asyncio
from siosocks.io.asyncio import open_connection
HOST = "api.ipify.org"
REQ = """GET /?format=json HTTP/1.1
Host: api.ipify.org
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,en-US;q=0.7,ru;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
"""
async def main():
    # assume we have tor started
    r, w = await open_connection(HOST, 80, socks_host="localhost", socks_port=9050, socks_version=5)
    w.write(REQ.replace("\n", "\r\n").encode())
    await w.drain()
    print(await r.read(8192))
    w.close()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
```
### Server
``` python
import socket
import asyncio
import contextlib
from siosocks.io.asyncio import socks_server_handler
loop = asyncio.get_event_loop()
server = loop.run_until_complete(asyncio.start_server(socks_server_handler, port=1080))
addresses = []
for sock in server.sockets:
    if sock.family in (socket.AF_INET, socket.AF_INET6):
        host, port, *_ = sock.getsockname()
        addresses.append(f"{host}:{port}")
print(f"Socks4/5 proxy serving on {', '.join(addresses)}")
with contextlib.suppress(KeyboardInterrupt):
    loop.run_forever()
```
But if you just want one-shot socks server then try:
``` bash
python -m siosocks
```
This will start socks 4, 5 server on all interfaces on 1080 port. For more information try `--help`
``` bash
python -m siosocks --help
usage: siosocks [-h] [--backend {asyncio,socketserver,trio}] [--host HOST]
                [--port PORT] [--family {ipv4,ipv6,auto}] [--socks SOCKS]
                [--username USERNAME] [--password PASSWORD]
                [--encoding ENCODING] [--no-strict] [-v]
Socks proxy server
optional arguments:
  -h, --help            show this help message and exit
  --backend {asyncio,socketserver,trio}
                        Socks server backend [default: asyncio]
  --host HOST           Socks server host [default: None]
  --port PORT           Socks server port [default: 1080]
  --family {ipv4,ipv6,auto}
                        Socket family [default: auto]
  --socks SOCKS         Socks protocol version [default: []]
  --username USERNAME   Socks auth username [default: None]
  --password PASSWORD   Socks auth password [default: None]
  --encoding ENCODING   String encoding [default: utf-8]
  --no-strict           Allow multiversion socks server, when socks5 used with
                        username/password auth [default: False]
  -v, --version         Show siosocks version
```
### Exceptions
`siosocks` have unified exception for all types of socks-related errors:
``` python
import asyncio
from siosocks.exceptions import SocksException
from siosocks.io.asyncio import open_connection
async def main():
    try:
        r, w = await open_connection("127.0.0.1", 80, socks_host="localhost", socks_port=9050, socks_version=5)
    except SocksException:
    else:
        # at this point all socks-related tasks done and returned reader and writer
        # are just plain asyncio objects without any siosocks proxies
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
```
## Low-level
Shadowsocks-like [client/server](https://github.com/pohmelie/siosocks/blob/master/examples/shadowsocks-like.py). Shadowsocks-like built on top of socks5 and encryption. It have «client», which is actually socks server and «server». So, precisely there are two servers: client side and server side. Purpose of shadowsocks is to encrypt data between «incoming» and «outgoing» servers. In common this looks like:
```
client (non-encrypted socks) «incoming» socks server (encrypted socks) «outgoing» socks server (non-socks connection) target server
```
Example above use Caesar cipher for simplicity (and security of course).
# Roadmap/contibutions
- [ ] add more backends (average)
- [ ] speed up `passthrough` implementation (seems hard)
- [ ] add client redirection

%package -n python3-siosocks
Summary:	sans-io socks proxy client/server with couple io backends
Provides:	python-siosocks
BuildRequires:	python3-devel
BuildRequires:	python3-setuptools
BuildRequires:	python3-pip
%description -n python3-siosocks
asyncio | + | +
trio | + | +
socket | | +
Feel free to make it bigger :wink:
# Usage
End user implementations mimic «parent» library api.
## Client
- asyncio: [`open_connection`](https://docs.python.org/3/library/asyncio-stream.html#asyncio.open_connection)
- trio: [`open_tcp_stream`](https://trio.readthedocs.io/en/stable/reference-io.html#trio.open_tcp_stream)
Extra keyword-only arguments:
- `socks_host`: string
- `socks_port`: integer
- `socks_version`: integer (4 or 5)
- `username`: optional string (default: `None`)
- `password`: optional string (default: `None`)
- `encoding`: optional string (default: `"utf-8"`)
- `socks4_extras`: optional dictionary
- `socks5_extras`: optional dictionary
Extras:
- socks4
    - `user_id`: string (default: `""`)
- socks5
    - None at this moment, added for uniform api
## Server
End user implementations mimic «parent» library server request handlers.
- asyncio: [`start_server`](https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server)
- trio: [`serve_tcp`](https://trio.readthedocs.io/en/stable/reference-io.html#trio.serve_tcp)
- socketserver: [`ThreadingTCPServer`](https://docs.python.org/3/library/socketserver.html#socketserver.ThreadingTCPServer)
You should use [`partial`](https://docs.python.org/3/library/functools.html#functools.partial) to bind socks specific arguments:
- `allowed_versions`: set of integers (default: `{4, 5}`)
- `username`: optional string (default: `None`)
- `password`: optional string (default: `None`)
- `strict_security_policy`: boolean, if `True` exception will be raised if authentication required and 4 is in allowed versions set (default: `True`)
- `encoding`: optional string (default: `"utf-8"`)
Nothing to say more. Typical usage can be found at [`__main__.py`](https://github.com/pohmelie/siosocks/blob/master/siosocks/__main__.py)
# Examples
## High-level
This section will use `asyncio` as backend, since it is main target/reason for `siosocks`
### Client
``` python
import asyncio
from siosocks.io.asyncio import open_connection
HOST = "api.ipify.org"
REQ = """GET /?format=json HTTP/1.1
Host: api.ipify.org
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,en-US;q=0.7,ru;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
"""
async def main():
    # assume we have tor started
    r, w = await open_connection(HOST, 80, socks_host="localhost", socks_port=9050, socks_version=5)
    w.write(REQ.replace("\n", "\r\n").encode())
    await w.drain()
    print(await r.read(8192))
    w.close()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
```
### Server
``` python
import socket
import asyncio
import contextlib
from siosocks.io.asyncio import socks_server_handler
loop = asyncio.get_event_loop()
server = loop.run_until_complete(asyncio.start_server(socks_server_handler, port=1080))
addresses = []
for sock in server.sockets:
    if sock.family in (socket.AF_INET, socket.AF_INET6):
        host, port, *_ = sock.getsockname()
        addresses.append(f"{host}:{port}")
print(f"Socks4/5 proxy serving on {', '.join(addresses)}")
with contextlib.suppress(KeyboardInterrupt):
    loop.run_forever()
```
But if you just want one-shot socks server then try:
``` bash
python -m siosocks
```
This will start socks 4, 5 server on all interfaces on 1080 port. For more information try `--help`
``` bash
python -m siosocks --help
usage: siosocks [-h] [--backend {asyncio,socketserver,trio}] [--host HOST]
                [--port PORT] [--family {ipv4,ipv6,auto}] [--socks SOCKS]
                [--username USERNAME] [--password PASSWORD]
                [--encoding ENCODING] [--no-strict] [-v]
Socks proxy server
optional arguments:
  -h, --help            show this help message and exit
  --backend {asyncio,socketserver,trio}
                        Socks server backend [default: asyncio]
  --host HOST           Socks server host [default: None]
  --port PORT           Socks server port [default: 1080]
  --family {ipv4,ipv6,auto}
                        Socket family [default: auto]
  --socks SOCKS         Socks protocol version [default: []]
  --username USERNAME   Socks auth username [default: None]
  --password PASSWORD   Socks auth password [default: None]
  --encoding ENCODING   String encoding [default: utf-8]
  --no-strict           Allow multiversion socks server, when socks5 used with
                        username/password auth [default: False]
  -v, --version         Show siosocks version
```
### Exceptions
`siosocks` have unified exception for all types of socks-related errors:
``` python
import asyncio
from siosocks.exceptions import SocksException
from siosocks.io.asyncio import open_connection
async def main():
    try:
        r, w = await open_connection("127.0.0.1", 80, socks_host="localhost", socks_port=9050, socks_version=5)
    except SocksException:
    else:
        # at this point all socks-related tasks done and returned reader and writer
        # are just plain asyncio objects without any siosocks proxies
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
```
## Low-level
Shadowsocks-like [client/server](https://github.com/pohmelie/siosocks/blob/master/examples/shadowsocks-like.py). Shadowsocks-like built on top of socks5 and encryption. It have «client», which is actually socks server and «server». So, precisely there are two servers: client side and server side. Purpose of shadowsocks is to encrypt data between «incoming» and «outgoing» servers. In common this looks like:
```
client (non-encrypted socks) «incoming» socks server (encrypted socks) «outgoing» socks server (non-socks connection) target server
```
Example above use Caesar cipher for simplicity (and security of course).
# Roadmap/contibutions
- [ ] add more backends (average)
- [ ] speed up `passthrough` implementation (seems hard)
- [ ] add client redirection

%package help
Summary:	Development documents and examples for siosocks
Provides:	python3-siosocks-doc
%description help
asyncio | + | +
trio | + | +
socket | | +
Feel free to make it bigger :wink:
# Usage
End user implementations mimic «parent» library api.
## Client
- asyncio: [`open_connection`](https://docs.python.org/3/library/asyncio-stream.html#asyncio.open_connection)
- trio: [`open_tcp_stream`](https://trio.readthedocs.io/en/stable/reference-io.html#trio.open_tcp_stream)
Extra keyword-only arguments:
- `socks_host`: string
- `socks_port`: integer
- `socks_version`: integer (4 or 5)
- `username`: optional string (default: `None`)
- `password`: optional string (default: `None`)
- `encoding`: optional string (default: `"utf-8"`)
- `socks4_extras`: optional dictionary
- `socks5_extras`: optional dictionary
Extras:
- socks4
    - `user_id`: string (default: `""`)
- socks5
    - None at this moment, added for uniform api
## Server
End user implementations mimic «parent» library server request handlers.
- asyncio: [`start_server`](https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server)
- trio: [`serve_tcp`](https://trio.readthedocs.io/en/stable/reference-io.html#trio.serve_tcp)
- socketserver: [`ThreadingTCPServer`](https://docs.python.org/3/library/socketserver.html#socketserver.ThreadingTCPServer)
You should use [`partial`](https://docs.python.org/3/library/functools.html#functools.partial) to bind socks specific arguments:
- `allowed_versions`: set of integers (default: `{4, 5}`)
- `username`: optional string (default: `None`)
- `password`: optional string (default: `None`)
- `strict_security_policy`: boolean, if `True` exception will be raised if authentication required and 4 is in allowed versions set (default: `True`)
- `encoding`: optional string (default: `"utf-8"`)
Nothing to say more. Typical usage can be found at [`__main__.py`](https://github.com/pohmelie/siosocks/blob/master/siosocks/__main__.py)
# Examples
## High-level
This section will use `asyncio` as backend, since it is main target/reason for `siosocks`
### Client
``` python
import asyncio
from siosocks.io.asyncio import open_connection
HOST = "api.ipify.org"
REQ = """GET /?format=json HTTP/1.1
Host: api.ipify.org
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,en-US;q=0.7,ru;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
"""
async def main():
    # assume we have tor started
    r, w = await open_connection(HOST, 80, socks_host="localhost", socks_port=9050, socks_version=5)
    w.write(REQ.replace("\n", "\r\n").encode())
    await w.drain()
    print(await r.read(8192))
    w.close()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
```
### Server
``` python
import socket
import asyncio
import contextlib
from siosocks.io.asyncio import socks_server_handler
loop = asyncio.get_event_loop()
server = loop.run_until_complete(asyncio.start_server(socks_server_handler, port=1080))
addresses = []
for sock in server.sockets:
    if sock.family in (socket.AF_INET, socket.AF_INET6):
        host, port, *_ = sock.getsockname()
        addresses.append(f"{host}:{port}")
print(f"Socks4/5 proxy serving on {', '.join(addresses)}")
with contextlib.suppress(KeyboardInterrupt):
    loop.run_forever()
```
But if you just want one-shot socks server then try:
``` bash
python -m siosocks
```
This will start socks 4, 5 server on all interfaces on 1080 port. For more information try `--help`
``` bash
python -m siosocks --help
usage: siosocks [-h] [--backend {asyncio,socketserver,trio}] [--host HOST]
                [--port PORT] [--family {ipv4,ipv6,auto}] [--socks SOCKS]
                [--username USERNAME] [--password PASSWORD]
                [--encoding ENCODING] [--no-strict] [-v]
Socks proxy server
optional arguments:
  -h, --help            show this help message and exit
  --backend {asyncio,socketserver,trio}
                        Socks server backend [default: asyncio]
  --host HOST           Socks server host [default: None]
  --port PORT           Socks server port [default: 1080]
  --family {ipv4,ipv6,auto}
                        Socket family [default: auto]
  --socks SOCKS         Socks protocol version [default: []]
  --username USERNAME   Socks auth username [default: None]
  --password PASSWORD   Socks auth password [default: None]
  --encoding ENCODING   String encoding [default: utf-8]
  --no-strict           Allow multiversion socks server, when socks5 used with
                        username/password auth [default: False]
  -v, --version         Show siosocks version
```
### Exceptions
`siosocks` have unified exception for all types of socks-related errors:
``` python
import asyncio
from siosocks.exceptions import SocksException
from siosocks.io.asyncio import open_connection
async def main():
    try:
        r, w = await open_connection("127.0.0.1", 80, socks_host="localhost", socks_port=9050, socks_version=5)
    except SocksException:
    else:
        # at this point all socks-related tasks done and returned reader and writer
        # are just plain asyncio objects without any siosocks proxies
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
```
## Low-level
Shadowsocks-like [client/server](https://github.com/pohmelie/siosocks/blob/master/examples/shadowsocks-like.py). Shadowsocks-like built on top of socks5 and encryption. It have «client», which is actually socks server and «server». So, precisely there are two servers: client side and server side. Purpose of shadowsocks is to encrypt data between «incoming» and «outgoing» servers. In common this looks like:
```
client (non-encrypted socks) «incoming» socks server (encrypted socks) «outgoing» socks server (non-socks connection) target server
```
Example above use Caesar cipher for simplicity (and security of course).
# Roadmap/contibutions
- [ ] add more backends (average)
- [ ] speed up `passthrough` implementation (seems hard)
- [ ] add client redirection

%prep
%autosetup -n siosocks-0.3.0

%build
%py3_build

%install
%py3_install
install -d -m755 %{buildroot}/%{_pkgdocdir}
if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
pushd %{buildroot}
if [ -d usr/lib ]; then
	find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/lib64 ]; then
	find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/bin ]; then
	find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/sbin ]; then
	find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
fi
touch doclist.lst
if [ -d usr/share/man ]; then
	find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
fi
popd
mv %{buildroot}/filelist.lst .
mv %{buildroot}/doclist.lst .

%files -n python3-siosocks -f filelist.lst
%dir %{python3_sitelib}/*

%files help -f doclist.lst
%{_docdir}/*

%changelog
* Fri Apr 21 2023 Python_Bot <Python_Bot@openeuler.org> - 0.3.0-1
- Package Spec generated