From f1572f9321e4e757e9d2833dc9f608ee9733f52a Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Fri, 27 Dec 2024 14:43:44 +0900 Subject: [PATCH] enhanced for wpx response transformer --- Makefile | 8 ++- go.mod | 6 +-- go.sum | 4 +- server-ctl.go | 23 ++++---- server-proxy.go | 137 +++++++++++++++++++++++++++--------------------- server.go | 110 ++++++++++++++++++++++++++++++-------- system.go | 2 +- transform.go | 82 +++++++++++++++++++++++++++++ xterm.html | 6 ++- 9 files changed, 277 insertions(+), 101 deletions(-) create mode 100644 transform.go diff --git a/Makefile b/Makefile index 680eed9..230c8fb 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,8 @@ SRCS=\ server-ctl.go \ server-peer.go \ server-proxy.go \ - system.go + system.go \ + transform.go \ DATA = \ xterm.css \ @@ -40,7 +41,10 @@ CMD_SRCS=\ all: $(NAME) $(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) + ##go build -buildmode=plugin -o modres.so hook/modres.go + ##CGO_ENABLED=0 go build -x -ldflags "-X 'main.HODU_NAME=$(NAME)' -X 'main.HODU_VERSION=$(VERSION)'" -o $@ $(CMD_SRCS) + CGO_ENABLED=1 go build -x -ldflags "-X 'main.HODU_NAME=$(NAME)' -X 'main.HODU_VERSION=$(VERSION)'" -o $@ $(CMD_SRCS) + ##CGO_ENABLED=1 go build -x -ldflags "-X 'main.HODU_NAME=$(NAME)' -X 'main.HODU_VERSION=$(VERSION)' -linkmode external -extldflags=-static" -o $@ $(CMD_SRCS) clean: go clean -x -i diff --git a/go.mod b/go.mod index 84dd787..c94f297 100644 --- a/go.mod +++ b/go.mod @@ -6,12 +6,10 @@ require ( golang.org/x/crypto v0.26.0 golang.org/x/net v0.28.0 golang.org/x/sys v0.24.0 + golang.org/x/text v0.21.0 google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 ) -require ( - golang.org/x/text v0.17.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect -) +require google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect diff --git a/go.sum b/go.sum index d3ff4f8..190427e 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ 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= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= diff --git a/server-ctl.go b/server-ctl.go index 40b0852..b663d5e 100644 --- a/server-ctl.go +++ b/server-ctl.go @@ -56,6 +56,7 @@ type server_ctl_server_conns_id_routes_id struct { type server_ctl_stats struct { s *Server + id string } // ------------------------------------ @@ -124,7 +125,7 @@ func (ctl *server_ctl_server_conns) ServeHTTP(w http.ResponseWriter, req *http.R } //done: - s.log.Write("", LOG_DEBUG, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) // TODO: time taken + s.log.Write("", LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) // TODO: time taken return oops: @@ -197,7 +198,7 @@ func (ctl *server_ctl_server_conns_id) ServeHTTP(w http.ResponseWriter, req *htt } done: - s.log.Write("", LOG_DEBUG, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) + s.log.Write("", LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) return oops: @@ -263,7 +264,7 @@ func (ctl *server_ctl_server_conns_id_routes) ServeHTTP(w http.ResponseWriter, r } done: - s.log.Write("", LOG_DEBUG, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) // TODO: time taken + s.log.Write("", LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) // TODO: time taken return oops: @@ -320,7 +321,7 @@ func (ctl *server_ctl_server_conns_id_routes_id) ServeHTTP(w http.ResponseWriter } //done: - s.log.Write("", LOG_DEBUG, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) // TODO: time taken + s.log.Write("", LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) // TODO: time taken return oops: @@ -330,7 +331,11 @@ oops: // ------------------------------------ -func (ctl *server_ctl_stats) ServeHTTP(w http.ResponseWriter, req *http.Request) { +func (ctl *server_ctl_stats) GetId() string { + return ctl.id +} + +func (ctl *server_ctl_stats) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) { var s *Server var status_code int var err error @@ -367,10 +372,10 @@ func (ctl *server_ctl_stats) ServeHTTP(w http.ResponseWriter, req *http.Request) } //done: - s.log.Write("", LOG_DEBUG, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) // TODO: time taken - return + //s.log.Write(ctl.id, LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) // TODO: time taken + return status_code, nil oops: - s.log.Write("", LOG_ERROR, "[%s] %s %s - %s", req.RemoteAddr, req.Method, req.URL.String(), err.Error()) - return + //s.log.Write(ctl.id, LOG_ERROR, "[%s] %s %s - %s", req.RemoteAddr, req.Method, req.URL.String(), err.Error()) + return status_code, err } diff --git a/server-proxy.go b/server-proxy.go index 54287f9..b058553 100644 --- a/server-proxy.go +++ b/server-proxy.go @@ -34,21 +34,25 @@ var xterm_html []byte type server_proxy_http_init struct { s *Server prefix string + id string } type server_proxy_http_main struct { s *Server prefix string - restore bool // restore URLs in response text + id string } -type server_proxy_ssh struct { +type server_proxy_xterm_file struct { s *Server + file string + id string } + // ------------------------------------ //Copied from net/http/httputil/reverseproxy.go -var hopHeaders = []string{ +var hop_headers = []string{ "Connection", "Proxy-Connection", // non-standard but still sent by libcurl and rejected by e.g. google "Keep-Alive", @@ -75,12 +79,12 @@ func copy_headers(dst http.Header, src http.Header) { func delete_hop_by_hop_headers(header http.Header) { var h string - for _, h = range hopHeaders { + for _, h = range hop_headers { header.Del(h) } } -func mutate_proxy_req_headers(req *http.Request, newreq *http.Request, path_prefix string) bool { +func mutate_proxy_req_headers(req *http.Request, newreq *http.Request, path_prefix string, in_wpx_mode bool) bool { var hdr http.Header var newhdr http.Header var remote_addr string @@ -143,7 +147,7 @@ func mutate_proxy_req_headers(req *http.Request, newreq *http.Request, path_pref newhdr.Set("X-Forwarded-Host", req.Host) } - if path_prefix != "" { + if !in_wpx_mode && path_prefix != "" { var v []string _, ok = newhdr["X-Forwarded-Path"] @@ -166,7 +170,11 @@ func mutate_proxy_req_headers(req *http.Request, newreq *http.Request, path_pref // ------------------------------------ -func (pxy *server_proxy_http_init) ServeHTTP(w http.ResponseWriter, req *http.Request) { +func (pxy *server_proxy_http_init) GetId() string { + return pxy.id +} + +func (pxy *server_proxy_http_init) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) { var s *Server var status_code int var conn_id string @@ -198,12 +206,10 @@ func (pxy *server_proxy_http_init) ServeHTTP(w http.ResponseWriter, req *http.Re status_code = http.StatusFound; w.WriteHeader(status_code) //done: - s.log.Write("", LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) - return + return status_code, nil oops: - s.log.Write("", LOG_ERROR, "[%s] %s %s %d - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, err.Error()) - return + return status_code, err } // ------------------------------------ @@ -212,14 +218,14 @@ func prevent_follow_redirect (req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse } -func (pxy *server_proxy_http_main) get_route(req *http.Request) (*ServerRoute, string, string, string, error) { +func (pxy *server_proxy_http_main) get_route(req *http.Request, in_wpx_mode bool) (*ServerRoute, string, string, string, error) { var conn_id string var route_id string var r *ServerRoute var path_prefix string var err error - if pxy.prefix == PORT_ID_MARKER { // for wpx + if in_wpx_mode { // for wpx conn_id = req.PathValue("port_id") route_id = pxy.prefix } else { @@ -238,7 +244,7 @@ func (pxy *server_proxy_http_main) get_route(req *http.Request) (*ServerRoute, s } ids = strings.Split(id.Value, "-") - if (len(ids) != 2) { + if len(ids) != 2 { return nil, "", "", "", fmt.Errorf("invalid proxy id cookie value - %s", id.Value) } @@ -246,7 +252,7 @@ func (pxy *server_proxy_http_main) get_route(req *http.Request) (*ServerRoute, s route_id = ids[1] path_prefix = "" } else { - if pxy.prefix == PORT_ID_MARKER { // for wpx + if in_wpx_mode { // for wpx path_prefix = fmt.Sprintf("/%s", conn_id) } else { path_prefix = fmt.Sprintf("%s/%s/%s", pxy.prefix, conn_id, route_id) @@ -358,7 +364,11 @@ func (pxy *server_proxy_http_main) req_to_proxy_url (req *http.Request, r *Serve } } -func (pxy *server_proxy_http_main) ServeHTTP(w http.ResponseWriter, req *http.Request) { +func (pxy *server_proxy_http_main) GetId() string { + return pxy.id +} + +func (pxy *server_proxy_http_main) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) { var s *Server var r *ServerRoute var status_code int @@ -366,6 +376,7 @@ func (pxy *server_proxy_http_main) ServeHTTP(w http.ResponseWriter, req *http.Re var route_id string var path_prefix string var resp *http.Response + var in_wpx_mode bool var transport *http.Transport var client *http.Client var addr *net.TCPAddr @@ -380,14 +391,9 @@ func (pxy *server_proxy_http_main) ServeHTTP(w http.ResponseWriter, req *http.Re }() s = pxy.s + in_wpx_mode = pxy.prefix == PORT_ID_MARKER -/* - ctx := req.Context() - if ctx.Done() != nil { - } -*/ - - r, path_prefix, conn_id, route_id, err = pxy.get_route(req) + r, path_prefix, conn_id, route_id, err = pxy.get_route(req, in_wpx_mode) if err != nil { status_code = http.StatusNotFound; w.WriteHeader(status_code) goto oops @@ -400,9 +406,7 @@ func (pxy *server_proxy_http_main) ServeHTTP(w http.ResponseWriter, req *http.Re goto oops } */ - addr = svc_addr_to_dst_addr(r.svc_addr) - //transport, err = pxy.addr_to_transport(req.Context(), addr) transport, err = pxy.addr_to_transport(s.ctx, addr) if err != nil { status_code = http.StatusBadGateway; w.WriteHeader(status_code) @@ -410,72 +414,86 @@ func (pxy *server_proxy_http_main) ServeHTTP(w http.ResponseWriter, req *http.Re } proxy_url = pxy.req_to_proxy_url(req, r, path_prefix) - s.log.Write("", LOG_INFO, "[%s] %s %s -> %+v", req.RemoteAddr, req.Method, req.URL.String(), proxy_url) + s.log.Write(pxy.id, LOG_INFO, "[%s] %s %s -> %+v", req.RemoteAddr, req.Method, req.URL.String(), proxy_url) - //proxy_req, err = http.NewRequestWithContext(req.Context(), req.Method, proxy_url.String(), req.Body) proxy_req, err = http.NewRequestWithContext(s.ctx, req.Method, proxy_url.String(), req.Body) if err != nil { status_code = http.StatusInternalServerError; w.WriteHeader(status_code) goto oops } - upgrade_required = mutate_proxy_req_headers(req, proxy_req, path_prefix) + upgrade_required = mutate_proxy_req_headers(req, proxy_req, path_prefix, in_wpx_mode) + + if in_wpx_mode { + proxy_req.Header.Set("Accept-Encoding", "") + } -//fmt.Printf ("proxy NEW req [%+v]\n", proxy_req.Header) client = &http.Client{ Transport: transport, CheckRedirect: prevent_follow_redirect, - Timeout: 5 * time.Second, // TODO: make this configurable.... + // don't specify Timeout for this here or make it configurable... } resp, err = client.Do(proxy_req) - //resp, err = transport.RoundTrip(proxy_req) + //resp, err = transport.RoundTrip(proxy_req) // any advantage if using RoundTrip instead? if err != nil { status_code = http.StatusInternalServerError; w.WriteHeader(status_code) goto oops } else { status_code = resp.StatusCode if upgrade_required && resp.StatusCode == http.StatusSwitchingProtocols { - s.log.Write("", LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) + s.log.Write(pxy.id, LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) err = pxy.serve_upgraded(w, req, resp) if err != nil { goto oops } - return // print the log mesage before calling serve_upgraded() and exit here + return 0, nil// print the log mesage before calling serve_upgraded() and exit here } else { - var hdr http.Header - defer resp.Body.Close() + var outhdr http.Header + var resp_hdr http.Header + var resp_body io.Reader - hdr = w.Header() - copy_headers(hdr, resp.Header) - delete_hop_by_hop_headers(hdr) + defer resp.Body.Close() + resp_hdr = resp.Header + resp_body = resp.Body + + if in_wpx_mode && s.wpx_resp_tf != nil { + resp_body = s.wpx_resp_tf(path_prefix, resp) + } + + outhdr = w.Header() + copy_headers(outhdr, resp_hdr) + delete_hop_by_hop_headers(outhdr) if path_prefix == "" { - hdr.Add("Set-Cookie", fmt.Sprintf("%s=%s-%s; Path=/; HttpOnly", SERVER_PROXY_ID_COOKIE, conn_id, route_id)) + outhdr.Add("Set-Cookie", fmt.Sprintf("%s=%s-%s; Path=/; HttpOnly", SERVER_PROXY_ID_COOKIE, conn_id, route_id)) } w.WriteHeader(status_code) - io.Copy(w, resp.Body) + + _, err = io.Copy(w, resp_body) + if err != nil { + s.log.Write(pxy.id, LOG_WARN, "[%s] %s %s %s", req.RemoteAddr, req.Method, req.URL.String(), err.Error()) + } + // TODO: handle trailers } } //done: - s.log.Write("", LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) - return + return status_code, nil oops: - s.log.Write("", LOG_ERROR, "[%s] %s %s %d - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, err.Error()) - return + return status_code, err } // ------------------------------------ -type server_proxy_xterm_file struct { - s *Server - file string -} type server_proxy_xterm_session_info struct { ConnId string RouteId string } -func (pxy *server_proxy_xterm_file) ServeHTTP(w http.ResponseWriter, req *http.Request) { +func (pxy *server_proxy_xterm_file) GetId() string { + return pxy.id +} + +func (pxy *server_proxy_xterm_file) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) { var s *Server var status_code int var err error @@ -535,12 +553,10 @@ func (pxy *server_proxy_xterm_file) ServeHTTP(w http.ResponseWriter, req *http.R } //done: - s.log.Write("", LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) - return + return status_code, nil oops: - s.log.Write("", LOG_ERROR, "[%s] %s %s %d - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, err.Error()) - return + return status_code, err } // ------------------------------------ @@ -548,6 +564,7 @@ oops: type server_proxy_ssh_ws struct { s *Server ws *websocket.Conn + id string } type json_ssh_ws_event struct { @@ -683,14 +700,14 @@ func (pxy *server_proxy_ssh_ws) ServeWebsocket(ws *websocket.Conn) { n, err = out.Read(buf) if err != nil { if err != io.EOF { - s.log.Write("", LOG_ERROR, "Read from SSH stdout error - %s", err.Error()) + s.log.Write(pxy.id, LOG_ERROR, "Read from SSH stdout error - %s", err.Error()) } break } if n > 0 { err = pxy.send_ws_data(ws, "iov", string(buf[:n])) if err != nil { - s.log.Write("", LOG_ERROR, "Failed to send to websocket - %s", err.Error()) + s.log.Write(pxy.id, LOG_ERROR, "Failed to send to websocket - %s", err.Error()) break } } @@ -724,13 +741,13 @@ ws_recv_loop: defer wg.Done() c, sess, in, out, err = pxy.connect_ssh(connect_ssh_ctx, username, password, r) if err != nil { - s.log.Write("", LOG_ERROR, "failed to connect ssh - %s", err.Error()) + s.log.Write(pxy.id, LOG_ERROR, "failed to connect ssh - %s", err.Error()) pxy.send_ws_data(ws, "error", err.Error()) ws.Close() // dirty way to flag out the error } else { err = pxy.send_ws_data(ws, "status", "opened") if err != nil { - s.log.Write("", LOG_ERROR, "Failed to write opened event to websocket - %s", err.Error()) + s.log.Write(pxy.id, LOG_ERROR, "Failed to write opened event to websocket - %s", err.Error()) ws.Close() // dirty way to flag out the error } else { conn_ready_chan <- true @@ -762,7 +779,7 @@ ws_recv_loop: rows, _ = strconv.Atoi(ev.Data[0]) cols, _ = strconv.Atoi(ev.Data[1]) sess.WindowChange(rows, cols) - s.log.Write("", LOG_DEBUG, "Resized terminal to %d,%d", rows, cols) + s.log.Write(pxy.id, LOG_DEBUG, "Resized terminal to %d,%d", rows, cols) // ignore error } } @@ -782,8 +799,8 @@ done: if c != nil { c.Close() } wg.Wait() if err != nil { - s.log.Write("", LOG_ERROR, "[%s] %s %s - %s", req.RemoteAddr, req.Method, req.URL.String(), err.Error()) + s.log.Write(pxy.id, LOG_ERROR, "[%s] %s %s - %s", req.RemoteAddr, req.Method, req.URL.String(), err.Error()) } else { - s.log.Write("", LOG_DEBUG, "[%s] %s %s - ended", req.RemoteAddr, req.Method, req.URL.String()) + s.log.Write(pxy.id, LOG_DEBUG, "[%s] %s %s - ended", req.RemoteAddr, req.Method, req.URL.String()) } } diff --git a/server.go b/server.go index 8351cb5..081ce78 100644 --- a/server.go +++ b/server.go @@ -9,9 +9,11 @@ import "log" import "net" import "net/http" import "net/netip" +import "plugin" import "strconv" import "sync" import "sync/atomic" +import "time" import "unsafe" import "golang.org/x/net/websocket" @@ -33,6 +35,8 @@ type ServerRouteMap = map[RouteId]*ServerRoute type ServerPeerConnMap = map[PeerId]*ServerPeerConn type ServerSvcPortMap = map[PortId]ConnRouteId +type ServerWpxResponseTransformer func(path_prefix string, resp *http.Response) io.Reader + type Server struct { ctx context.Context ctx_cancel context.CancelFunc @@ -56,6 +60,7 @@ type Server struct { wpx_addr []string wpx_mux *http.ServeMux wpx []*http.Server // proxy server than handles http/https only + wpx_resp_tf ServerWpxResponseTransformer ctl_addr []string ctl_prefix string @@ -129,6 +134,12 @@ type ServerRoute struct { stop_req atomic.Bool } +type ServerPluginInterface interface { + ModifyResponse(w http.ResponseWriter, r *http.Request) + Init(server *Server) + Cleanup() +} + type GuardedPacketStreamServer struct { mtx sync.Mutex //pss Hodu_PacketStreamServer @@ -491,7 +502,7 @@ func (cts *ServerConn) ReportEvent(route_id RouteId, pts_id PeerId, event_type P cts.route_mtx.Lock() r, ok = cts.route_map[route_id] - if (!ok) { + if !ok { cts.route_mtx.Unlock() return fmt.Errorf("non-existent route id - %d", route_id) } @@ -905,16 +916,44 @@ func (hlw *server_http_log_writer) Write(p []byte) (n int, err error) { return len(p), nil } +type ServerHttpHandler interface { + GetId() string + ServeHTTP (w http.ResponseWriter, req *http.Request) (int, error) +} + +func (s *Server) wrap_http_handler(handler ServerHttpHandler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + var status_code int + var err error + var start_time time.Time + var time_taken time.Duration + + start_time = time.Now() + status_code, err = handler.ServeHTTP(w, req) + time_taken = time.Now().Sub(start_time) + + if status_code > 0 { + if err == nil { + s.log.Write(handler.GetId(), LOG_INFO, "[%s] %s %s %d %v", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken) + } else { + s.log.Write(handler.GetId(), LOG_INFO, "[%s] %s %s %d %v - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken, err.Error()) + } + } + }) +} + func NewServer(ctx context.Context, logger Logger, ctl_addrs []string, rpc_addrs []string, pxy_addrs []string, wpx_addrs []string, ctl_prefix string, ctltlscfg *tls.Config, rpctlscfg *tls.Config, pxytlscfg *tls.Config, wpxtlscfg *tls.Config, rpc_max int, peer_max int) (*Server, error) { var s Server var l *net.TCPListener var rpcaddr *net.TCPAddr - var err error var addr string var gl *net.TCPListener var i int var hs_log *log.Logger var opts []grpc.ServerOption + var plgin *plugin.Plugin + var plgsym plugin.Symbol + var err error if len(rpc_addrs) <= 0 { return nil, fmt.Errorf("no server addresses provided") @@ -980,7 +1019,7 @@ func NewServer(ctx context.Context, logger Logger, ctl_addrs []string, rpc_addrs 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}) s.ctl_mux.Handle(s.ctl_prefix + "/server-conns/{conn_id}/routes/{route_id}", &server_ctl_server_conns_id_routes_id{s: &s}) - s.ctl_mux.Handle(s.ctl_prefix + "/stats", &server_ctl_stats{s: &s}) + s.ctl_mux.Handle(s.ctl_prefix + "/stats", s.wrap_http_handler(&server_ctl_stats{s: &s, id: "ctl-stat"})) s.ctl_addr = make([]string, len(ctl_addrs)) s.ctl = make([]*http.Server, len(ctl_addrs)) @@ -998,20 +1037,20 @@ func NewServer(ctx context.Context, logger Logger, ctl_addrs []string, rpc_addrs // --------------------------------------------------------- - s.pxy_ws = &server_proxy_ssh_ws{s: &s} + s.pxy_ws = &server_proxy_ssh_ws{s: &s, id: "pxy-ssh"} s.pxy_mux = http.NewServeMux() // TODO: make /_init,_ssh,_ssh_ws,_http configurable... s.pxy_mux.Handle("/_ssh-ws/{conn_id}/{route_id}", websocket.Handler(func(ws *websocket.Conn) { s.pxy_ws.ServeWebsocket(ws) })) s.pxy_mux.Handle("/_ssh/server-conns/{conn_id}/routes/{route_id}", &server_ctl_server_conns_id_routes_id{s: &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"}) + s.pxy_mux.Handle("/_ssh/{conn_id}/{route_id}/", s.wrap_http_handler(&server_proxy_xterm_file{s: &s, file: "xterm.html", id: "pxy-file"})) + s.pxy_mux.Handle("/_ssh/xterm.js", s.wrap_http_handler(&server_proxy_xterm_file{s: &s, file: "xterm.js", id: "pxy-file"})) + s.pxy_mux.Handle("/_ssh/xterm-addon-fit.js", s.wrap_http_handler(&server_proxy_xterm_file{s: &s, file: "xterm-addon-fit.js", id: "pxy-file"})) + s.pxy_mux.Handle("/_ssh/xterm.css", s.wrap_http_handler(&server_proxy_xterm_file{s: &s, file: "xterm.css", id: "pxy-file"})) + s.pxy_mux.Handle("/_ssh/", s.wrap_http_handler(&server_proxy_xterm_file{s: &s, file: "_forbidden", id: "pxy-file"})) - s.pxy_mux.Handle("/_http/{conn_id}/{route_id}/{trailer...}", &server_proxy_http_main{s: &s, prefix: "/_http"}) - s.pxy_mux.Handle("/_init/{conn_id}/{route_id}/{trailer...}", &server_proxy_http_init{s: &s, prefix: "/_init"}) - s.pxy_mux.Handle("/", &server_proxy_http_main{s: &s, prefix: ""}) + s.pxy_mux.Handle("/_http/{conn_id}/{route_id}/{trailer...}", s.wrap_http_handler(&server_proxy_http_main{s: &s, prefix: "/_http", id: "pxy-http"})) + s.pxy_mux.Handle("/_init/{conn_id}/{route_id}/{trailer...}", s.wrap_http_handler(&server_proxy_http_init{s: &s, prefix: "/_init", id: "pxy-http"})) + s.pxy_mux.Handle("/", s.wrap_http_handler(&server_proxy_http_main{s: &s, prefix: "", id: "pxy-http"})) s.pxy_addr = make([]string, len(pxy_addrs)) s.pxy = make([]*http.Server, len(pxy_addrs)) @@ -1030,7 +1069,7 @@ func NewServer(ctx context.Context, logger Logger, ctl_addrs []string, rpc_addrs // --------------------------------------------------------- s.wpx_mux = http.NewServeMux() - s.wpx_mux.Handle("/{port_id}/{trailer...}", &server_proxy_http_main{s: &s, prefix: PORT_ID_MARKER}) + s.wpx_mux.Handle("/{port_id}/{trailer...}", s.wrap_http_handler(&server_proxy_http_main{s: &s, prefix: PORT_ID_MARKER, id: "wpx"})) s.wpx_mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusForbidden) }) @@ -1055,21 +1094,49 @@ func NewServer(ctx context.Context, logger Logger, ctl_addrs []string, rpc_addrs s.stats.peers.Store(0) s.stats.ssh_proxy_sessions.Store(0) + // --------------------------------------------------------- + plgin, err = plugin.Open("modres.so") + if err == nil { + plgsym, err = plgin.Lookup("Plugin") + if err == nil { + var plg ServerPluginInterface + var ok bool + + switch plgsym.(type) { + case *ServerPluginInterface: + var tmp *ServerPluginInterface + tmp, ok = plgsym.(*ServerPluginInterface) + if ok { plg = *tmp } + case ServerPluginInterface: + plg, ok = plgsym.(ServerPluginInterface) + } + //plg, ok = plgsym.(*ServerPluginInterface) + if ok { + plg.Init(&s) + plg.Cleanup() + } else { +fmt.Printf ("YYYYYYYYYYYYYYY NOT OK\n") + } + } else { +fmt.Printf ("YYYYYYYYYYYYYYY[%v]\n", err) + } + } else { +fmt.Printf ("XXXXXX[%v]\n", err) + } + return &s, nil oops: - // TODO: check if rpc_svr needs to be closed. probably not. closing the listen may be good enough - if gl != nil { - gl.Close() - } - - for _, l = range s.rpc { - l.Close() - } + if gl != nil { gl.Close() } + for _, l = range s.rpc { l.Close() } s.rpc = make([]*net.TCPListener, 0) return nil, err } +func (s *Server) SetWpxResponseTransformer(tf ServerWpxResponseTransformer) { + s.wpx_resp_tf = tf +} + func (s *Server) run_grpc_server(idx int, wg *sync.WaitGroup) error { var l *net.TCPListener var err error @@ -1428,7 +1495,6 @@ func (s *Server) FindServerConnByAddr(addr net.Addr) *ServerConn { return cts } - func (s *Server) FindServerRouteById(id ConnId, route_id RouteId) *ServerRoute { var cts *ServerConn var ok bool diff --git a/system.go b/system.go index 420c92d..c78f007 100644 --- a/system.go +++ b/system.go @@ -16,7 +16,7 @@ func monotonic_time() uint64 { var uts unix.Timespec n = nanotime() // hopefully it's faster than a system call. say, vdso is utilized. - if (n >= 0) { return uint64(n) } + if n >= 0 { return uint64(n) } err = unix.ClockGettime(unix.CLOCK_MONOTONIC, &uts) if err != nil { diff --git a/transform.go b/transform.go new file mode 100644 index 0000000..fda4a5a --- /dev/null +++ b/transform.go @@ -0,0 +1,82 @@ +package hodu + +import "bytes" +import "golang.org/x/text/transform" + +type Transformer struct { + replacement []byte + needle []byte + needle_len int +} + +func NewBytesTransformer(needle []byte, replacement []byte) *Transformer { + return &Transformer{needle: needle, replacement: replacement, needle_len: len(needle)} +} + +func NewStringTransformer(needle string, replacement string) *Transformer { + return NewBytesTransformer([]byte(needle), []byte(replacement)) +} + +func (t *Transformer) Reset() { + // do nothing +} + +func (t *Transformer) Transform(dst []byte, src []byte, at_eof bool) (int, int, error) { + var n int + var i int + var ndst int + var nsrc int + var rem int + var err error + + if t.needle_len <= 0 { + n, err = t.copy_all(dst, src) + return n, n, err + } + + nsrc = 0; ndst = 0 + for { + i = bytes.Index(src[nsrc:], t.needle) + if i == -1 { break } + + // copy the part before the match + n, err = t.copy_all(dst[ndst:], src[nsrc:nsrc+i]) + nsrc += n; ndst += n + if err != nil { goto done } + + // copy the new value in place of the match + n, err = t.copy_all(dst[ndst:], t.replacement) + if err != nil { goto done } + ndst += n; nsrc += t.needle_len + } + + if at_eof { + n, err = t.copy_all(dst[ndst:], src[nsrc:]) + ndst += n; nsrc += n + goto done + } + + rem = len(src[nsrc:]) + if rem >= t.needle_len { + n, err = t.copy_all(dst[ndst:], src[nsrc: nsrc + (rem - t.needle_len) + 1]) + nsrc += n; ndst += n + if err != nil { goto done } + } + + // ErrShortSrc means that the source buffer has insufficient data to + // complete the transformation. + err = transform.ErrShortSrc + +done: + return ndst, nsrc, err +} + +func (t *Transformer) copy_all(dst []byte, src []byte) (int, error) { + var n int + var err error + n = copy(dst, src) + // ErrShortDst means that the destination buffer was too short to + // receive all of the transformed bytes. + if n < len(src) { err = transform.ErrShortDst } + return n, err +} diff --git a/xterm.html b/xterm.html index a8aa23e..1c5f5b8 100644 --- a/xterm.html +++ b/xterm.html @@ -106,7 +106,11 @@ window.onload = function(event) { const username_field = document.getElementById('username'); const password_field= document.getElementById('password'); - const term = new window.Terminal(); + const term = new window.Terminal({ + lineHeight: 1.2, + //fontSize: 14, + fontFamily: 'Ubuntu Mono, Consolas, SF Mono, courier-new, courier, monospace' + }); const fit_addon = new window.FitAddon.FitAddon(); const text_decoder = new TextDecoder();