init
This commit is contained in:
commit
98f555391f
|
@ -0,0 +1,2 @@
|
||||||
|
bin
|
||||||
|
.swp
|
|
@ -0,0 +1,2 @@
|
||||||
|
bin
|
||||||
|
.swap
|
|
@ -0,0 +1,27 @@
|
||||||
|
tidy:
|
||||||
|
@GOPROXY=https://goproxy.cn go mod tidy
|
||||||
|
|
||||||
|
build: tidy
|
||||||
|
@go build -o bin/nip cmd/main.go
|
||||||
|
|
||||||
|
run-server: build
|
||||||
|
@NIP_SERVER_ADDR=127.0.0.1:8000 \
|
||||||
|
NIP_SERVER_TOKEN=xxx \
|
||||||
|
./bin/nip server
|
||||||
|
|
||||||
|
run-agent: build
|
||||||
|
@NIP_AGENT_ADDR=127.0.0.1:10080 \
|
||||||
|
NIP_AGENT_TOKEN=xxx \
|
||||||
|
NIP_AGENT_SERVER_ADDR=127.0.0.1:8000 \
|
||||||
|
./bin/nip agent
|
||||||
|
|
||||||
|
install: build
|
||||||
|
@yes | cp ./bin/nip /usr/bin/nip
|
||||||
|
@chmod +x /usr/bin/nip
|
||||||
|
@yes | cp scripts/nip-server.service /usr/lib/systemd/system/
|
||||||
|
@yes | cp scripts/nip-agent.service /usr/lib/systemd/system/
|
||||||
|
@systemctl daemon-reload
|
||||||
|
|
||||||
|
test:
|
||||||
|
@curl -p -x http://127.0.0.1:10080 https://myip.ipip.net -vv
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.lovezsh.com/lovezsh/nip/internal/agent"
|
||||||
|
"git.lovezsh.com/lovezsh/nip/internal/server"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := &cli.App{
|
||||||
|
Commands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "server",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "addr",
|
||||||
|
Usage: "listen address",
|
||||||
|
Value: ":3000",
|
||||||
|
EnvVars: []string{"NIP_SERVER_ADDR"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "token",
|
||||||
|
Required: true,
|
||||||
|
EnvVars: []string{"NIP_SERVER_TOKEN"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
s := server.New(ctx.String("token"), ctx.String("addr"))
|
||||||
|
log.Printf("server listen at: %s", ctx.String("addr"))
|
||||||
|
return s.ListenAndServe()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "agent",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "addr",
|
||||||
|
Usage: "listen address",
|
||||||
|
Value: ":4000",
|
||||||
|
EnvVars: []string{"NIP_AGENT_ADDR"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "token",
|
||||||
|
Required: true,
|
||||||
|
EnvVars: []string{"NIP_AGENT_TOKEN"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "server",
|
||||||
|
Value: "server address",
|
||||||
|
Required: true,
|
||||||
|
EnvVars: []string{"NIP_AGENT_SERVER_ADDR"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
a := agent.New(ctx.String("addr"), ctx.String("token"), ctx.String("server"))
|
||||||
|
log.Printf("agent listen at %s", ctx.String("addr"))
|
||||||
|
log.Printf("using proxy server %s", ctx.String("server"))
|
||||||
|
return a.ListenAndServe()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
module git.lovezsh.com/lovezsh/nip
|
||||||
|
|
||||||
|
go 1.22.4
|
||||||
|
|
||||||
|
require github.com/urfave/cli/v2 v2.27.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,8 @@
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=
|
||||||
|
github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
|
@ -0,0 +1,21 @@
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.lovezsh.com/lovezsh/nip/internal/forward"
|
||||||
|
"git.lovezsh.com/lovezsh/nip/internal/pkg/httptunnel"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Agent struct {
|
||||||
|
httptunnel *httptunnel.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(addr string, token string, server string) *Agent {
|
||||||
|
forwarder := forward.New(server, token)
|
||||||
|
return &Agent{
|
||||||
|
httptunnel: httptunnel.New(addr, forwarder.Forward),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (agent *Agent) ListenAndServe() (err error) {
|
||||||
|
return agent.httptunnel.ListenAndServe()
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package forward
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"git.lovezsh.com/lovezsh/nip/internal/pkg/encoding"
|
||||||
|
"git.lovezsh.com/lovezsh/nip/internal/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Forwarder interface {
|
||||||
|
Forward(net.Conn, string) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type forwarder struct {
|
||||||
|
server string
|
||||||
|
token string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(server string, token string) Forwarder {
|
||||||
|
return &forwarder{server, token}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *forwarder) Forward(src net.Conn, target string) (err error) {
|
||||||
|
dst, err := net.Dial("tcp", f.server)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dst.Close()
|
||||||
|
|
||||||
|
req := proto.Request{
|
||||||
|
Target: target,
|
||||||
|
Token: f.token,
|
||||||
|
}
|
||||||
|
if err = encoding.NewEncoder(dst).Encode(&req); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp proto.Response
|
||||||
|
if err = encoding.NewDecoder(dst).Decode(&resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Code != 0 {
|
||||||
|
return errors.New(resp.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan error)
|
||||||
|
go func() {
|
||||||
|
_, err := io.Copy(dst, src)
|
||||||
|
ch <- err
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
_, err := io.Copy(src, dst)
|
||||||
|
ch <- err
|
||||||
|
}()
|
||||||
|
return <-ch
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package encoding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type decoder struct {
|
||||||
|
r io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDecoder(r io.Reader) Decoder {
|
||||||
|
return &decoder{r}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) Decode(v interface{}) (err error) {
|
||||||
|
return json.NewDecoder(d.r).Decode(&v)
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package encoding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type encoder struct {
|
||||||
|
w io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEncoder(w io.Writer) Encoder {
|
||||||
|
return &encoder{w}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *encoder) Encode(v interface{}) (err error) {
|
||||||
|
return json.NewEncoder(e.w).Encode(&v)
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package encoding
|
||||||
|
|
||||||
|
type Decoder interface {
|
||||||
|
Decode(v interface{}) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Encoder interface {
|
||||||
|
Encode(v interface{}) (err error)
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package httptunnel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
addr string
|
||||||
|
forward func(src net.Conn, dst string) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(addr string, forward func(src net.Conn, dst string) (err error)) *Server {
|
||||||
|
return &Server{
|
||||||
|
addr,
|
||||||
|
forward,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ListenAndServe() (err error) {
|
||||||
|
l, err := net.Listen("tcp", s.addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go s.Serve(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Parse(conn net.Conn) (target string, err error) {
|
||||||
|
reader := bufio.NewReader(conn)
|
||||||
|
line, _, err := reader.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
terms := strings.Split(string(line), " ")
|
||||||
|
if len(terms) != 3 {
|
||||||
|
return "", errors.New("bad protocol")
|
||||||
|
}
|
||||||
|
if terms[0] != http.MethodConnect {
|
||||||
|
return "", errors.New("method is not connect")
|
||||||
|
}
|
||||||
|
_, err = conn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n"))
|
||||||
|
return terms[1], err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Serve(conn net.Conn) {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
target, err := s.Parse(conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to parse http tunnel protocol error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.forward(conn, target); err != nil {
|
||||||
|
log.Printf("http tunnel server forward request error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package proto
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Target string
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Code int
|
||||||
|
Message string
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"git.lovezsh.com/lovezsh/nip/internal/pkg/encoding"
|
||||||
|
"git.lovezsh.com/lovezsh/nip/internal/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
token string
|
||||||
|
addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(token string, addr string) *Server {
|
||||||
|
return &Server{
|
||||||
|
token: token,
|
||||||
|
addr: addr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ListenAndServe() (err error) {
|
||||||
|
l, err := net.Listen("tcp", s.addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go s.Serve(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handshake(conn net.Conn) (target net.Conn, err error) {
|
||||||
|
var req proto.Request
|
||||||
|
if err = encoding.NewDecoder(conn).Decode(&req); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Token != s.token {
|
||||||
|
encoding.NewEncoder(conn).Encode(&proto.Response{Code: 1, Message: "token invalid"})
|
||||||
|
return nil, errors.New("token invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
target, err = net.Dial("tcp", req.Target)
|
||||||
|
if err != nil {
|
||||||
|
encoding.NewEncoder(conn).Encode(&proto.Response{Code: 2, Message: err.Error()})
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = encoding.NewEncoder(conn).Encode(&proto.Response{Code: 0}); err != nil {
|
||||||
|
target.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return target, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Serve(conn net.Conn) {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
dst, err := s.handshake(conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("handshake error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer dst.Close()
|
||||||
|
|
||||||
|
ch := make(chan error)
|
||||||
|
go func() {
|
||||||
|
_, err := io.Copy(dst, conn)
|
||||||
|
ch <- err
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
_, err := io.Copy(conn, dst)
|
||||||
|
ch <- err
|
||||||
|
}()
|
||||||
|
if err := <-ch; err != nil {
|
||||||
|
log.Printf("data stream copy error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
[Unit]
|
||||||
|
Description="NIP agent"
|
||||||
|
After=network.target
|
||||||
|
Wants=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
Environment=NIP_AGENT_ADRR=127.0.0.1:10080
|
||||||
|
Environment=NIP_AGENT_TOKEN=xxx
|
||||||
|
Environment=NIP_AGENT_SERVER_ADDR=127.0.0.1:3000
|
||||||
|
ExecStart=/usr/bin/nip agent
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
[Unit]
|
||||||
|
Description="NIP server"
|
||||||
|
After=network.target
|
||||||
|
Wants=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
Environment=NIP_SERVER_ADRR=:3000
|
||||||
|
Environment=NIP_SERVER_TOKEN=xxx
|
||||||
|
ExecStart=/usr/bin/nip server
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
Loading…
Reference in New Issue