github.com/aitjcize/Overlord@v0.0.0-20240314041920-104a804cf5e8/overlord/overlord.go (about)

     1  // Copyright 2015 The Chromium OS Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  package overlord
     6  
     7  import (
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"log"
    14  	"net"
    15  	"net/http"
    16  	"os"
    17  	"path/filepath"
    18  	"runtime"
    19  	"sort"
    20  	"strconv"
    21  	"strings"
    22  	"sync"
    23  	"time"
    24  
    25  	socketio "github.com/googollee/go-socket.io"
    26  	"github.com/gorilla/mux"
    27  	"github.com/gorilla/websocket"
    28  	uuid "github.com/satori/go.uuid"
    29  )
    30  
    31  const (
    32  	systemAppDir = "../share/overlord"
    33  	ldInterval   = 5
    34  )
    35  
    36  // SpawnTerminalCmd is an overlord intend to launch a terminal.
    37  type SpawnTerminalCmd struct {
    38  	Sid       string // Session ID
    39  	TtyDevice string // Termainl device to open
    40  }
    41  
    42  // SpawnShellCmd is an overlord intend to launch a shell command.
    43  type SpawnShellCmd struct {
    44  	Sid     string // Session ID
    45  	Command string // Command to execute
    46  }
    47  
    48  // SpawnFileCmd is an overlord intend to perform file transfer.
    49  type SpawnFileCmd struct {
    50  	Sid         string // Session ID
    51  	TerminalSid string // Target terminal's session ID
    52  	Action      string // Action, download or upload
    53  	Filename    string // File to perform action on
    54  	Dest        string // Destination, use for upload
    55  	Perm        int    // File permissions to set
    56  	CheckOnly   bool   // Check permission only (writable?)
    57  }
    58  
    59  // SpawnModeForwarderCmd is an overlord intend to perform port forwarding.
    60  type SpawnModeForwarderCmd struct {
    61  	Sid  string // Session ID
    62  	Port int    // Port to forward
    63  }
    64  
    65  // ConnectLogcatCmd is an overlord intend to connect to a logcat session.
    66  type ConnectLogcatCmd struct {
    67  	Conn *websocket.Conn
    68  }
    69  
    70  // webSocketContext is used for maintaining the session information of
    71  // WebSocket requests. When requests come from Web Server, we create a new
    72  // WebSocketConext to store the session ID and WebSocket connection. ConnServer
    73  // will request a new terminal connection with the given session ID.
    74  // This way, the ConnServer can retrieve the connresponding webSocketContext
    75  // with it's the given session ID and get the WebSocket.
    76  type webSocketContext struct {
    77  	Sid  string          // Session ID
    78  	Conn *websocket.Conn // WebSocket connection
    79  }
    80  
    81  // newWebsocketContext create  webSocketContext object.
    82  func newWebsocketContext(conn *websocket.Conn) *webSocketContext {
    83  	return &webSocketContext{
    84  		Sid:  uuid.NewV4().String(),
    85  		Conn: conn,
    86  	}
    87  }
    88  
    89  // TLSCerts stores the TLS certificate filenames.
    90  type TLSCerts struct {
    91  	Cert string // cert.pem filename
    92  	Key  string // key.pem filename
    93  }
    94  
    95  // Overlord type is the main context for storing the overlord server state.
    96  type Overlord struct {
    97  	bindAddr         string                            // Bind address
    98  	port             int                               // Port number to listen to
    99  	lanDiscInterface string                            // Network interface used for broadcasting LAN discovery packet
   100  	lanDisc          bool                              // Enable LAN discovery broadcasting
   101  	auth             bool                              // Enable HTTP basic authentication
   102  	certs            *TLSCerts                         // TLS certificate
   103  	linkTLS          bool                              // Enable TLS between ghost and overlord
   104  	htpasswdPath     string                            // Path to .htpasswd file
   105  	agents           map[string]*ConnServer            // Normal ghost agents
   106  	logcats          map[string]map[string]*ConnServer // logcat clients
   107  	wsctxs           map[string]*webSocketContext      // (sid, webSocketContext) mapping
   108  	downloads        map[string]*ConnServer            // Download file agents
   109  	uploads          map[string]*ConnServer            // Upload file agents
   110  	ioserver         *socketio.Server                  // SocketIO server handle
   111  	agentsMu         sync.Mutex                        // Mutex for agents
   112  	logcatsMu        sync.Mutex                        // Mutex for logcats
   113  	wsctxsMu         sync.Mutex                        // Mutex for wsctxs
   114  	downloadsMu      sync.Mutex                        // Mutex for downloads
   115  	uploadsMu        sync.Mutex                        // Mutex for uploads
   116  	ioserverMu       sync.Mutex                        // Mutex for ioserver
   117  }
   118  
   119  // NewOverlord creates an Overlord object.
   120  func NewOverlord(
   121  	bindAddr string, port int,
   122  	lanDiscInterface string,
   123  	lanDisc bool, auth bool,
   124  	certsString string, linkTLS bool, htpasswdPath string) *Overlord {
   125  
   126  	var certs *TLSCerts
   127  	if certsString != "" {
   128  		parts := strings.Split(certsString, ",")
   129  		if len(parts) != 2 {
   130  			log.Fatalf("TLSCerts: invalid TLS certs argument")
   131  		} else {
   132  			certs = &TLSCerts{parts[0], parts[1]}
   133  		}
   134  	}
   135  	if !filepath.IsAbs(htpasswdPath) {
   136  		execPath, err := os.Executable()
   137  		if err != nil {
   138  			log.Fatalln(err)
   139  		}
   140  		execDir := filepath.Dir(execPath)
   141  		htpasswdPath, err = filepath.Abs(filepath.Join(execDir, htpasswdPath))
   142  		if err != nil {
   143  			log.Fatalln(err)
   144  		}
   145  	}
   146  	return &Overlord{
   147  		bindAddr:         bindAddr,
   148  		port:             port,
   149  		lanDiscInterface: lanDiscInterface,
   150  		lanDisc:          lanDisc,
   151  		auth:             auth,
   152  		certs:            certs,
   153  		linkTLS:          linkTLS,
   154  		htpasswdPath:     htpasswdPath,
   155  		agents:           make(map[string]*ConnServer),
   156  		logcats:          make(map[string]map[string]*ConnServer),
   157  		wsctxs:           make(map[string]*webSocketContext),
   158  		downloads:        make(map[string]*ConnServer),
   159  		uploads:          make(map[string]*ConnServer),
   160  	}
   161  }
   162  
   163  // Register a client.
   164  func (ovl *Overlord) Register(conn *ConnServer) (*websocket.Conn, error) {
   165  	msg, err := json.Marshal(map[string]interface{}{
   166  		"mid": conn.Mid,
   167  		"sid": conn.Sid,
   168  	})
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	var wsconn *websocket.Conn
   174  
   175  	switch conn.Mode {
   176  	case ModeControl:
   177  		ovl.agentsMu.Lock()
   178  		if _, ok := ovl.agents[conn.Mid]; ok {
   179  			ovl.agentsMu.Unlock()
   180  			return nil, errors.New("duplicate machine ID: " + conn.Mid)
   181  		}
   182  		ovl.agents[conn.Mid] = conn
   183  		ovl.agentsMu.Unlock()
   184  		ovl.ioserver.BroadcastTo("monitor", "agent joined", string(msg))
   185  	case ModeTerminal, ModeShell, ModeForward:
   186  		ovl.wsctxsMu.Lock()
   187  		ctx, ok := ovl.wsctxs[conn.Sid]
   188  		if !ok {
   189  			ovl.wsctxsMu.Unlock()
   190  			return nil, errors.New("client " + conn.Sid + " registered without context")
   191  		}
   192  		wsconn = ctx.Conn
   193  		ovl.wsctxsMu.Unlock()
   194  	case ModeLogcat:
   195  		ovl.logcatsMu.Lock()
   196  		if _, ok := ovl.logcats[conn.Mid]; !ok {
   197  			ovl.logcats[conn.Mid] = make(map[string]*ConnServer)
   198  		}
   199  		if _, ok := ovl.logcats[conn.Mid][conn.Sid]; ok {
   200  			ovl.logcatsMu.Unlock()
   201  			return nil, errors.New("duplicate session ID: " + conn.Sid)
   202  		}
   203  		ovl.logcats[conn.Mid][conn.Sid] = conn
   204  		ovl.logcatsMu.Unlock()
   205  		ovl.ioserver.BroadcastTo("monitor", "logcat joined", string(msg))
   206  	case ModeFile:
   207  		// Do nothing, we wait until 'request_to_download' call from client to
   208  		// send the message to the browser
   209  	default:
   210  		return nil, errors.New("Unknown client mode")
   211  	}
   212  
   213  	var id string
   214  	if conn.Mode == ModeControl {
   215  		id = conn.Mid
   216  	} else {
   217  		id = conn.Sid
   218  	}
   219  
   220  	log.Printf("%s %s registered\n", ModeStr(conn.Mode), id)
   221  
   222  	return wsconn, nil
   223  }
   224  
   225  // Unregister a client.
   226  func (ovl *Overlord) Unregister(conn *ConnServer) {
   227  	msg, err := json.Marshal(map[string]interface{}{
   228  		"mid": conn.Mid,
   229  		"sid": conn.Sid,
   230  	})
   231  
   232  	if err != nil {
   233  		panic(err)
   234  	}
   235  
   236  	switch conn.Mode {
   237  	case ModeControl:
   238  		ovl.ioserver.BroadcastTo("monitor", "agent left", string(msg))
   239  		ovl.agentsMu.Lock()
   240  		delete(ovl.agents, conn.Mid)
   241  		ovl.agentsMu.Unlock()
   242  	case ModeLogcat:
   243  		ovl.logcatsMu.Lock()
   244  		if _, ok := ovl.logcats[conn.Mid]; ok {
   245  			ovl.ioserver.BroadcastTo("monitor", "logcat left", string(msg))
   246  			delete(ovl.logcats[conn.Mid], conn.Sid)
   247  			if len(ovl.logcats[conn.Mid]) == 0 {
   248  				delete(ovl.logcats, conn.Mid)
   249  			}
   250  		}
   251  		ovl.logcatsMu.Unlock()
   252  	case ModeFile:
   253  		ovl.downloadsMu.Lock()
   254  		if _, ok := ovl.downloads[conn.Sid]; ok {
   255  			delete(ovl.downloads, conn.Sid)
   256  		}
   257  		ovl.downloadsMu.Unlock()
   258  		ovl.uploadsMu.Lock()
   259  		if _, ok := ovl.uploads[conn.Sid]; ok {
   260  			delete(ovl.uploads, conn.Sid)
   261  		}
   262  		ovl.uploadsMu.Unlock()
   263  	default:
   264  		ovl.wsctxsMu.Lock()
   265  		if _, ok := ovl.wsctxs[conn.Sid]; ok {
   266  			delete(ovl.wsctxs, conn.Sid)
   267  		}
   268  		ovl.wsctxsMu.Unlock()
   269  	}
   270  
   271  	var id string
   272  	if conn.Mode == ModeControl {
   273  		id = conn.Mid
   274  	} else {
   275  		id = conn.Sid
   276  	}
   277  	log.Printf("%s %s unregistered\n", ModeStr(conn.Mode), id)
   278  }
   279  
   280  // AddWebsocketContext adds an websocket context to the overlord state.
   281  func (ovl *Overlord) AddWebsocketContext(wc *webSocketContext) {
   282  	ovl.wsctxsMu.Lock()
   283  	ovl.wsctxs[wc.Sid] = wc
   284  	ovl.wsctxsMu.Unlock()
   285  }
   286  
   287  // RegisterDownloadRequest registers a file download request.
   288  func (ovl *Overlord) RegisterDownloadRequest(conn *ConnServer) {
   289  	// Use session ID as download session ID instead of machine ID, so a machine
   290  	// can have multiple download at the same time
   291  	ovl.ioserver.BroadcastTo(conn.TerminalSid, "file download", string(conn.Sid))
   292  	ovl.downloadsMu.Lock()
   293  	ovl.downloads[conn.Sid] = conn
   294  	ovl.downloadsMu.Unlock()
   295  }
   296  
   297  // RegisterUploadRequest registers a file upload request.
   298  func (ovl *Overlord) RegisterUploadRequest(conn *ConnServer) {
   299  	// Use session ID as upload session ID instead of machine ID, so a machine
   300  	// can have multiple upload at the same time
   301  	ovl.ioserver.BroadcastTo(conn.TerminalSid, "file upload", string(conn.Sid))
   302  	ovl.uploadsMu.Lock()
   303  	ovl.uploads[conn.Sid] = conn
   304  	ovl.uploadsMu.Unlock()
   305  }
   306  
   307  // Handle TCP Connection.
   308  func (ovl *Overlord) handleConnection(conn net.Conn) {
   309  	handler := NewConnServer(ovl, conn)
   310  	go handler.Listen()
   311  }
   312  
   313  // InitSocketIOServer initializes the Socket.io server.
   314  func (ovl *Overlord) InitSocketIOServer() {
   315  	server, err := socketio.NewServer(nil)
   316  	if err != nil {
   317  		log.Fatalln(err)
   318  	}
   319  
   320  	server.On("connection", func(so socketio.Socket) {
   321  		so.Join("monitor")
   322  	})
   323  
   324  	server.On("error", func(so socketio.Socket, err error) {
   325  		log.Println("error:", err)
   326  	})
   327  
   328  	// Client initiated subscription
   329  	server.On("subscribe", func(so socketio.Socket, name string) {
   330  		so.Join(name)
   331  	})
   332  
   333  	// Client initiated unsubscription
   334  	server.On("unsubscribe", func(so socketio.Socket, name string) {
   335  		so.Leave(name)
   336  	})
   337  
   338  	ovl.ioserver = server
   339  }
   340  
   341  // GetAppDir returns the overlord application directory.
   342  func (ovl *Overlord) GetAppDir() string {
   343  	execPath, err := os.Executable()
   344  	if err != nil {
   345  		log.Fatalln(err)
   346  	}
   347  	execDir := filepath.Dir(execPath)
   348  
   349  	appDir, err := filepath.Abs(filepath.Join(execDir, "app"))
   350  	if err != nil {
   351  		log.Fatalln(err)
   352  	}
   353  
   354  	if _, err := os.Stat(appDir); err != nil {
   355  		// Try system install directory
   356  		appDir, err = filepath.Abs(filepath.Join(execDir, systemAppDir, "app"))
   357  		if err != nil {
   358  			log.Fatalln(err)
   359  		}
   360  		if _, err := os.Stat(appDir); err != nil {
   361  			log.Fatalln("Can not find app directory")
   362  		}
   363  	}
   364  	return appDir
   365  }
   366  
   367  // GetAppNames return the name of overlord apps.
   368  func (ovl *Overlord) GetAppNames(ignoreSpecial bool) ([]string, error) {
   369  	var appNames []string
   370  
   371  	isSpecial := func(target string) bool {
   372  		for _, name := range []string{"common", "upgrade", "third_party"} {
   373  			if name == target {
   374  				return true
   375  			}
   376  		}
   377  		return false
   378  	}
   379  
   380  	apps, err := ioutil.ReadDir(ovl.GetAppDir())
   381  	if err != nil {
   382  		return nil, nil
   383  	}
   384  
   385  	for _, app := range apps {
   386  		if !app.IsDir() || (ignoreSpecial && isSpecial(app.Name())) {
   387  			continue
   388  		}
   389  		appNames = append(appNames, app.Name())
   390  	}
   391  	return appNames, nil
   392  }
   393  
   394  type byMid []map[string]interface{}
   395  
   396  func (a byMid) Len() int      { return len(a) }
   397  func (a byMid) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   398  func (a byMid) Less(i, j int) bool {
   399  	return a[i]["mid"].(string) < a[j]["mid"].(string)
   400  }
   401  
   402  // RegisterHTTPHandlers register handlers for http routes.
   403  func (ovl *Overlord) RegisterHTTPHandlers() {
   404  	var upgrader = websocket.Upgrader{
   405  		ReadBufferSize:  bufferSize,
   406  		WriteBufferSize: bufferSize,
   407  		Subprotocols:    []string{"binary"},
   408  		CheckOrigin: func(r *http.Request) bool {
   409  			return true
   410  		},
   411  	}
   412  
   413  	// Helper function for writing error message to WebSocket
   414  	WebSocketSendError := func(ws *websocket.Conn, err string) {
   415  		log.Println(err)
   416  		msg := websocket.FormatCloseMessage(websocket.CloseProtocolError, err)
   417  		ws.WriteMessage(websocket.CloseMessage, msg)
   418  		ws.Close()
   419  	}
   420  
   421  	GhostConnectHandler := func(w http.ResponseWriter, r *http.Request) {
   422  		conn, err := upgrader.Upgrade(w, r, nil)
   423  		if err != nil {
   424  			log.Println(err)
   425  			return
   426  		}
   427  
   428  		log.Printf("Incoming connection from %s", conn.UnderlyingConn().RemoteAddr())
   429  		cs := NewConnServer(ovl, conn.UnderlyingConn())
   430  		cs.Listen()
   431  	}
   432  
   433  	// List all apps available on Overlord.
   434  	AppsListHandler := func(w http.ResponseWriter, r *http.Request) {
   435  		w.Header().Set("Content-Type", "application/json")
   436  
   437  		apps, err := ovl.GetAppNames(true)
   438  		if err != nil {
   439  			w.Write([]byte(fmt.Sprintf(`{"error": "%s"}`, err)))
   440  		}
   441  
   442  		result, err := json.Marshal(map[string][]string{"apps": apps})
   443  		if err != nil {
   444  			w.Write([]byte(fmt.Sprintf(`{"error": "%s"}`, err)))
   445  		} else {
   446  			w.Write(result)
   447  		}
   448  	}
   449  
   450  	// List all agents connected to the Overlord.
   451  	AgentsListHandler := func(w http.ResponseWriter, r *http.Request) {
   452  		w.Header().Set("Content-Type", "application/json")
   453  		var data = make([]map[string]interface{}, 0)
   454  		ovl.agentsMu.Lock()
   455  		for _, agent := range ovl.agents {
   456  			data = append(data, map[string]interface{}{
   457  				"mid":        agent.Mid,
   458  				"sid":        agent.Sid,
   459  				"properties": agent.Properties,
   460  			})
   461  		}
   462  		ovl.agentsMu.Unlock()
   463  		sort.Sort(byMid(data))
   464  
   465  		result, err := json.Marshal(data)
   466  		if err != nil {
   467  			w.Write([]byte(fmt.Sprintf(`{"error": "%s"}`, err)))
   468  		} else {
   469  			w.Write(result)
   470  		}
   471  	}
   472  
   473  	// Agent upgrade request handler.
   474  	AgentsUpgradeHandler := func(w http.ResponseWriter, r *http.Request) {
   475  		ovl.agentsMu.Lock()
   476  		for _, agent := range ovl.agents {
   477  			err := agent.SendUpgradeRequest()
   478  			if err != nil {
   479  				w.Write([]byte(fmt.Sprintf("Failed to send upgrade request for `%s'.\n",
   480  					agent.Mid)))
   481  			}
   482  		}
   483  		ovl.agentsMu.Unlock()
   484  		w.Write([]byte(`{"status": "success"}`))
   485  	}
   486  
   487  	// List all logcat clients connected to the Overlord.
   488  	LogcatsListHandler := func(w http.ResponseWriter, r *http.Request) {
   489  		w.Header().Set("Content-Type", "application/json")
   490  		var data = make([]map[string]interface{}, 0)
   491  		ovl.logcatsMu.Lock()
   492  		for mid, logcats := range ovl.logcats {
   493  			var sids []string
   494  			for sid := range logcats {
   495  				sids = append(sids, sid)
   496  			}
   497  			data = append(data, map[string]interface{}{
   498  				"mid":  mid,
   499  				"sids": sids,
   500  			})
   501  		}
   502  		ovl.logcatsMu.Unlock()
   503  
   504  		result, err := json.Marshal(data)
   505  		if err != nil {
   506  			w.Write([]byte(fmt.Sprintf(`{"error": "%s"}`, err)))
   507  		} else {
   508  			w.Write(result)
   509  		}
   510  	}
   511  
   512  	// Logcat request handler.
   513  	// We directly send the WebSocket connection to ConnServer for forwarding
   514  	// the log stream.
   515  	LogcatHandler := func(w http.ResponseWriter, r *http.Request) {
   516  		log.Printf("Logcat request from %s\n", r.RemoteAddr)
   517  
   518  		conn, err := upgrader.Upgrade(w, r, nil)
   519  		if err != nil {
   520  			log.Println(err)
   521  			return
   522  		}
   523  
   524  		vars := mux.Vars(r)
   525  		mid := vars["mid"]
   526  		sid := vars["sid"]
   527  
   528  		ovl.logcatsMu.Lock()
   529  		if logcats, ok := ovl.logcats[mid]; ok {
   530  			ovl.logcatsMu.Unlock()
   531  			if logcat, ok := logcats[sid]; ok {
   532  				logcat.Command <- ConnectLogcatCmd{conn}
   533  			} else {
   534  				WebSocketSendError(conn, "No client with sid "+sid)
   535  			}
   536  		} else {
   537  			ovl.logcatsMu.Unlock()
   538  			WebSocketSendError(conn, "No client with mid "+mid)
   539  		}
   540  	}
   541  
   542  	// TTY stream request handler.
   543  	// We first create a webSocketContext to store the connection, then send a
   544  	// command to Overlord to client to spawn a terminal connection.
   545  	AgentTtyHandler := func(w http.ResponseWriter, r *http.Request) {
   546  		log.Printf("Terminal request from %s\n", r.RemoteAddr)
   547  		conn, err := upgrader.Upgrade(w, r, nil)
   548  		if err != nil {
   549  			log.Println(err)
   550  			return
   551  		}
   552  
   553  		var ttyDevice string
   554  
   555  		vars := mux.Vars(r)
   556  		mid := vars["mid"]
   557  		if _ttyDevice, ok := r.URL.Query()["tty_device"]; ok {
   558  			ttyDevice = _ttyDevice[0]
   559  		}
   560  
   561  		ovl.agentsMu.Lock()
   562  		if agent, ok := ovl.agents[mid]; ok {
   563  			ovl.agentsMu.Unlock()
   564  			wc := newWebsocketContext(conn)
   565  			ovl.AddWebsocketContext(wc)
   566  			agent.Command <- SpawnTerminalCmd{wc.Sid, ttyDevice}
   567  			if res := <-agent.Response; res != "" {
   568  				WebSocketSendError(conn, res)
   569  			}
   570  		} else {
   571  			ovl.agentsMu.Unlock()
   572  			WebSocketSendError(conn, "No client with mid "+mid)
   573  		}
   574  	}
   575  
   576  	// Shell command request handler.
   577  	// We first create a webSocketContext to store the connection, then send a
   578  	// command to ConnServer to client to spawn a shell connection.
   579  	ModeShellHandler := func(w http.ResponseWriter, r *http.Request) {
   580  		log.Printf("Shell request from %s\n", r.RemoteAddr)
   581  
   582  		conn, err := upgrader.Upgrade(w, r, nil)
   583  		if err != nil {
   584  			log.Println(err)
   585  			return
   586  		}
   587  
   588  		vars := mux.Vars(r)
   589  		mid := vars["mid"]
   590  		ovl.agentsMu.Lock()
   591  		if agent, ok := ovl.agents[mid]; ok {
   592  			ovl.agentsMu.Unlock()
   593  			if command, ok := r.URL.Query()["command"]; ok {
   594  				wc := newWebsocketContext(conn)
   595  				ovl.AddWebsocketContext(wc)
   596  				agent.Command <- SpawnShellCmd{wc.Sid, command[0]}
   597  				if res := <-agent.Response; res != "" {
   598  					WebSocketSendError(conn, res)
   599  				}
   600  			} else {
   601  				WebSocketSendError(conn, "No command specified for shell request "+mid)
   602  			}
   603  		} else {
   604  			ovl.agentsMu.Unlock()
   605  			WebSocketSendError(conn, "No client with mid "+mid)
   606  		}
   607  	}
   608  
   609  	// Get agent properties as JSON.
   610  	AgentPropertiesHandler := func(w http.ResponseWriter, r *http.Request) {
   611  		w.Header().Set("Content-Type", "application/json")
   612  
   613  		vars := mux.Vars(r)
   614  		mid := vars["mid"]
   615  		ovl.agentsMu.Lock()
   616  		if agent, ok := ovl.agents[mid]; ok {
   617  			ovl.agentsMu.Unlock()
   618  			jsonResult, err := json.Marshal(agent.Properties)
   619  			if err != nil {
   620  				w.Write([]byte(fmt.Sprintf(`{"error": "%s"}`, err)))
   621  				return
   622  			}
   623  			w.Write(jsonResult)
   624  		} else {
   625  			ovl.agentsMu.Unlock()
   626  			w.Write([]byte(fmt.Sprintf(`{"error": "No client with mid %s"}`, mid)))
   627  		}
   628  	}
   629  
   630  	// Helper function for serving file and write it into response body.
   631  	serveFileHTTP := func(w http.ResponseWriter, c *ConnServer) {
   632  		defer func() {
   633  			if c != nil {
   634  				c.StopListen()
   635  			}
   636  		}()
   637  		c.SendClearToDownload()
   638  
   639  		dispose := fmt.Sprintf("attachment; filename=\"%s\"", c.Download.Name)
   640  		w.Header().Set("Content-Disposition", dispose)
   641  		w.Header().Set("Content-Length", strconv.FormatInt(c.Download.Size, 10))
   642  
   643  		for {
   644  			data := <-c.Download.Data
   645  			if data == nil {
   646  				return
   647  			}
   648  			if _, err := w.Write(data); err != nil {
   649  				break
   650  			}
   651  		}
   652  	}
   653  
   654  	// File download request handler.
   655  	// Handler for file download request, the filename target machine is
   656  	// specified in the request URL.
   657  	AgentDownloadHandler := func(w http.ResponseWriter, r *http.Request) {
   658  		vars := mux.Vars(r)
   659  		mid := vars["mid"]
   660  
   661  		var agent *ConnServer
   662  		var ok bool
   663  
   664  		ovl.agentsMu.Lock()
   665  		if agent, ok = ovl.agents[mid]; !ok {
   666  			ovl.agentsMu.Unlock()
   667  			http.NotFound(w, r)
   668  			return
   669  		}
   670  		ovl.agentsMu.Unlock()
   671  
   672  		var filename []string
   673  		if filename, ok = r.URL.Query()["filename"]; !ok {
   674  			http.NotFound(w, r)
   675  			return
   676  		}
   677  
   678  		sid := uuid.NewV4().String()
   679  		agent.Command <- SpawnFileCmd{
   680  			Sid: sid, Action: "download", Filename: filename[0]}
   681  
   682  		res := <-agent.Response
   683  		if res != "" {
   684  			http.Error(w, fmt.Sprintf(`{"error": "%s"}`, res),
   685  				http.StatusBadRequest)
   686  			return
   687  		}
   688  
   689  		var c *ConnServer
   690  		const maxTries = 100 // 20 seconds
   691  		count := 0
   692  
   693  		// Wait until download client connects
   694  		for {
   695  			if count++; count == maxTries {
   696  				http.NotFound(w, r)
   697  				return
   698  			}
   699  			ovl.downloadsMu.Lock()
   700  			if c, ok = ovl.downloads[sid]; ok {
   701  				ovl.downloadsMu.Unlock()
   702  				break
   703  			}
   704  			ovl.downloadsMu.Unlock()
   705  			time.Sleep(200 * time.Millisecond)
   706  		}
   707  		serveFileHTTP(w, c)
   708  	}
   709  
   710  	// Passive file download request handler.
   711  	// This handler deal with requests that are initiated by the client. We
   712  	// simply check if the session id exists in the download client list, than
   713  	// start to download the file if it does.
   714  	FileDownloadHandler := func(w http.ResponseWriter, r *http.Request) {
   715  		vars := mux.Vars(r)
   716  		sid := vars["sid"]
   717  
   718  		var c *ConnServer
   719  		var ok bool
   720  
   721  		ovl.downloadsMu.Lock()
   722  		if c, ok = ovl.downloads[sid]; !ok {
   723  			ovl.downloadsMu.Unlock()
   724  			http.NotFound(w, r)
   725  			return
   726  		}
   727  		ovl.downloadsMu.Unlock()
   728  		serveFileHTTP(w, c)
   729  	}
   730  
   731  	// File upload request handler.
   732  	AgentUploadHandler := func(w http.ResponseWriter, r *http.Request) {
   733  		var ok bool
   734  		var err error
   735  		var agent *ConnServer
   736  		var errMsg string
   737  
   738  		defer func() {
   739  			if errMsg != "" {
   740  				http.Error(w, fmt.Sprintf(`{"error": "%s"}`, errMsg),
   741  					http.StatusBadRequest)
   742  			}
   743  		}()
   744  
   745  		vars := mux.Vars(r)
   746  		mid := vars["mid"]
   747  		ovl.agentsMu.Lock()
   748  		if agent, ok = ovl.agents[mid]; !ok {
   749  			ovl.agentsMu.Unlock()
   750  			errMsg = fmt.Sprintf("No client with mid %s", mid)
   751  			return
   752  		}
   753  		ovl.agentsMu.Unlock()
   754  
   755  		// Target terminal session ID
   756  		var terminalSids []string
   757  		if terminalSids, ok = r.URL.Query()["terminal_sid"]; !ok {
   758  			terminalSids = []string{""}
   759  		}
   760  		sid := uuid.NewV4().String()
   761  
   762  		// Upload destination
   763  		var dsts []string
   764  		if dsts, ok = r.URL.Query()["dest"]; !ok {
   765  			dsts = []string{""}
   766  		}
   767  
   768  		// Upload destination
   769  		var perm int64
   770  		if perms, ok := r.URL.Query()["perm"]; ok {
   771  			if perm, err = strconv.ParseInt(perms[0], 8, 32); err != nil {
   772  				errMsg = err.Error()
   773  				return
   774  			}
   775  		}
   776  
   777  		// Check only
   778  		if r.Method == "GET" {
   779  			// If we are checking only, we need a extra filename parameters since
   780  			// we don't have a form to supply the filename.
   781  			var filenames []string
   782  			if filenames, ok = r.URL.Query()["filename"]; !ok {
   783  				filenames = []string{""}
   784  			}
   785  
   786  			agent.Command <- SpawnFileCmd{sid, terminalSids[0], "upload",
   787  				filenames[0], dsts[0], int(perm), true}
   788  
   789  			errMsg = <-agent.Response
   790  			return
   791  		}
   792  
   793  		mr, err := r.MultipartReader()
   794  		if err != nil {
   795  			errMsg = err.Error()
   796  			return
   797  		}
   798  
   799  		p, err := mr.NextPart()
   800  		if err != nil {
   801  			errMsg = err.Error()
   802  			return
   803  		}
   804  
   805  		agent.Command <- SpawnFileCmd{sid, terminalSids[0], "upload",
   806  			p.FileName(), dsts[0], int(perm), false}
   807  
   808  		res := <-agent.Response
   809  		if res != "" {
   810  			http.NotFound(w, r)
   811  			return
   812  		}
   813  
   814  		const maxTries = 100 // 20 seconds
   815  		count := 0
   816  
   817  		// Wait until upload client connects
   818  		var c *ConnServer
   819  		for {
   820  			if count++; count == maxTries {
   821  				http.Error(w, "no response from client", http.StatusInternalServerError)
   822  				return
   823  			}
   824  			ovl.uploadsMu.Lock()
   825  			if c, ok = ovl.uploads[sid]; ok {
   826  				ovl.uploadsMu.Unlock()
   827  				break
   828  			}
   829  			ovl.uploadsMu.Unlock()
   830  			time.Sleep(200 * time.Millisecond)
   831  		}
   832  
   833  		_, err = io.Copy(c.Conn, p)
   834  		c.StopListen()
   835  
   836  		if err != nil {
   837  			errMsg = err.Error()
   838  			return
   839  		}
   840  		w.Write([]byte(`{"status": "success"}`))
   841  		return
   842  	}
   843  
   844  	// Port forwarding request handler
   845  	AgentModeForwardHandler := func(w http.ResponseWriter, r *http.Request) {
   846  		log.Printf("ModeForward request from %s\n", r.RemoteAddr)
   847  		conn, err := upgrader.Upgrade(w, r, nil)
   848  		if err != nil {
   849  			log.Println(err)
   850  			return
   851  		}
   852  
   853  		var port int
   854  
   855  		vars := mux.Vars(r)
   856  		mid := vars["mid"]
   857  		if _port, ok := r.URL.Query()["port"]; ok {
   858  			if port, err = strconv.Atoi(_port[0]); err != nil {
   859  				WebSocketSendError(conn, "invalid port")
   860  				return
   861  			}
   862  		} else {
   863  			WebSocketSendError(conn, "no port specified")
   864  			return
   865  		}
   866  
   867  		ovl.agentsMu.Lock()
   868  		if agent, ok := ovl.agents[mid]; ok {
   869  			ovl.agentsMu.Unlock()
   870  			wc := newWebsocketContext(conn)
   871  			ovl.AddWebsocketContext(wc)
   872  			agent.Command <- SpawnModeForwarderCmd{wc.Sid, port}
   873  			if res := <-agent.Response; res != "" {
   874  				WebSocketSendError(conn, res)
   875  			}
   876  		} else {
   877  			ovl.agentsMu.Unlock()
   878  			WebSocketSendError(conn, "No client with mid "+mid)
   879  		}
   880  	}
   881  
   882  	// Initialize socket IO server
   883  	ovl.InitSocketIOServer()
   884  
   885  	appDir := ovl.GetAppDir()
   886  
   887  	// HTTP basic auth
   888  	auth := NewBasicAuth("Overlord", ovl.htpasswdPath, !ovl.auth)
   889  
   890  	http.HandleFunc("/connect", GhostConnectHandler)
   891  
   892  	// Register the request handlers.
   893  	r := mux.NewRouter()
   894  
   895  	r.HandleFunc("/api/apps/list", AppsListHandler)
   896  	r.HandleFunc("/api/agents/list", AgentsListHandler)
   897  	r.HandleFunc("/api/agents/upgrade", AgentsUpgradeHandler)
   898  	r.HandleFunc("/api/logcats/list", LogcatsListHandler)
   899  
   900  	// Logcat methods
   901  	r.HandleFunc("/api/log/{mid}/{sid}", LogcatHandler)
   902  
   903  	// Agent methods
   904  	r.HandleFunc("/api/agent/tty/{mid}", AgentTtyHandler)
   905  	r.HandleFunc("/api/agent/shell/{mid}", ModeShellHandler)
   906  	r.HandleFunc("/api/agent/properties/{mid}", AgentPropertiesHandler)
   907  	r.HandleFunc("/api/agent/download/{mid}", AgentDownloadHandler)
   908  	r.HandleFunc("/api/agent/upload/{mid}", AgentUploadHandler)
   909  	r.HandleFunc("/api/agent/forward/{mid}", AgentModeForwardHandler)
   910  
   911  	// File methods
   912  	r.HandleFunc("/api/file/download/{sid}", FileDownloadHandler)
   913  
   914  	http.Handle("/api/", auth.WrapHandler(r))
   915  	http.Handle("/api/socket.io/", auth.WrapHandler(ovl.ioserver))
   916  
   917  	// /upgrade/ does not need authenticiation
   918  	http.Handle("/upgrade/", http.StripPrefix("/upgrade/",
   919  		http.FileServer(http.Dir(filepath.Join(appDir, "upgrade")))))
   920  	http.Handle("/vendor/", auth.WrapHandler(http.FileServer(
   921  		http.Dir(filepath.Join(appDir, "common")))))
   922  	http.Handle("/", auth.WrapHandler(http.FileServer(
   923  		http.Dir(filepath.Join(appDir, "dashboard")))))
   924  
   925  	// Serve all apps
   926  	appNames, err := ovl.GetAppNames(false)
   927  	if err != nil {
   928  		log.Fatalln(err)
   929  	}
   930  
   931  	for _, app := range appNames {
   932  		if app == "upgrade" {
   933  			continue
   934  		}
   935  		if app != "common" && app != "third_party" {
   936  			log.Printf("App `%s' registered\n", app)
   937  		}
   938  		prefix := fmt.Sprintf("/%s/", app)
   939  		http.Handle(prefix, http.StripPrefix(prefix,
   940  			auth.WrapHandler(http.FileServer(http.Dir(filepath.Join(appDir, app))))))
   941  	}
   942  }
   943  
   944  // ServHTTP is the Web server main routine.
   945  func (ovl *Overlord) ServHTTP() {
   946  	var err error
   947  
   948  	tlsStatus := "disabled"
   949  	if ovl.certs != nil {
   950  		tlsStatus = "enabled"
   951  	}
   952  	if ovl.port == 0 {
   953  		if ovl.certs != nil {
   954  			ovl.port = DefaultHTTPSPort
   955  		} else {
   956  			ovl.port = DefaultHTTPPort
   957  		}
   958  	}
   959  
   960  	addr := fmt.Sprintf("%s:%d", ovl.bindAddr, ovl.port)
   961  	log.Printf("HTTP server started, listening at %s, TLS: %s",
   962  		addr, tlsStatus)
   963  
   964  	if ovl.certs != nil {
   965  		err = http.ListenAndServeTLS(addr, ovl.certs.Cert, ovl.certs.Key, nil)
   966  	} else {
   967  		err = http.ListenAndServe(addr, nil)
   968  	}
   969  	if err != nil {
   970  		log.Fatalf("net.http could not listen on address '%s': %s\n",
   971  			addr, err)
   972  	}
   973  }
   974  
   975  // StartUDPBroadcast is the main routine for broadcasting LAN discovery message.
   976  func (ovl *Overlord) StartUDPBroadcast(port int) {
   977  	ifaceIP := ""
   978  	bcastIP := net.IPv4bcast.String()
   979  
   980  	if ovl.lanDiscInterface != "" {
   981  		interfaces, err := net.Interfaces()
   982  		if err != nil {
   983  			panic(err)
   984  		}
   985  
   986  	outter:
   987  		for _, iface := range interfaces {
   988  			if iface.Name == ovl.lanDiscInterface {
   989  				addrs, err := iface.Addrs()
   990  				if err != nil {
   991  					panic(err)
   992  				}
   993  				for _, addr := range addrs {
   994  					ip, ipnet, err := net.ParseCIDR(addr.String())
   995  					if err != nil {
   996  						continue
   997  					}
   998  					// Calculate broadcast IP
   999  					ip4 := ip.To4()
  1000  
  1001  					// We only care about IPv4 address
  1002  					if ip4 == nil {
  1003  						continue
  1004  					}
  1005  					bcastIPraw := make(net.IP, 4)
  1006  					for i := 0; i < 4; i++ {
  1007  						bcastIPraw[i] = ip4[i] | ^ipnet.Mask[i]
  1008  					}
  1009  					ifaceIP = ip.String()
  1010  					bcastIP = bcastIPraw.String()
  1011  					break outter
  1012  				}
  1013  			}
  1014  		}
  1015  
  1016  		if ifaceIP == "" {
  1017  			log.Fatalf("can not found any interface with name %s\n",
  1018  				ovl.lanDiscInterface)
  1019  		}
  1020  	}
  1021  
  1022  	addr := fmt.Sprintf("%s:%d", bcastIP, port)
  1023  	conn, err := net.Dial("udp", addr)
  1024  	if err != nil {
  1025  		log.Fatalln("Unable to start UDP broadcast:", err)
  1026  	}
  1027  
  1028  	log.Printf("UDP Broadcasting started, broadcasting at %s", addr)
  1029  
  1030  	ticker := time.NewTicker(time.Duration(ldInterval * time.Second))
  1031  
  1032  	for {
  1033  		select {
  1034  		case <-ticker.C:
  1035  			conn.Write([]byte(fmt.Sprintf("OVERLORD %s:%d", ifaceIP, ovl.port)))
  1036  		}
  1037  	}
  1038  }
  1039  
  1040  // Serv is the main routine for starting all the overlord sub-server.
  1041  func (ovl *Overlord) Serv() {
  1042  	ovl.RegisterHTTPHandlers()
  1043  	go ovl.ServHTTP()
  1044  	if ovl.lanDisc {
  1045  		go ovl.StartUDPBroadcast(OverlordLDPort)
  1046  	}
  1047  
  1048  	ticker := time.NewTicker(time.Duration(60 * time.Second))
  1049  
  1050  	for {
  1051  		select {
  1052  		case <-ticker.C:
  1053  			log.Printf("#Goroutines, #Ghostclient: %d, %d\n",
  1054  				runtime.NumGoroutine(), len(ovl.agents))
  1055  		}
  1056  	}
  1057  }
  1058  
  1059  // StartOverlord starts the overlord server.
  1060  func StartOverlord(bindAddr string, port int, lanDiscInterface string, lanDisc bool, auth bool,
  1061  	certsString string, linkTLS bool, htpasswdPath string) {
  1062  	ovl := NewOverlord(bindAddr, port, lanDiscInterface, lanDisc, auth,
  1063  		certsString, linkTLS, htpasswdPath)
  1064  	ovl.Serv()
  1065  }