Files
p1neappleXpress d3d67eff8b Mode without a server: a free PHP exit on any hosting (stream mux, flow control, installer, full tunnel) (#137)
The exit is a small PHP program on an ordinary web hosting, reached through cups.online or Mail.ru. See the PR for the list.
2026-10-01 01:49:46 +03:00

102 lines
2.8 KiB
Go

// Command phpboxfetch dials one destination through a phpbox exit and prints
// the response - a manual end-to-end check of the transport/phpbox client
// against a running deploy/phpbox/phpbox.php.
//
// Local proof (no anti-bot in the way):
//
// PHPBOX_ALLOW_PRIVATE=1 PHP_CLI_SERVER_WORKERS=6 \
// php -S 127.0.0.1:8085 deploy/phpbox/phpbox.php &
// go run ./deploy/phpbox/cmd/phpboxfetch \
// -url http://127.0.0.1:8085/phpbox.php -token CHANGE-ME \
// -target https://icanhazip.com/
//
// Against a real host the client must first pass whatever bot check the host
// puts in front of PHP (e.g. InfinityFree serves a JS challenge to non-
// browsers); that is a separate problem from the tunnel itself.
package main
import (
"bufio"
"context"
"crypto/tls"
"flag"
"fmt"
"io"
"net/url"
"os"
"strconv"
"time"
"github.com/p1neappleXpress/OpenFlux/transport/phpbox"
)
func main() {
endpoint := flag.String("url", "", "phpbox.php URL, e.g. http://127.0.0.1:8085/phpbox.php")
token := flag.String("token", "CHANGE-ME", "phpbox token (PHPBOX_TOKEN on the exit)")
target := flag.String("target", "https://icanhazip.com/", "URL to fetch through the exit")
timeout := flag.Duration("timeout", 20*time.Second, "overall timeout")
flag.Parse()
if *endpoint == "" {
fmt.Fprintln(os.Stderr, "-url is required")
os.Exit(2)
}
u, err := url.Parse(*target)
if err != nil || u.Host == "" {
fmt.Fprintln(os.Stderr, "bad -target:", err)
os.Exit(2)
}
host := u.Hostname()
port := 80
if u.Scheme == "https" {
port = 443
}
if p := u.Port(); p != "" {
port, _ = strconv.Atoi(p)
}
sess := "fetch" + strconv.FormatInt(time.Now().UnixNano(), 36)
c := phpbox.NewClient(*endpoint, *token, sess)
defer c.Close()
ctx, cancel := context.WithTimeout(context.Background(), *timeout)
defer cancel()
conn, err := c.Dial(ctx, host, port)
if err != nil {
fmt.Fprintln(os.Stderr, "dial through phpbox:", err)
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "opened stream to %s:%d through %s\n", host, port, *endpoint)
var rw io.ReadWriter = conn
if u.Scheme == "https" {
tc := tls.Client(conn, &tls.Config{ServerName: host})
if err := tc.Handshake(); err != nil {
fmt.Fprintln(os.Stderr, "TLS handshake:", err)
os.Exit(1)
}
rw = tc
}
path := u.RequestURI()
fmt.Fprintf(rw, "GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: phpboxfetch\r\nAccept: */*\r\nConnection: close\r\n\r\n", path, host)
br := bufio.NewReader(rw)
status, err := br.ReadString('\n')
if err != nil {
fmt.Fprintln(os.Stderr, "read status:", err)
os.Exit(1)
}
fmt.Print("HTTP status: ", status)
// Skip headers, print the body (icanhazip returns just the IP).
for {
line, err := br.ReadString('\n')
if err != nil || line == "\r\n" || line == "\n" {
break
}
}
body, _ := io.ReadAll(br)
fmt.Printf("body: %s\n", body)
}