Implement lan sharing

This commit is contained in:
Emran Hejazi
2026-04-22 21:11:58 +03:30
parent edd9af03a3
commit 1d48d50565
6 changed files with 227 additions and 7 deletions
+26 -1
View File
@@ -174,6 +174,29 @@ Firefox uses its own certificate store, so even after OS-level install you need
---
## LAN Sharing (Optional)
By default, the proxy only listens on `127.0.0.1` (localhost), meaning only your computer can use it. To allow other devices on your local network (LAN) to use the proxy:
1. Set `"lan_sharing": true` in your `config.json`
2. The proxy will automatically listen on all network interfaces (`0.0.0.0`)
3. The startup log will show your LAN IP addresses that other devices can connect to
**Example LAN configuration:**
```json
{
"lan_sharing": true,
"listen_host": "0.0.0.0",
"listen_port": 8085
}
```
**Security Warning:** When LAN sharing is enabled, anyone on your local network can use your proxy. Ensure your network is trusted and consider additional security measures.
**On other devices:** Configure them to use your computer's LAN IP (shown in the startup log) and port 8085 as the HTTP proxy.
---
## Modes Overview
This project focuses entirely on the **Apps Script** relay — a free Google account is all you need, no server, no VPS, no Cloudflare setup. Everything is configured out of the box for this mode.
@@ -188,8 +211,9 @@ This project focuses entirely on the **Apps Script** relay — a free Google acc
|---------|-------------|
| `auth_key` | Password shared between your computer and the relay |
| `script_id` | Your Google Apps Script Deployment ID |
| `listen_host` | Where to listen (`127.0.0.1` = only this computer) |
| `listen_host` | Where to listen (`127.0.0.1` = only this computer, `0.0.0.0` = all interfaces for LAN sharing) |
| `listen_port` | Which port to listen on (default: `8085`) |
| `lan_sharing` | Enable LAN sharing to allow other devices on your network to use the proxy (`false` by default) |
| `log_level` | How much detail to show: `DEBUG`, `INFO`, `WARNING`, `ERROR` |
### Advanced Settings
@@ -215,6 +239,7 @@ Install everything from [`requirements.txt`](requirements.txt). All listed packa
| `h2` | HTTP/2 multiplexing to the Apps Script relay (significantly faster) |
| `brotli` | Decompression of `Content-Encoding: br` responses |
| `zstandard` | Decompression of `Content-Encoding: zstd` responses |
| `netifaces` | Better network interface detection for LAN sharing (fallback available without it) |
### Load Balancing
+31 -6
View File
@@ -162,6 +162,7 @@ Firefox معمولا certificate store جداگانه دارد:
نکته امنیتی: پوشه `ca/` را با کسی به اشتراک نگذارید. اگر خواستید از اول گواهی جدید بسازید، این پوشه را حذف کنید تا دوباره ساخته شود.
---
## حالت‌های موجود
@@ -170,15 +171,38 @@ Firefox معمولا certificate store جداگانه دارد:
---
## تنظیمات مهم
## اشتراک‌گذاری در شبکه محلی (اختیاری)
به‌طور پیش‌فرض، پروکسی فقط به `127.0.0.1` (localhost) گوش می‌دهد، به این معنی که فقط کامپیوتر شما می‌تواند از آن استفاده کند. برای اینکه سایر دستگاه‌های موجود در شبکه محلی (LAN) شما بتوانند از این پروکسی استفاده کنند:
۱. در فایل `config.json` خود، مقدار `"lan_sharing"` را `true` قرار دهید.
۲. پروکسی به طور خودکار به تمام رابط‌های شبکه (`0.0.0.0`) گوش خواهد داد.
۳. در لاگ راه‌اندازی، آدرس‌های IP شبکه محلی شما که سایر دستگاه‌ها می‌توانند به آن متصل شوند، نمایش داده می‌شود.
**نمونه پیکربندی برای شبکه محلی:**
json
{
"lan_sharing": true,
"listen_host": "0.0.0.0",
"listen_port": 8085
}
**هشدار امنیتی:** وقتی اشتراک‌گذاری در شبکه محلی فعال باشد، هر کسی در شبکه محلی شما می‌تواند از پروکسی شما استفاده کند. اطمینان حاصل کنید که شبکه شما مورد اعتماد است و اقدامات امنیتی بیشتری را در نظر بگیرید.
**در سایر دستگاه‌ها:** آن‌ها را طوری پیکربندی کنید که از آدرس IP کامپیوتر شما در شبکه محلی (که در لاگ راه‌اندازی نمایش داده می‌شود) و پورت 8085 به عنوان پروکسی HTTP استفاده کنند.
---
## تنظیمات اصلی
| تنظیم | توضیح |
|------|-------|
| `auth_key` | رمز مشترک بین برنامه و رله |
| `script_id` | Deployment ID مربوط به Apps Script |
| `listen_host` | آدرس محلی برای اجرا |
| `listen_port` | پورت پراکسی |
| `log_level` | میزان جزئیات لاگ |
| `auth_key` | رمز مشترک بین کامپیوتر شما و رله |
| `script_id` | شناسه Deployment مربوط به Google Apps Script شما |
| `listen_host` | محل گوش دادن (`127.0.0.1` = فقط همین کامپیوتر، `0.0.0.0` = همه اینترفیس‌ها برای اشتراک‌گذاری LAN) |
| `listen_port` | پورتی که پروکسی روی آن اجرا می‌شود (پیش‌فرض: `8085`) |
| `lan_sharing` | فعال‌سازی اشتراک‌گذاری LAN تا دستگاه‌های دیگر در شبکه شما بتوانند از پروکسی استفاده کنند (به‌صورت پیش‌فرض `false`) |
| `log_level` | میزان جزئیات لاگ: `DEBUG`، `INFO`، `WARNING`، `ERROR` |
### تنظیمات پیشرفته
@@ -202,6 +226,7 @@ Firefox معمولا certificate store جداگانه دارد:
| `h2` | ارتباط HTTP/2 با رله Apps Script (به‌طور محسوسی سریع‌تر) |
| `brotli` | پشتیبانی از فشرده‌سازی `Content-Encoding: br` |
| `zstandard` | پشتیبانی از فشرده‌سازی `Content-Encoding: zstd` |
| `netifaces` | تشخیص بهتر اینترفیس‌های شبکه برای اشتراک‌گذاری LAN (در صورت نبود آن، حالت جایگزین در دسترس است) |
### استفاده از چند Script ID
+1
View File
@@ -10,6 +10,7 @@
"socks5_port": 1080,
"log_level": "INFO",
"verify_ssl": true,
"lan_sharing": true,
"parallel_relay": 1,
"block_hosts": [],
"bypass_hosts": [
+14
View File
@@ -22,6 +22,7 @@ if _SRC_DIR not in sys.path:
from cert_installer import install_ca, is_ca_trusted
from constants import __version__
from lan_utils import log_lan_access
from logging_utils import configure as configure_logging, print_banner
from mitm import CA_CERT_FILE
from proxy_server import ProxyServer
@@ -205,6 +206,14 @@ def main():
else:
log.info("MITM CA is already trusted.")
# ── LAN sharing configuration ────────────────────────────────────────
lan_sharing = config.get("lan_sharing", True)
if lan_sharing:
# If LAN sharing is enabled and host is still localhost, change to all interfaces
if config.get("listen_host", "127.0.0.1") == "127.0.0.1":
config["listen_host"] = "0.0.0.0"
log.info("LAN sharing enabled — listening on all interfaces")
log.info("HTTP proxy : %s:%d",
config.get("listen_host", "127.0.0.1"),
config.get("listen_port", 8080))
@@ -213,6 +222,11 @@ def main():
config.get("socks5_host", config.get("listen_host", "127.0.0.1")),
config.get("socks5_port", 1080))
# Log LAN access addresses if sharing is enabled
if lan_sharing:
socks_port = config.get("socks5_port", 1080) if config.get("socks5_enabled", True) else None
log_lan_access(config.get("listen_port", 8080), socks_port)
try:
asyncio.run(_run(config))
except KeyboardInterrupt:
+3
View File
@@ -12,3 +12,6 @@ brotli>=1.1.0
# Optional: Zstandard decompression (some CDNs now serve `zstd`)
zstandard>=0.22.0
# Optional: Better network interface detection for LAN sharing
netifaces>=0.11.0
+152
View File
@@ -0,0 +1,152 @@
"""
LAN utilities for detecting network interfaces and IP addresses.
Provides functionality to enumerate local network interfaces and their
associated IP addresses for LAN proxy sharing.
"""
import ipaddress
import logging
import socket
from typing import Dict, List, Optional
log = logging.getLogger("LAN")
def get_network_interfaces() -> Dict[str, List[str]]:
"""
Get all network interfaces and their associated IP addresses.
Returns a dictionary mapping interface names to lists of IP addresses
(both IPv4 and IPv6). Only includes interfaces with valid IP addresses
that are not loopback.
Returns:
Dict[str, List[str]]: Interface name -> list of IP addresses
"""
interfaces = {}
try:
import netifaces
for iface in netifaces.interfaces():
addrs = netifaces.ifaddresses(iface)
ips = []
# IPv4 addresses
if netifaces.AF_INET in addrs:
for addr in addrs[netifaces.AF_INET]:
ip = addr.get('addr')
if ip and not ip.startswith('127.'):
ips.append(ip)
# IPv6 addresses (without scope)
if netifaces.AF_INET6 in addrs:
for addr in addrs[netifaces.AF_INET6]:
ip = addr.get('addr')
if ip and not ip.startswith('::1') and not '%' in ip:
# Remove scope if present
ips.append(ip.split('%')[0])
if ips:
interfaces[iface] = ips
except ImportError:
# Fallback to socket method for basic detection
log.debug("netifaces not available, using socket fallback")
interfaces = _get_interfaces_socket_fallback()
return interfaces
def _get_interfaces_socket_fallback() -> Dict[str, List[str]]:
"""
Fallback method to get network interfaces using socket.
This is less comprehensive than netifaces but works without extra dependencies.
"""
interfaces = {}
try:
# Get hostname and try to resolve to IPs
hostname = socket.gethostname()
try:
# Get IPv4 addresses
ipv4_info = socket.getaddrinfo(hostname, None, socket.AF_INET)
ipv4_addrs = [info[4][0] for info in ipv4_info if not info[4][0].startswith('127.')]
if ipv4_addrs:
interfaces['primary'] = list(set(ipv4_addrs)) # Remove duplicates
except socket.gaierror:
pass
try:
# Get IPv6 addresses
ipv6_info = socket.getaddrinfo(hostname, None, socket.AF_INET6)
ipv6_addrs = []
for info in ipv6_info:
ip = info[4][0]
if not ip.startswith('::1') and not '%' in ip:
ipv6_addrs.append(ip.split('%')[0])
if ipv6_addrs:
interfaces['primary_ipv6'] = list(set(ipv6_addrs))
except socket.gaierror:
pass
except Exception as e:
log.debug("Socket fallback failed: %s", e)
return interfaces
def get_lan_ips(port: int = 8085) -> List[str]:
"""
Get list of LAN-accessible proxy addresses.
Returns a list of IP:port combinations that can be used to access
the proxy from other devices on the local network.
Args:
port: The port the proxy is listening on
Returns:
List[str]: List of "IP:port" strings for LAN access
"""
interfaces = get_network_interfaces()
lan_addresses = []
for iface_ips in interfaces.values():
for ip in iface_ips:
try:
# Validate IP and check if it's a private address
addr = ipaddress.ip_address(ip)
if addr.is_private or addr.is_link_local:
lan_addresses.append(f"{ip}:{port}")
except ValueError:
continue
# Remove duplicates while preserving order
seen = set()
unique_addresses = []
for addr in lan_addresses:
if addr not in seen:
seen.add(addr)
unique_addresses.append(addr)
return unique_addresses
def log_lan_access(port: int = 8085, socks_port: Optional[int] = None):
"""
Log the LAN-accessible proxy addresses for user convenience.
Args:
port: HTTP proxy port
socks_port: Optional SOCKS5 proxy port
"""
lan_http = get_lan_ips(port)
if lan_http:
log.info("LAN HTTP proxy : %s", ", ".join(lan_http))
else:
log.warning("No LAN IP addresses detected for HTTP proxy")
if socks_port:
lan_socks = get_lan_ips(socks_port)
if lan_socks:
log.info("LAN SOCKS5 proxy : %s", ", ".join(lan_socks))
else:
log.warning("No LAN IP addresses detected for SOCKS5 proxy")