1// Copyright (C) 2019 Storj Labs, Inc. 2// See LICENSE for copying information. 3 4package trust 5 6import ( 7 "bufio" 8 "context" 9 "io" 10 "net" 11 "strconv" 12 "strings" 13 14 "github.com/zeebo/errs" 15 16 "storj.io/common/storj" 17) 18 19var ( 20 // ErrSatelliteURL is an error class for satellite URL related errors. 21 ErrSatelliteURL = errs.Class("invalid satellite URL") 22) 23 24// SatelliteURL represents a Satellite URL. 25type SatelliteURL struct { 26 ID storj.NodeID `json:"id"` 27 Host string `json:"host"` 28 Port int `json:"port"` 29} 30 31// Address returns the address (i.e. host:port) of the Satellite. 32func (u *SatelliteURL) Address() string { 33 return net.JoinHostPort(u.Host, strconv.Itoa(u.Port)) 34} 35 36// NodeURL returns a full Node URL to the Satellite. 37func (u *SatelliteURL) NodeURL() storj.NodeURL { 38 return storj.NodeURL{ 39 ID: u.ID, 40 Address: u.Address(), 41 } 42} 43 44// String returns a string representation of the Satellite URL. 45func (u *SatelliteURL) String() string { 46 return u.ID.String() + "@" + u.Address() 47} 48 49// ParseSatelliteURL parses a Satellite URL. For the purposes of the trust list, 50// the Satellite URL MUST contain both an ID and port designation. 51func ParseSatelliteURL(s string) (SatelliteURL, error) { 52 url, err := storj.ParseNodeURL(s) 53 if err != nil { 54 return SatelliteURL{}, ErrSatelliteURL.Wrap(err) 55 } 56 if url.ID.IsZero() { 57 return SatelliteURL{}, ErrSatelliteURL.New("must contain an ID") 58 } 59 60 if url.Address == "" { 61 return SatelliteURL{}, ErrSatelliteURL.New("must specify the host:port") 62 } 63 64 // storj.ParseNodeURL will have already verified that the address is 65 // well-formed, so if SplitHostPort fails it should be due to the address 66 // not having a port 67 host, portStr, err := net.SplitHostPort(url.Address) 68 if err != nil { 69 return SatelliteURL{}, ErrSatelliteURL.New("must specify the port") 70 } 71 72 // Port should already be numeric so this shouldn't fail, but just in case. 73 port, err := strconv.Atoi(portStr) 74 if err != nil { 75 return SatelliteURL{}, ErrSatelliteURL.New("port is not numeric") 76 } 77 78 return SatelliteURL{ 79 ID: url.ID, 80 Host: host, 81 Port: port, 82 }, nil 83} 84 85// ParseSatelliteURLList parses a newline separated list of Satellite URLs. 86// Empty lines or lines starting with '#' (comments) are ignored. 87func ParseSatelliteURLList(ctx context.Context, r io.Reader) (urls []SatelliteURL, err error) { 88 defer mon.Task()(&ctx)(&err) 89 90 scanner := bufio.NewScanner(r) 91 for scanner.Scan() { 92 line := strings.TrimSpace(scanner.Text()) 93 if len(line) == 0 { 94 continue 95 } 96 if line[0] == '#' { 97 continue 98 } 99 100 url, err := ParseSatelliteURL(line) 101 if err != nil { 102 return nil, err 103 } 104 urls = append(urls, url) 105 } 106 107 if err := scanner.Err(); err != nil { 108 return nil, Error.Wrap(err) 109 } 110 111 return urls, nil 112} 113