package service import "os" import "log" import "fmt" import "net" import "errors" import "strings" import "crypto/tls" import "encoding/base64" import "hnakra/protocol" // M creates a very basic MountConfig with the specified name and description. func M (name, description string) MountConfig { return MountConfig { Name: name, Description: description, } } // Mount is an interface satisfied by all mount types. type Mount interface { Close () error Shutdown () error Run () error } // MountConfig contains generic information common to all mounts. type MountConfig struct { // Host specifies the host to mount on. If the host is left empty, it // will default to @ (meaning default/any host). The port is entirely up // to the router. Maximum length for host portion: 255 bytes Host string // Scheme specifies the protocol to mount on. This will be automatically // set by specialized mount types, so setting it manually shouldn't be // needed. Scheme string // Path specifies the path to mount on. If the path ends with a /, then // all requests under the path will be sent to this service. If there is // no trailing /, this service will only recieve requests that match the // path exactly (when normalized). // Maximum length: 2^16-1 bytes Path string // Router specifies the host:port of the router to connect to. This // defaults to $HNAKRA_ROUTER_HOST:$HNAKRA_ROUTER_PORT if left empty. // The default value of these environment variables (if not set) is // localhost:2048. If you are giving this service to other people, it is // a good idea to leave this empty. Router string // Name should be set to a human-readable name of the service. // Maximum length: 255 bytes Name string // Description should be set to a human-readable description of what the // service does. // Maximum length: 255 bytes Description string // User is an optional string that specifies who is connecting to the // router. This can be used by routers in conjunction with the key // bytes in order to enforce rules about which service can do what. This // defaults to $HNAKRA_USER if left empty. If you are giving this // service to other people, it is a good idea to leave this empty. // Maximum length: 255 bytes User string // Key is an optional byte slice that is sent to the router in order for // it to authorize the connection. If nil, this defaults to the contents // of $HNAKRA_KEY interpreted as base64. Do not embed a key in your // code - you should only use this if your service reads the key from a // safe source upon startup. In addition to this, If you choose to set // the key via the aforementioned enviroment variable, ensure that it // can only be read by the service. // Maximum length: 255 bytes Key []byte // TLSConfig is an optional TLS configuration. TLSConfig *tls.Config } // Connect creates a new connection to the router specified in the MountConfig. func (mount *MountConfig) Connect () ( conn net.Conn, idFactory *protocol.IDFactory, err error, ) { log.Println("(i) service", mount.Name) idFactory = protocol.NewServiceIDFactory() defaultRouterHost := os.Getenv("HNAKRA_ROUTER_HOST") if defaultRouterHost == "" { defaultRouterHost = "localhost" } defaultRouterPort := os.Getenv("HNAKRA_ROUTER_PORT") if defaultRouterPort == "" { defaultRouterPort = "2048" } // parse router host/port routerHost, routerPort, _ := strings.Cut(mount.Router, ":") if routerHost == "" { routerHost = defaultRouterHost } if routerPort == "" { routerPort = defaultRouterPort } // get mount point scheme := mount.Scheme host := mount.Host if host == "" { host = "@" } if len(host) > 255 { return nil, nil, errors.New ( "mount point host cannot be longer than 255 bytes") } path := mount.Path if path == "" { path = "/" } if len(path) > int(protocol.MaxIntOfSize(2)) { return nil, nil, errors.New(fmt.Sprint ( "mount point path cannot be longer than ", protocol.MaxIntOfSize(2), " bytes")) } // get user user := mount.User if user == "" { user = os.Getenv("HNAKRA_USER") } if len(user) > 255 { return nil, nil, errors.New ( "user cannot be longer than 255 bytes") } // get key key := mount.Key if key == nil { base64Key := os.Getenv("HNAKRA_KEY") key, err = base64.StdEncoding.DecodeString(base64Key) if err != nil { return nil, nil, err } } if len(key) > 255 { return nil, nil, errors.New ( "key cannot be longer than 255 bytes") } // ensure name/description aren't too big if len(mount.Name) > 255 { return nil, nil, errors.New ( "service name cannot be longer than 255 bytes") } if len(mount.Description) > 255 { return nil, nil, errors.New ( "service description cannot be longer than 255 bytes") } // connect to router routerAddr := fmt.Sprint(routerHost, ":", routerPort) log.Println("... dialing", routerAddr) conn, err = tls.Dial("tcp", routerAddr, mount.TLSConfig) if err != nil { return nil, nil, err } // log in log.Println ( "... logging in as", user, "on", scheme + "://" + host + path) err = protocol.MessageLogin { ID: idFactory.Next(), Version: protocol.Version { Major: 0, Minor: 0 }, User: user, Key: key, Name: mount.Name, Description: mount.Description, Scheme: scheme, Host: host, Path: path, }.Send(conn) if err != nil { conn.Close() return nil, nil, err } // read status message, err := protocol.ReadMessage(conn) if err != nil { conn.Close() return nil, nil, err } status, ok := message.(protocol.MessageStatus) if !ok { conn.Close() return nil, nil, errors.New(fmt.Sprint ( "router sent unknown type, expecting", protocol.TypeStatus)) } if status.Status != protocol.StatusOk { return nil, nil, status } log.Println(".// logged in") return conn, idFactory, nil }