diff --git a/Makefile b/Makefile index 887aa3b..7dc0e06 100644 --- a/Makefile +++ b/Makefile @@ -14,9 +14,14 @@ SRCS=\ server-ctl.go \ server-peer.go \ server-proxy.go \ - server-ws.go \ system.go +DATA = \ + xterm.css \ + xterm.js \ + xterm-addon-fit.js \ + xterm.html + CMD_DATA=\ cmd/tls.crt \ cmd/tls.key @@ -27,7 +32,7 @@ CMD_SRCS=\ all: $(NAME) -$(NAME): $(SRCS) $(CMD_DATA) $(CMD_SRCS) +$(NAME): $(DATA) $(SRCS) $(CMD_DATA) $(CMD_SRCS) CGO_ENABLED=0 go build -x -ldflags "-X 'main.HODU_NAME=$(NAME)' -X 'main.HODU_VERSION=$(VERSION)'" -o $@ $(CMD_SRCS) clean: @@ -44,6 +49,15 @@ hodu_grpc.pb.go: hodu.proto --go-grpc_out=. --go-grpc_opt=paths=source_relative \ hodu.proto +xterm.js: + curl -L -o "$@" https://cdn.jsdelivr.net/npm/@xterm/xterm/lib/xterm.min.js + +xterm-addon-fit.js: + curl -L -o "$@" https://cdn.jsdelivr.net/npm/xterm-addon-fit/lib/xterm-addon-fit.min.js + +xterm.css: + curl -L -o "$@" https://cdn.jsdelivr.net/npm/@xterm/xterm/css/xterm.min.css + cmd/tls.crt: openssl req -x509 -newkey rsa:4096 -keyout cmd/tls.key -out cmd/tls.crt -sha256 -days 36500 -nodes -subj "/CN=$(NAME)" --addext "subjectAltName=DNS:$(NAME),IP:10.0.0.1,IP:::1" diff --git a/client-ctl.go b/client-ctl.go index e5f28a2..ab3da04 100644 --- a/client-ctl.go +++ b/client-ctl.go @@ -1,6 +1,7 @@ package hodu import "encoding/json" +import "fmt" import "net/http" import "net/url" import "runtime" @@ -154,7 +155,7 @@ func (ctl *client_ctl_client_conns) ServeHTTP(w http.ResponseWriter, req *http.R ClientPeerAddr: r.peer_addr, ServerPeerListenAddr: r.server_peer_listen_addr.String(), ServerPeerNet: r.server_peer_net, - ServerPeerOption: r.server_peer_proto.string(), + ServerPeerOption: r.server_peer_option.string(), }) } js = append(js, json_out_client_conn{ @@ -272,7 +273,7 @@ func (ctl *client_ctl_client_conns_id) ServeHTTP(w http.ResponseWriter, req *htt ClientPeerAddr: r.peer_addr, ServerPeerListenAddr: r.server_peer_listen_addr.String(), ServerPeerNet: r.server_peer_net, - ServerPeerOption: r.server_peer_proto.string(), + ServerPeerOption: r.server_peer_option.string(), }) } js = &json_out_client_conn{ @@ -355,7 +356,7 @@ func (ctl *client_ctl_client_conns_id_routes) ServeHTTP(w http.ResponseWriter, r ClientPeerAddr: r.peer_addr, ServerPeerListenAddr: r.server_peer_listen_addr.String(), ServerPeerNet: r.server_peer_net, - ServerPeerOption: r.server_peer_proto.string(), + ServerPeerOption: r.server_peer_option.string(), }) } cts.route_mtx.Unlock() @@ -366,21 +367,28 @@ func (ctl *client_ctl_client_conns_id_routes) ServeHTTP(w http.ResponseWriter, r case http.MethodPost: var jcr json_in_client_route var r *ClientRoute - var server_peer_proto RouteOption + var server_peer_option RouteOption err = json.NewDecoder(req.Body).Decode(&jcr) - if err != nil || jcr.ClientPeerAddr == "" { + if err != nil { status_code = http.StatusBadRequest; w.WriteHeader(status_code) - goto done + goto oops } - server_peer_proto = string_to_route_proto(jcr.ServerPeerOption) - if server_peer_proto == RouteOption(ROUTE_OPTION_UNSPEC) { + if jcr.ClientPeerAddr == "" { status_code = http.StatusBadRequest; w.WriteHeader(status_code) - goto done + err = fmt.Errorf("blank client-peer-addr") + goto oops; } - r, err = cts.AddNewClientRoute(jcr.ClientPeerAddr, jcr.ServerPeerServiceAddr, jcr.ServerPeerServiceNet, server_peer_proto) + server_peer_option = string_to_route_option(jcr.ServerPeerOption) + if server_peer_option == RouteOption(ROUTE_OPTION_UNSPEC) { + status_code = http.StatusBadRequest; w.WriteHeader(status_code) + err = fmt.Errorf("wrong server-peer-option value - %d", server_peer_option) + goto oops + } + + r, err = cts.AddNewClientRoute(jcr.ClientPeerAddr, jcr.ServerPeerServiceAddr, jcr.ServerPeerServiceNet, server_peer_option) if err != nil { status_code = http.StatusInternalServerError; w.WriteHeader(status_code) if err = je.Encode(json_errmsg{Text: err.Error()}); err != nil { goto oops } @@ -468,7 +476,7 @@ func (ctl *client_ctl_client_conns_id_routes_id) ServeHTTP(w http.ResponseWriter ClientPeerAddr: r.peer_addr, ServerPeerListenAddr: r.server_peer_listen_addr.String(), ServerPeerNet: r.server_peer_net, - ServerPeerOption: r.server_peer_proto.string(), + ServerPeerOption: r.server_peer_option.string(), }) if err != nil { goto oops } diff --git a/client.go b/client.go index 89a1842..cb621f5 100644 --- a/client.go +++ b/client.go @@ -102,12 +102,12 @@ type ClientRoute struct { cts *ClientConn id RouteId peer_addr string - peer_proto RouteOption + peer_option RouteOption server_peer_listen_addr *net.TCPAddr // actual service-side service address server_peer_addr string // desired server-side service address server_peer_net string - server_peer_proto RouteOption + server_peer_option RouteOption ptc_mtx sync.Mutex ptc_map ClientPeerConnMap @@ -155,7 +155,7 @@ func (g *GuardedPacketStreamClient) Context() context.Context { }*/ // -------------------------------------------------------------------- -func NewClientRoute(cts *ClientConn, id RouteId, client_peer_addr string, server_peer_svc_addr string, server_peer_svc_net string, server_peer_proto RouteOption) *ClientRoute { +func NewClientRoute(cts *ClientConn, id RouteId, client_peer_addr string, server_peer_svc_addr string, server_peer_svc_net string, server_peer_option RouteOption) *ClientRoute { var r ClientRoute r.cts = cts @@ -164,11 +164,11 @@ func NewClientRoute(cts *ClientConn, id RouteId, client_peer_addr string, server r.ptc_cancel_map = make(ClientPeerCancelFuncMap) r.peer_addr = client_peer_addr // client-side peer // if the client_peer_addr is a domain name, it can't tell between tcp4 and tcp6 - r.peer_proto = string_to_route_proto(tcp_addr_str_class(client_peer_addr)) + r.peer_option = string_to_route_option(tcp_addr_str_class(client_peer_addr)) r.server_peer_addr = server_peer_svc_addr r.server_peer_net = server_peer_svc_net // permitted network for server-side peer - r.server_peer_proto = server_peer_proto + r.server_peer_option = server_peer_option r.stop_req.Store(false) r.stop_chan = make(chan bool, 8) @@ -257,16 +257,16 @@ func (r *ClientRoute) RunTask(wg *sync.WaitGroup) { // most useful works are triggered by ReportEvent() and done by ConnectToPeer() defer wg.Done() - err = r.cts.psc.Send(MakeRouteStartPacket(r.id, r.server_peer_proto, r.peer_addr, r.server_peer_addr, r.server_peer_net)) + err = r.cts.psc.Send(MakeRouteStartPacket(r.id, r.server_peer_option, r.peer_addr, r.server_peer_addr, r.server_peer_net)) if err != nil { r.cts.cli.log.Write(r.cts.sid, LOG_DEBUG, "Failed to send route_start for route(%d,%s,%v,%v) to %s", - r.id, r.peer_addr, r.server_peer_proto, r.server_peer_net, r.cts.remote_addr) + r.id, r.peer_addr, r.server_peer_option, r.server_peer_net, r.cts.remote_addr) goto done } else { r.cts.cli.log.Write(r.cts.sid, LOG_DEBUG, "Sent route_start for route(%d,%s,%v,%v) to %s", - r.id, r.peer_addr, r.server_peer_proto, r.server_peer_net, r.cts.remote_addr) + r.id, r.peer_addr, r.server_peer_option, r.server_peer_net, r.cts.remote_addr) } main_loop: @@ -281,15 +281,15 @@ done: r.ReqStop() r.ptc_wg.Wait() // wait for all peer tasks are finished - err = r.cts.psc.Send(MakeRouteStopPacket(r.id, r.server_peer_proto, r.peer_addr, r.server_peer_addr, r.server_peer_net)) + err = r.cts.psc.Send(MakeRouteStopPacket(r.id, r.server_peer_option, r.peer_addr, r.server_peer_addr, r.server_peer_net)) if err != nil { r.cts.cli.log.Write(r.cts.sid, LOG_DEBUG, "Failed to route_stop for route(%d,%s,%v,%v) to %s - %s", - r.id, r.peer_addr, r.server_peer_proto, r.server_peer_net, r.cts.remote_addr, err.Error()) + r.id, r.peer_addr, r.server_peer_option, r.server_peer_net, r.cts.remote_addr, err.Error()) } else { r.cts.cli.log.Write(r.cts.sid, LOG_DEBUG, "Sent route_stop for route(%d,%s,%v,%v) to %s", - r.id, r.peer_addr, r.server_peer_proto, r.server_peer_net, r.cts.remote_addr) + r.id, r.peer_addr, r.server_peer_option, r.server_peer_net, r.cts.remote_addr) } r.cts.RemoveClientRoute(r) @@ -305,7 +305,7 @@ func (r *ClientRoute) ReqStop() { } } -func (r *ClientRoute) ConnectToPeer(pts_id PeerId, route_proto RouteOption, pts_raddr string, pts_laddr string, wg *sync.WaitGroup) { +func (r *ClientRoute) ConnectToPeer(pts_id PeerId, route_option RouteOption, pts_raddr string, pts_laddr string, wg *sync.WaitGroup) { var err error var conn net.Conn var real_conn *net.TCPConn @@ -319,7 +319,7 @@ func (r *ClientRoute) ConnectToPeer(pts_id PeerId, route_proto RouteOption, pts_ var ok bool // TODO: handle TTY -// if route_proto & RouteOption(ROUTE_OPTION_TTY) it must create a pseudo-tty insteaad of connecting to tcp address +// if route_option & RouteOption(ROUTE_OPTION_TTY) it must create a pseudo-tty insteaad of connecting to tcp address // defer wg.Done() @@ -470,7 +470,7 @@ func (r *ClientRoute) ReportEvent(pts_id PeerId, event_type PACKET_KIND, event_d } } else { r.ptc_wg.Add(1) - go r.ConnectToPeer(pts_id, r.peer_proto, pd.RemoteAddrStr, pd.LocalAddrStr, &r.ptc_wg) + go r.ConnectToPeer(pts_id, r.peer_option, pd.RemoteAddrStr, pd.LocalAddrStr, &r.ptc_wg) } } @@ -735,8 +735,10 @@ func (cts *ClientConn) AddClientRoutes(peer_addrs []string) error { } option = RouteOption(ROUTE_OPTION_TCP) - // automatic determination of optioncol for common ports + // automatic determination of protocol for common ports switch port { + case "22": + option |= RouteOption(ROUTE_OPTION_SSH) case "80": option |= RouteOption(ROUTE_OPTION_HTTP) case "443": diff --git a/go.mod b/go.mod index a421e9a..84dd787 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module hodu go 1.22.0 require ( + golang.org/x/crypto v0.26.0 golang.org/x/net v0.28.0 golang.org/x/sys v0.24.0 google.golang.org/grpc v1.67.1 diff --git a/go.sum b/go.sum index 344f62a..d3ff4f8 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,13 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= diff --git a/hodu.go b/hodu.go index 30b3430..f936bcf 100644 --- a/hodu.go +++ b/hodu.go @@ -54,7 +54,7 @@ func tcp_addr_str_class(addr string) string { return "tcp" } -func word_to_route_proto(word string) RouteOption { +func word_to_route_option(word string) RouteOption { switch word { case "tcp4": return RouteOption(ROUTE_OPTION_TCP4) @@ -68,35 +68,37 @@ func word_to_route_proto(word string) RouteOption { return RouteOption(ROUTE_OPTION_HTTP) case "https": return RouteOption(ROUTE_OPTION_HTTPS) + case "ssh": + return RouteOption(ROUTE_OPTION_SSH) } return RouteOption(ROUTE_OPTION_UNSPEC) } -func string_to_route_proto(desc string) RouteOption { +func string_to_route_option(desc string) RouteOption { var fld string - var proto RouteOption + var option RouteOption var p RouteOption - proto = RouteOption(0) + option = RouteOption(0) for _, fld = range strings.Fields(desc) { - p = word_to_route_proto(fld) + p = word_to_route_option(fld) if p == RouteOption(ROUTE_OPTION_UNSPEC) { return p } - proto |= p + option |= p } - return proto + return option } -func (proto RouteOption) string() string { +func (option RouteOption) string() string { var str string - str = "" - if proto & RouteOption(ROUTE_OPTION_TCP6) != 0 { str += " tcp6" } - if proto & RouteOption(ROUTE_OPTION_TCP4) != 0 { str += " tcp4" } - if proto & RouteOption(ROUTE_OPTION_TCP) != 0 { str += " tcp" } - if proto & RouteOption(ROUTE_OPTION_TTY) != 0 { str += " tty" } - if proto & RouteOption(ROUTE_OPTION_HTTP) != 0 { str += " http" } - if proto & RouteOption(ROUTE_OPTION_HTTPS) != 0 { str += " https" } + if option & RouteOption(ROUTE_OPTION_TCP6) != 0 { str += " tcp6" } + if option & RouteOption(ROUTE_OPTION_TCP4) != 0 { str += " tcp4" } + if option & RouteOption(ROUTE_OPTION_TCP) != 0 { str += " tcp" } + if option & RouteOption(ROUTE_OPTION_TTY) != 0 { str += " tty" } + if option & RouteOption(ROUTE_OPTION_HTTP) != 0 { str += " http" } + if option & RouteOption(ROUTE_OPTION_HTTPS) != 0 { str += " https" } + if option & RouteOption(ROUTE_OPTION_SSH) != 0 { str += " ssh" } if str == "" { return str } return str[1:] // remove the leading space } diff --git a/hodu.pb.go b/hodu.pb.go index cb347e8..3dfe5b5 100644 --- a/hodu.pb.go +++ b/hodu.pb.go @@ -30,6 +30,7 @@ const ( ROUTE_OPTION_TTY ROUTE_OPTION = 8 ROUTE_OPTION_HTTP ROUTE_OPTION = 16 ROUTE_OPTION_HTTPS ROUTE_OPTION = 32 + ROUTE_OPTION_SSH ROUTE_OPTION = 64 ) // Enum value maps for ROUTE_OPTION. @@ -42,6 +43,7 @@ var ( 8: "TTY", 16: "HTTP", 32: "HTTPS", + 64: "SSH", } ROUTE_OPTION_value = map[string]int32{ "UNSPEC": 0, @@ -51,6 +53,7 @@ var ( "TTY": 8, "HTTP": 16, "HTTPS": 32, + "SSH": 64, } ) @@ -566,30 +569,30 @@ var file_hodu_proto_rawDesc = []byte{ 0x32, 0x09, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x44, 0x65, 0x73, 0x63, 0x48, 0x00, 0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x04, - 0x44, 0x61, 0x74, 0x61, 0x42, 0x03, 0x0a, 0x01, 0x55, 0x2a, 0x55, 0x0a, 0x0c, 0x52, 0x4f, 0x55, + 0x44, 0x61, 0x74, 0x61, 0x42, 0x03, 0x0a, 0x01, 0x55, 0x2a, 0x5e, 0x0a, 0x0c, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x43, 0x50, 0x34, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x43, 0x50, 0x36, 0x10, 0x04, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x54, 0x59, 0x10, 0x08, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x10, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x20, - 0x2a, 0xb5, 0x01, 0x0a, 0x0b, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, - 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0f, - 0x0a, 0x0b, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x01, 0x12, - 0x0e, 0x0a, 0x0a, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x02, 0x12, - 0x11, 0x0a, 0x0d, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, - 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x50, - 0x50, 0x45, 0x44, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x45, 0x45, 0x52, 0x5f, 0x53, 0x54, - 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x45, 0x45, 0x52, 0x5f, - 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x45, 0x45, - 0x52, 0x5f, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x50, - 0x45, 0x45, 0x52, 0x5f, 0x45, 0x4f, 0x46, 0x10, 0x08, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x45, 0x45, - 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x10, 0x09, 0x32, 0x49, 0x0a, 0x04, 0x48, 0x6f, 0x64, 0x75, - 0x12, 0x19, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x53, 0x65, 0x65, 0x64, 0x12, 0x05, 0x2e, 0x53, 0x65, - 0x65, 0x64, 0x1a, 0x05, 0x2e, 0x53, 0x65, 0x65, 0x64, 0x22, 0x00, 0x12, 0x26, 0x0a, 0x0c, 0x50, - 0x61, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x07, 0x2e, 0x50, 0x61, - 0x63, 0x6b, 0x65, 0x74, 0x1a, 0x07, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x00, 0x28, - 0x01, 0x30, 0x01, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2f, 0x68, 0x6f, 0x64, 0x75, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x07, 0x0a, 0x03, 0x53, 0x53, 0x48, 0x10, 0x40, 0x2a, 0xb5, 0x01, 0x0a, 0x0b, 0x50, 0x41, + 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x53, + 0x45, 0x52, 0x56, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x4f, 0x55, 0x54, 0x45, + 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x4f, 0x55, 0x54, + 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x4f, 0x55, 0x54, + 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x52, + 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x04, 0x12, 0x10, + 0x0a, 0x0c, 0x50, 0x45, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x05, + 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x45, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, + 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x45, 0x45, 0x52, 0x5f, 0x41, 0x42, 0x4f, 0x52, 0x54, + 0x45, 0x44, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x45, 0x45, 0x52, 0x5f, 0x45, 0x4f, 0x46, + 0x10, 0x08, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x45, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x10, + 0x09, 0x32, 0x49, 0x0a, 0x04, 0x48, 0x6f, 0x64, 0x75, 0x12, 0x19, 0x0a, 0x07, 0x47, 0x65, 0x74, + 0x53, 0x65, 0x65, 0x64, 0x12, 0x05, 0x2e, 0x53, 0x65, 0x65, 0x64, 0x1a, 0x05, 0x2e, 0x53, 0x65, + 0x65, 0x64, 0x22, 0x00, 0x12, 0x26, 0x0a, 0x0c, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x12, 0x07, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x1a, 0x07, 0x2e, + 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x08, 0x5a, 0x06, + 0x2e, 0x2f, 0x68, 0x6f, 0x64, 0x75, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/hodu.proto b/hodu.proto index 4761621..f6acec6 100644 --- a/hodu.proto +++ b/hodu.proto @@ -26,6 +26,7 @@ enum ROUTE_OPTION { TTY = 8; HTTP = 16; HTTPS = 32; + SSH = 64; }; message RouteDesc { diff --git a/server-ws.go b/server-ws.go deleted file mode 100644 index 3960c0c..0000000 --- a/server-ws.go +++ /dev/null @@ -1,43 +0,0 @@ -package hodu - -import "fmt" -import "net/http" -import "golang.org/x/net/websocket" - -type server_ctl_ws_tty struct { - s *Server - h websocket.Handler -} - -func server_ws_tty (ws* websocket.Conn) { - var msg []byte - var err error - - ws.Write([]byte("hello world\r\n")) - ws.Write([]byte("it's so wrong. it's awesome\r\n")) - ws.Write([]byte("it's so wrong. 동키가 지나간다.it's awesome\r\n")) - - for { - err = websocket.Message.Receive(ws, &msg) - if err != nil { - break - } else if len(msg) == 0 { - continue - } - -fmt.Printf ("RECEIVED MESSAGE [%v]\n", msg) - } -} - -func new_server_ctl_ws_tty(s *Server) *server_ctl_ws_tty { - return &server_ctl_ws_tty{s: s, h: websocket.Handler(server_ws_tty)} -} - -func (ctl *server_ctl_ws_tty) ServeHTTP(w http.ResponseWriter, req *http.Request) { - ctl.h.ServeHTTP(w, req) -} - - - - - diff --git a/server.go b/server.go index 4b604bb..38883ff 100644 --- a/server.go +++ b/server.go @@ -6,14 +6,13 @@ import "errors" import "fmt" import "io" import "log" -//import "math/rand" import "net" import "net/http" import "net/netip" -import "os" import "sync" import "sync/atomic" +import "golang.org/x/net/websocket" import "google.golang.org/grpc" import "google.golang.org/grpc/credentials" //import "google.golang.org/grpc/metadata" @@ -874,7 +873,6 @@ func NewServer(ctx context.Context, logger Logger, ctl_addrs []string, rpc_addrs var addr string var gl *net.TCPListener var i int - var cwd string var hs_log *log.Logger var opts []grpc.ServerOption @@ -934,10 +932,7 @@ func NewServer(ctx context.Context, logger Logger, ctl_addrs []string, rpc_addrs s.ctl_prefix = ctl_prefix s.ctl_mux = http.NewServeMux() - cwd, _ = os.Getwd() // TODO: - s.ctl_mux.Handle(s.ctl_prefix + "/ui/", http.StripPrefix(s.ctl_prefix, http.FileServer(http.Dir(cwd)))) // TODO: proper directory. it must not use the current working directory... - s.ctl_mux.Handle(s.ctl_prefix + "/ws/tty", new_server_ctl_ws_tty(&s)) s.ctl_mux.Handle(s.ctl_prefix + "/server-conns", &server_ctl_server_conns{s: &s}) s.ctl_mux.Handle(s.ctl_prefix + "/server-conns/{conn_id}", &server_ctl_server_conns_id{s: &s}) s.ctl_mux.Handle(s.ctl_prefix + "/server-conns/{conn_id}/routes", &server_ctl_server_conns_id_routes{s: &s}) @@ -959,7 +954,21 @@ func NewServer(ctx context.Context, logger Logger, ctl_addrs []string, rpc_addrs } // --------------------------------------------------------- + s.pxy_mux = http.NewServeMux() // TODO: make /_init configurable... + s.pxy_mux.Handle("/_ssh-ws/{conn_id}/{route_id}", + websocket.Handler(func(ws *websocket.Conn) { + server_proxy_serve_ssh_ws(ws, &s) + })) + s.pxy_mux.Handle("/_ssh/{conn_id}/{route_id}/", &server_proxy_xterm_file{s: &s, file: "xterm.html"}) + s.pxy_mux.Handle("/_ssh/xterm.js", &server_proxy_xterm_file{s: &s, file: "xterm.js"}) + s.pxy_mux.Handle("/_ssh/xterm-addon-fit.js", &server_proxy_xterm_file{s: &s, file: "xterm-addon-fit.js"}) + s.pxy_mux.Handle("/_ssh/xterm.css", &server_proxy_xterm_file{s: &s, file: "xterm.css"}) + s.pxy_mux.Handle("/_ssh/", &server_proxy_xterm_file{s: &s, file: "_forbidden"}) + + //cwd, _ = os.Getwd() // TODO: + //s.pxy_mux.Handle(s.ctl_prefix + "/ui/", http.StripPrefix(s.ctl_prefix, http.FileServer(http.Dir(cwd)))) // TODO: proper directory. it must not use the current working directory... + s.pxy_mux.Handle("/_init/{conn_id}/{route_id}/{trailer...}", &server_proxy_http_init{s: &s}) s.pxy_mux.Handle("/", &server_proxy_http_main{s: &s}) diff --git a/xterm-addon-fit.js b/xterm-addon-fit.js new file mode 100644 index 0000000..7384f10 --- /dev/null +++ b/xterm-addon-fit.js @@ -0,0 +1,8 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue("height")),s=Math.max(0,parseInt(i.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=s-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})())); +//# sourceMappingURL=xterm-addon-fit.js.map \ No newline at end of file diff --git a/xterm.css b/xterm.css new file mode 100644 index 0000000..f8fcefc --- /dev/null +++ b/xterm.css @@ -0,0 +1,8 @@ +/** + * Minified by jsDelivr using clean-css v5.3.2. + * Original file: /npm/@xterm/xterm@5.5.0/css/xterm.css + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:0}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm .xterm-cursor-pointer,.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative} +/*# sourceMappingURL=/sm/97377c0c258e109358121823f5790146c714989366481f90e554c42277efb500.map */ \ No newline at end of file diff --git a/xterm.html b/xterm.html new file mode 100644 index 0000000..8456d6b --- /dev/null +++ b/xterm.html @@ -0,0 +1,76 @@ + + +
+ + + +