WIP
This commit is contained in:
		| @@ -16,13 +16,11 @@ type TX interface { | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| type Config struct { | ||||
| 	ConfigID   int64 | ||||
| 	HubAddress string | ||||
| 	VPNNetwork []byte | ||||
| 	Password   []byte | ||||
| 	ConfigID int64 | ||||
| 	Password []byte | ||||
| } | ||||
|  | ||||
| const Config_SelectQuery = "SELECT ConfigID,HubAddress,VPNNetwork,Password FROM config" | ||||
| const Config_SelectQuery = "SELECT ConfigID,Password FROM config" | ||||
|  | ||||
| func Config_Insert( | ||||
| 	tx TX, | ||||
| @@ -33,7 +31,7 @@ func Config_Insert( | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	_, err = tx.Exec("INSERT INTO config(ConfigID,HubAddress,VPNNetwork,Password) VALUES(?,?,?,?)", row.ConfigID, row.HubAddress, row.VPNNetwork, row.Password) | ||||
| 	_, err = tx.Exec("INSERT INTO config(ConfigID,Password) VALUES(?,?)", row.ConfigID, row.Password) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| @@ -46,7 +44,7 @@ func Config_Update( | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	result, err := tx.Exec("UPDATE config SET HubAddress=?,VPNNetwork=? WHERE ConfigID=?", row.HubAddress, row.VPNNetwork, row.ConfigID) | ||||
| 	result, err := tx.Exec("UPDATE config SET Password=? WHERE ConfigID=?", row.Password, row.ConfigID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -74,7 +72,7 @@ func Config_UpdateFull( | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	result, err := tx.Exec("UPDATE config SET HubAddress=?,VPNNetwork=?,Password=? WHERE ConfigID=?", row.HubAddress, row.VPNNetwork, row.Password, row.ConfigID) | ||||
| 	result, err := tx.Exec("UPDATE config SET Password=? WHERE ConfigID=?", row.Password, row.ConfigID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -124,8 +122,8 @@ func Config_Get( | ||||
| 	err error, | ||||
| ) { | ||||
| 	row = &Config{} | ||||
| 	r := tx.QueryRow("SELECT ConfigID,HubAddress,VPNNetwork,Password FROM config WHERE ConfigID=?", ConfigID) | ||||
| 	err = r.Scan(&row.ConfigID, &row.HubAddress, &row.VPNNetwork, &row.Password) | ||||
| 	r := tx.QueryRow("SELECT ConfigID,Password FROM config WHERE ConfigID=?", ConfigID) | ||||
| 	err = r.Scan(&row.ConfigID, &row.Password) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| @@ -139,7 +137,7 @@ func Config_GetWhere( | ||||
| ) { | ||||
| 	row = &Config{} | ||||
| 	r := tx.QueryRow(query, args...) | ||||
| 	err = r.Scan(&row.ConfigID, &row.HubAddress, &row.VPNNetwork, &row.Password) | ||||
| 	err = r.Scan(&row.ConfigID, &row.Password) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| @@ -159,7 +157,7 @@ func Config_Iterate( | ||||
| 		defer rows.Close() | ||||
| 		for rows.Next() { | ||||
| 			row := &Config{} | ||||
| 			err := rows.Scan(&row.ConfigID, &row.HubAddress, &row.VPNNetwork, &row.Password) | ||||
| 			err := rows.Scan(&row.ConfigID, &row.Password) | ||||
| 			if !yield(row, err) { | ||||
| 				return | ||||
| 			} | ||||
| @@ -302,11 +300,156 @@ func Session_List( | ||||
| 	return l, nil | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Table: networks | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| type Network struct { | ||||
| 	NetworkID int64 | ||||
| 	Name      string | ||||
| 	Network   []byte | ||||
| } | ||||
|  | ||||
| const Network_SelectQuery = "SELECT NetworkID,Name,Network FROM networks" | ||||
|  | ||||
| func Network_Insert( | ||||
| 	tx TX, | ||||
| 	row *Network, | ||||
| ) (err error) { | ||||
| 	Network_Sanitize(row) | ||||
| 	if err = Network_Validate(row); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	_, err = tx.Exec("INSERT INTO networks(NetworkID,Name,Network) VALUES(?,?,?)", row.NetworkID, row.Name, row.Network) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func Network_UpdateFull( | ||||
| 	tx TX, | ||||
| 	row *Network, | ||||
| ) (err error) { | ||||
| 	Network_Sanitize(row) | ||||
| 	if err = Network_Validate(row); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	result, err := tx.Exec("UPDATE networks SET Name=?,Network=? WHERE NetworkID=?", row.Name, row.Network, row.NetworkID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	n, err := result.RowsAffected() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	switch n { | ||||
| 	case 0: | ||||
| 		return sql.ErrNoRows | ||||
| 	case 1: | ||||
| 		return nil | ||||
| 	default: | ||||
| 		panic("multiple rows updated") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func Network_Delete( | ||||
| 	tx TX, | ||||
| 	NetworkID int64, | ||||
| ) (err error) { | ||||
| 	result, err := tx.Exec("DELETE FROM networks WHERE NetworkID=?", NetworkID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	n, err := result.RowsAffected() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	switch n { | ||||
| 	case 0: | ||||
| 		return sql.ErrNoRows | ||||
| 	case 1: | ||||
| 		return nil | ||||
| 	default: | ||||
| 		panic("multiple rows deleted") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func Network_Get( | ||||
| 	tx TX, | ||||
| 	NetworkID int64, | ||||
| ) ( | ||||
| 	row *Network, | ||||
| 	err error, | ||||
| ) { | ||||
| 	row = &Network{} | ||||
| 	r := tx.QueryRow("SELECT NetworkID,Name,Network FROM networks WHERE NetworkID=?", NetworkID) | ||||
| 	err = r.Scan(&row.NetworkID, &row.Name, &row.Network) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func Network_GetWhere( | ||||
| 	tx TX, | ||||
| 	query string, | ||||
| 	args ...any, | ||||
| ) ( | ||||
| 	row *Network, | ||||
| 	err error, | ||||
| ) { | ||||
| 	row = &Network{} | ||||
| 	r := tx.QueryRow(query, args...) | ||||
| 	err = r.Scan(&row.NetworkID, &row.Name, &row.Network) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func Network_Iterate( | ||||
| 	tx TX, | ||||
| 	query string, | ||||
| 	args ...any, | ||||
| ) iter.Seq2[*Network, error] { | ||||
| 	rows, err := tx.Query(query, args...) | ||||
| 	if err != nil { | ||||
| 		return func(yield func(*Network, error) bool) { | ||||
| 			yield(nil, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return func(yield func(*Network, error) bool) { | ||||
| 		defer rows.Close() | ||||
| 		for rows.Next() { | ||||
| 			row := &Network{} | ||||
| 			err := rows.Scan(&row.NetworkID, &row.Name, &row.Network) | ||||
| 			if !yield(row, err) { | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func Network_List( | ||||
| 	tx TX, | ||||
| 	query string, | ||||
| 	args ...any, | ||||
| ) ( | ||||
| 	l []*Network, | ||||
| 	err error, | ||||
| ) { | ||||
| 	for row, err := range Network_Iterate(tx, query, args...) { | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		l = append(l, row) | ||||
| 	} | ||||
| 	return l, nil | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Table: peers | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| type Peer struct { | ||||
| 	NetworkID  int64 | ||||
| 	PeerIP     byte | ||||
| 	Version    int64 | ||||
| 	APIKey     string | ||||
| @@ -318,7 +461,7 @@ type Peer struct { | ||||
| 	PubSignKey []byte | ||||
| } | ||||
|  | ||||
| const Peer_SelectQuery = "SELECT PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers" | ||||
| const Peer_SelectQuery = "SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers" | ||||
|  | ||||
| func Peer_Insert( | ||||
| 	tx TX, | ||||
| @@ -329,7 +472,7 @@ func Peer_Insert( | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	_, err = tx.Exec("INSERT INTO peers(PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey) VALUES(?,?,?,?,?,?,?,?,?)", row.PeerIP, row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Relay, row.PubKey, row.PubSignKey) | ||||
| 	_, err = tx.Exec("INSERT INTO peers(NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey) VALUES(?,?,?,?,?,?,?,?,?,?)", row.NetworkID, row.PeerIP, row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Relay, row.PubKey, row.PubSignKey) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| @@ -342,7 +485,7 @@ func Peer_Update( | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	result, err := tx.Exec("UPDATE peers SET Version=?,Name=?,PublicIP=?,Port=?,Relay=? WHERE PeerIP=?", row.Version, row.Name, row.PublicIP, row.Port, row.Relay, row.PeerIP) | ||||
| 	result, err := tx.Exec("UPDATE peers SET Version=?,Name=?,PublicIP=?,Port=?,Relay=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.Name, row.PublicIP, row.Port, row.Relay, row.NetworkID, row.PeerIP) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -370,7 +513,7 @@ func Peer_UpdateFull( | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,PublicIP=?,Port=?,Relay=?,PubKey=?,PubSignKey=? WHERE PeerIP=?", row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Relay, row.PubKey, row.PubSignKey, row.PeerIP) | ||||
| 	result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,PublicIP=?,Port=?,Relay=?,PubKey=?,PubSignKey=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Relay, row.PubKey, row.PubSignKey, row.NetworkID, row.PeerIP) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -391,9 +534,10 @@ func Peer_UpdateFull( | ||||
|  | ||||
| func Peer_Delete( | ||||
| 	tx TX, | ||||
| 	NetworkID int64, | ||||
| 	PeerIP byte, | ||||
| ) (err error) { | ||||
| 	result, err := tx.Exec("DELETE FROM peers WHERE PeerIP=?", PeerIP) | ||||
| 	result, err := tx.Exec("DELETE FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -414,14 +558,15 @@ func Peer_Delete( | ||||
|  | ||||
| func Peer_Get( | ||||
| 	tx TX, | ||||
| 	NetworkID int64, | ||||
| 	PeerIP byte, | ||||
| ) ( | ||||
| 	row *Peer, | ||||
| 	err error, | ||||
| ) { | ||||
| 	row = &Peer{} | ||||
| 	r := tx.QueryRow("SELECT PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers WHERE PeerIP=?", PeerIP) | ||||
| 	err = r.Scan(&row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) | ||||
| 	r := tx.QueryRow("SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP) | ||||
| 	err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| @@ -435,7 +580,7 @@ func Peer_GetWhere( | ||||
| ) { | ||||
| 	row = &Peer{} | ||||
| 	r := tx.QueryRow(query, args...) | ||||
| 	err = r.Scan(&row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) | ||||
| 	err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| @@ -455,7 +600,7 @@ func Peer_Iterate( | ||||
| 		defer rows.Close() | ||||
| 		for rows.Next() { | ||||
| 			row := &Peer{} | ||||
| 			err := rows.Scan(&row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) | ||||
| 			err := rows.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) | ||||
| 			if !yield(row, err) { | ||||
| 				return | ||||
| 			} | ||||
|   | ||||
| @@ -3,35 +3,21 @@ package db | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"net/netip" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrInvalidIP   = errors.New("invalid IP") | ||||
| 	ErrInvalidPort = errors.New("invalid port") | ||||
| 	ErrInvalidIP       = errors.New("invalid IP") | ||||
| 	ErrNonPrivateIP    = errors.New("non-private IP") | ||||
| 	ErrInvalidPort     = errors.New("invalid port") | ||||
| 	ErrInvalidNetName  = errors.New("invalid network name") | ||||
| 	ErrInvalidPeerName = errors.New("invalid peer name") | ||||
| ) | ||||
|  | ||||
| func Config_Sanitize(c *Config) { | ||||
| 	if u, err := url.Parse(c.HubAddress); err == nil { | ||||
| 		c.HubAddress = u.String() | ||||
| 	} | ||||
|  | ||||
| 	if addr, ok := netip.AddrFromSlice(c.VPNNetwork); ok { | ||||
| 		c.VPNNetwork = addr.AsSlice() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func Config_Validate(c *Config) error { | ||||
| 	if _, err := url.Parse(c.HubAddress); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	addr, ok := netip.AddrFromSlice(c.VPNNetwork) | ||||
| 	if !ok || !addr.Is4() || addr.As4()[3] != 0 || addr.As4()[0] == 0 { | ||||
| 		return ErrInvalidIP | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @@ -42,6 +28,42 @@ func Session_Validate(s *Session) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func Network_Sanitize(n *Network) { | ||||
| 	n.Name = strings.TrimSpace(n.Name) | ||||
|  | ||||
| 	if addr, ok := netip.AddrFromSlice(n.Network); ok { | ||||
| 		n.Network = addr.AsSlice() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func Network_Validate(c *Network) error { | ||||
| 	// 16 bytes is linux limit for network interface names. | ||||
| 	if len(c.Name) == 0 || len(c.Name) > 16 { | ||||
| 		return ErrInvalidNetName | ||||
| 	} | ||||
|  | ||||
| 	for _, c := range c.Name { | ||||
| 		if c >= 'a' && c <= 'z' { | ||||
| 			continue | ||||
| 		} | ||||
| 		if c >= '0' && c <= '9' { | ||||
| 			continue | ||||
| 		} | ||||
| 		return ErrInvalidNetName | ||||
| 	} | ||||
|  | ||||
| 	addr, ok := netip.AddrFromSlice(c.Network) | ||||
| 	if !ok || !addr.Is4() || addr.As4()[3] != 0 || addr.As4()[0] == 0 { | ||||
| 		return ErrInvalidIP | ||||
| 	} | ||||
|  | ||||
| 	if !addr.IsPrivate() { | ||||
| 		return ErrNonPrivateIP | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func Peer_Sanitize(p *Peer) { | ||||
| 	p.Name = strings.TrimSpace(p.Name) | ||||
| 	if len(p.PublicIP) != 0 { | ||||
| @@ -65,5 +87,20 @@ func Peer_Validate(p *Peer) error { | ||||
| 	if p.Port == 0 { | ||||
| 		return ErrInvalidPort | ||||
| 	} | ||||
|  | ||||
| 	for _, c := range p.Name { | ||||
| 		if c >= 'a' && c <= 'z' { | ||||
| 			continue | ||||
| 		} | ||||
| 		if c >= '0' && c <= '9' { | ||||
| 			continue | ||||
| 		} | ||||
| 		if c == '.' || c == '-' || c == '_' { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		return ErrInvalidPeerName | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,6 @@ | ||||
| TABLE config OF Config ( | ||||
|   ConfigID    int64  PK, | ||||
|   HubAddress  string, | ||||
|   VPNNetwork  []byte, | ||||
|   Password    []byte NoUpdate | ||||
|   Password    []byte | ||||
| ); | ||||
|  | ||||
| TABLE sessions OF Session NoUpdate ( | ||||
| @@ -13,7 +11,14 @@ TABLE sessions OF Session NoUpdate ( | ||||
|   LastSeenAt int64 | ||||
| ); | ||||
|  | ||||
| TABLE networks OF Network ( | ||||
|   NetworkID int64 PK, | ||||
|   Name      string NoUpdate, | ||||
|   Network   []byte NoUpdate | ||||
| ); | ||||
|  | ||||
| TABLE peers OF Peer ( | ||||
|   NetworkID  int64 PK, | ||||
|   PeerIP     byte PK, | ||||
|   Version    int64, | ||||
|   APIKey     string NoUpdate, | ||||
|   | ||||
| @@ -26,16 +26,9 @@ func Session_DeleteBefore( | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func Config_UpdatePassword( | ||||
| 	tx TX, | ||||
| 	pwdHash []byte, | ||||
| ) (err error) { | ||||
| 	_, err = tx.Exec("UPDATE config SET Password=? WHERE ConfigID=1", pwdHash) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func Peer_ListAll(tx TX) ([]*Peer, error) { | ||||
| 	return Peer_List(tx, Peer_SelectQuery) | ||||
| func Peer_ListAll(tx TX, networkID int64) ([]*Peer, error) { | ||||
| 	const query = Peer_SelectQuery + ` WHERE NetworkID=? ORDER BY PeerIP ASC` | ||||
| 	return Peer_List(tx, query, networkID) | ||||
| } | ||||
|  | ||||
| func Peer_GetByAPIKey(tx TX, apiKey string) (*Peer, error) { | ||||
| @@ -45,7 +38,8 @@ func Peer_GetByAPIKey(tx TX, apiKey string) (*Peer, error) { | ||||
| 		apiKey) | ||||
| } | ||||
|  | ||||
| func Peer_Exists(tx TX, ip byte) (exists bool, err error) { | ||||
| 	err = tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM peers WHERE PeerIP=?)`, ip).Scan(&exists) | ||||
| func Peer_Exists(tx TX, networkID int64, ip byte) (exists bool, err error) { | ||||
| 	const query = `SELECT EXISTS(SELECT 1 FROM peers WHERE NetworkID=? AND PeerIP=?)` | ||||
| 	err = tx.QueryRow(query, networkID, ip).Scan(&exists) | ||||
| 	return | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user