aboutsummaryrefslogtreecommitdiff
path: root/src/gnunet/util/address.go
blob: bf6f562a0c9b2f5b5a763f3c8ed5346f53ab9a19 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// This file is part of gnunet-go, a GNUnet-implementation in Golang.
// Copyright (C) 2019-2022 Bernd Fix  >Y<
//
// gnunet-go is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// gnunet-go is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: AGPL3.0-or-later

package util

import (
	"bytes"
	"fmt"
	"net"
	"strings"
)

// Address specifies how a peer is reachable on the network.
type Address struct {
	Netw    string       // network protocol
	Options uint32       // address options
	Expires AbsoluteTime // expiration date for address
	Address []byte       // address data (protocol-dependent)
}

// NewAddress returns a new Address for the given transport and specs
func NewAddress(transport string, addr string) *Address {
	return &Address{
		Netw:    transport,
		Options: 0,
		Address: Clone([]byte(addr)),
		Expires: AbsoluteTimeNever(),
	}
}

// NewAddressWrap returns new address from net.Addr with no options
// or expiry date.
func NewAddressWrap(addr net.Addr) *Address {
	return &Address{
		Netw:    addr.Network(),
		Options: 0,
		Address: []byte(addr.String()),
		Expires: AbsoluteTimeNever(),
	}
}

// ParseAddress translates a GNUnet address string like
// "ip+udp://1.2.3.4:6789" or "gnunet+tcp://12.3.4.5/".
// It can also handle standard strings like "udp:127.0.0.1:6735".
func ParseAddress(s string) (addr *Address, err error) {
	p := strings.SplitN(s, ":", 2)
	if len(p) != 2 {
		err = fmt.Errorf("invalid address format: '%s'", s)
		return
	}
	addr = NewAddress(p[0], strings.Trim(p[1], "/"))
	return
}

// Equals return true if two addresses match.
func (a *Address) Equals(b *Address) bool {
	return a.Netw == b.Netw &&
		a.Options == b.Options &&
		bytes.Equal(a.Address, b.Address)
}

// implement net.Addr interface methods:

// String returns a human-readable representation of an address.
func (a *Address) String() string {
	return string(a.Address)
}

// Network returns the protocol specifier.
func (a *Address) Network() string {
	return a.Netw
}

//----------------------------------------------------------------------

// URI returns a string representation of an address.
func (a *Address) URI() string {
	return URI(a.Netw, a.Address)
}
func URI(network string, addr []byte) string {
	return network + "://" + string(addr)
}

//----------------------------------------------------------------------

// PeerAddrList is a list of addresses per peer ID.
type PeerAddrList struct {
	list *Map[string, []*Address]
}

// NewPeerAddrList returns a new and empty address list.
func NewPeerAddrList() *PeerAddrList {
	return &PeerAddrList{
		list: NewMap[string, []*Address](),
	}
}

// Add address for peer. The returned mode is 0=not added, 1=new peer,
// 2=new address
func (a *PeerAddrList) Add(peer *PeerID, addr *Address) (mode int) {
	// check for expired address.
	mode = 0
	if !addr.Expires.Expired() {
		// run add operation
		_ = a.list.Process(func(pid int) error {
			id := peer.String()
			list, ok := a.list.Get(id, pid)
			if !ok {
				list = make([]*Address, 0)
				mode = 1
			} else {
				for _, a := range list {
					if a.Equals(addr) {
						return nil
					}
				}
				mode = 2
			}
			list = append(list, addr)
			a.list.Put(id, list, pid)
			return nil
		}, false)
	}
	return
}

// Get address for peer
func (a *PeerAddrList) Get(peer *PeerID, transport string) (res []*Address) {
	id := peer.String()
	list, ok := a.list.Get(id, 0)
	if ok {
		for _, addr := range list {
			// check for expired address.
			if addr.Expires.Expired() {
				// skip expired
				continue
			}
			// check for matching protocol
			if len(transport) > 0 && transport != addr.Netw {
				// skip other transports
				continue
			}
			res = append(res, addr)
		}
	}
	return
}

// Delete a list entry by key.
func (a *PeerAddrList) Delete(peer *PeerID) {
	a.list.Delete(peer.String(), 0)
}

// Contains checks if a peer is contained in the list. Does not check
// for expired entries.
func (a *PeerAddrList) Contains(peer *PeerID) (ok bool) {
	_, ok = a.list.Get(peer.String(), 0)
	return
}