1 /* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "director.h"
6 #include "director-host.h"
7 
director_host_cmp(const struct director_host * b1,const struct director_host * b2)8 static int director_host_cmp(const struct director_host *b1,
9 			     const struct director_host *b2)
10 {
11 	int ret;
12 
13 	ret = net_ip_cmp(&b1->ip, &b2->ip);
14 	if (ret != 0)
15 		return ret;
16 	return (int)b1->port - (int)b2->port;
17 }
18 
director_host_cmp_p(struct director_host * const * host1,struct director_host * const * host2)19 int director_host_cmp_p(struct director_host *const *host1,
20 			struct director_host *const *host2)
21 {
22 	return director_host_cmp(*host1, *host2);
23 }
24 
25 struct director_host *
director_host_add(struct director * dir,const struct ip_addr * ip,in_port_t port)26 director_host_add(struct director *dir,
27 		  const struct ip_addr *ip, in_port_t port)
28 {
29 	struct director_host *host;
30 
31 	i_assert(director_host_lookup(dir, ip, port) == NULL);
32 
33 	host = i_new(struct director_host, 1);
34 	host->dir = dir;
35 	host->refcount = 1;
36 	host->ip = *ip;
37 	host->ip_str = i_strdup(net_ip2addr(&host->ip));
38 	host->port = port;
39 	host->name = i_strdup_printf("%s:%u", host->ip_str, port);
40 
41 	array_push_back(&dir->dir_hosts, &host);
42 
43 	/* there are few enough directors that sorting after each
44 	   addition should be fine */
45 	array_sort(&dir->dir_hosts, director_host_cmp_p);
46 	return host;
47 }
48 
director_host_free(struct director_host ** _host)49 void director_host_free(struct director_host **_host)
50 {
51 	struct director_host *host = *_host;
52 
53 	i_assert(host->refcount == 1);
54 
55 	*_host = NULL;
56 	director_host_unref(host);
57 }
58 
director_host_ref(struct director_host * host)59 void director_host_ref(struct director_host *host)
60 {
61 	i_assert(host->refcount > 0);
62 	host->refcount++;
63 }
64 
director_host_unref(struct director_host * host)65 void director_host_unref(struct director_host *host)
66 {
67 	struct director_host *const *hosts;
68 	unsigned int i, count;
69 
70 	i_assert(host->refcount > 0);
71 
72 	if (--host->refcount > 0)
73 		return;
74 
75 	hosts = array_get(&host->dir->dir_hosts, &count);
76 	for (i = 0; i < count; i++) {
77 		if (hosts[i] == host) {
78 			array_delete(&host->dir->dir_hosts, i, 1);
79 			break;
80 		}
81 	}
82 	i_free(host->name);
83 	i_free(host->ip_str);
84 	i_free(host);
85 }
86 
director_host_restarted(struct director_host * host)87 void director_host_restarted(struct director_host *host)
88 {
89 	host->last_seq = 0;
90 	host->last_sync_seq = 0;
91 	host->last_sync_seq_counter = 0;
92 	host->last_sync_timestamp = 0;
93 }
94 
95 struct director_host *
director_host_get(struct director * dir,const struct ip_addr * ip,in_port_t port)96 director_host_get(struct director *dir, const struct ip_addr *ip,
97 		  in_port_t port)
98 {
99 	struct director_host *host;
100 
101 	host = director_host_lookup(dir, ip, port);
102 	if (host == NULL)
103 		host = director_host_add(dir, ip, port);
104 	return host;
105 }
106 
107 struct director_host *
director_host_lookup(struct director * dir,const struct ip_addr * ip,in_port_t port)108 director_host_lookup(struct director *dir, const struct ip_addr *ip,
109 		     in_port_t port)
110 {
111 	struct director_host *host;
112 
113 	array_foreach_elem(&dir->dir_hosts, host) {
114 		if (net_ip_compare(&host->ip, ip) && host->port == port)
115 			return host;
116 	}
117 	return NULL;
118 }
119 
120 struct director_host *
director_host_lookup_ip(struct director * dir,const struct ip_addr * ip)121 director_host_lookup_ip(struct director *dir, const struct ip_addr *ip)
122 {
123 	struct director_host *host;
124 
125 	array_foreach_elem(&dir->dir_hosts, host) {
126 		if (net_ip_compare(&host->ip, ip))
127 			return host;
128 	}
129 	return NULL;
130 }
131 
director_host_cmp_to_self(const struct director_host * b1,const struct director_host * b2,const struct director_host * self)132 int director_host_cmp_to_self(const struct director_host *b1,
133 			      const struct director_host *b2,
134 			      const struct director_host *self)
135 {
136 	int ret;
137 
138 	if ((ret = director_host_cmp(b1, b2)) >= 0)
139 		return ret == 0 ? 0 : -director_host_cmp_to_self(b2, b1, self);
140 
141 	/* order -> return:
142 	   self, b1, b2 -> b2
143 	   b1, self, b2 -> b1
144 	   b1, b2, self -> b2
145 	*/
146 	if (director_host_cmp(self, b1) < 0)
147 		return 1; /* self, b1, b2 */
148 	if (director_host_cmp(self, b2) < 0)
149 		return -1; /* b1, self, b2 */
150 	return 1; /* b1, b2, self */
151 }
152 
director_host_add_string(struct director * dir,const char * host)153 static void director_host_add_string(struct director *dir, const char *host)
154 {
155 	struct ip_addr *ips;
156 	in_port_t port;
157 	unsigned int i, ips_count;
158 
159 	if (net_str2hostport(host, dir->self_port, &host, &port) < 0)
160 		i_fatal("Invalid director host:port in '%s'", host);
161 
162 	if (net_gethostbyname(host, &ips, &ips_count) < 0)
163 		i_fatal("Unknown director host: %s", host);
164 
165 	for (i = 0; i < ips_count; i++) {
166 		if (director_host_lookup(dir, &ips[i], port) == NULL)
167 			(void)director_host_add(dir, &ips[i], port);
168 	}
169 }
170 
director_host_add_from_string(struct director * dir,const char * hosts)171 void director_host_add_from_string(struct director *dir, const char *hosts)
172 {
173 	T_BEGIN {
174 		const char *const *tmp;
175 
176 		tmp = t_strsplit_spaces(hosts, " ");
177 		for (; *tmp != NULL; tmp++)
178 			director_host_add_string(dir, *tmp);
179 	} T_END;
180 
181 	if (array_count(&dir->dir_hosts) == 0) {
182 		/* standalone director */
183 		struct ip_addr ip;
184 
185 		if (net_addr2ip("127.0.0.1", &ip) < 0)
186 			i_unreached();
187 		dir->self_host = director_host_add(dir, &ip, 0);
188 		dir->self_host->self = TRUE;
189 	}
190 }
191