segfault/memleak on incorrect data fixed
[elliptics.git] / library / discovery.c
blob61ac78f9373a7f06fe9c35cb2347cd5d9fd506ae
1 /*
2 * 2012+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
3 * All rights reserved.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <sys/mman.h>
20 #include <dirent.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
28 #include "elliptics.h"
29 #include "elliptics/interface.h"
31 static int dnet_discover_loop = 1;
32 static int dnet_discover_ttl = 3;
34 static int dnet_discovery_add_v4(struct dnet_node *n, struct dnet_addr *addr, int s)
36 int err;
37 struct ip_mreq command;
39 err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &dnet_discover_loop, sizeof(dnet_discover_loop));
40 if (err < 0) {
41 err = -errno;
42 dnet_log_err(n, "unable to set loopback option");
43 goto err_out_exit;
46 err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &dnet_discover_ttl, sizeof(dnet_discover_ttl));
47 if (err < 0) {
48 err = -errno;
49 dnet_log_err(n, "unable to set %d hop limit", dnet_discover_ttl);
50 goto err_out_exit;
53 command.imr_multiaddr = ((struct sockaddr_in *)addr->addr)->sin_addr;
54 command.imr_interface = ((struct sockaddr_in *)n->addr.addr)->sin_addr;
56 err = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &command, sizeof(command));
57 if (err < 0) {
58 err = -errno;
59 dnet_log_err(n, "can not add multicast membership: %s", inet_ntoa(command.imr_multiaddr));
60 goto err_out_exit;
63 err_out_exit:
64 return err;
67 static int dnet_discovery_add_v6(struct dnet_node *n, struct dnet_addr *addr __unused, int s)
69 int err;
71 err = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &dnet_discover_loop, sizeof(dnet_discover_loop));
72 if (err < 0) {
73 err = -errno;
74 dnet_log_err(n, "unable to set loopback option");
75 goto err_out_exit;
78 err = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &dnet_discover_ttl, sizeof(dnet_discover_ttl));
79 if (err < 0) {
80 err = -errno;
81 dnet_log_err(n, "unable to set %d hop limit", dnet_discover_ttl);
82 goto err_out_exit;
85 err_out_exit:
86 return err;
89 int dnet_discovery_add(struct dnet_node *n, struct dnet_config *cfg)
91 struct dnet_addr addr;
92 int err = -EEXIST;
93 int s;
95 if (n->autodiscovery_socket != -1)
96 goto err_out_exit;
98 memset(&addr, 0, sizeof(struct dnet_addr));
100 cfg->sock_type = SOCK_DGRAM;
101 cfg->proto = IPPROTO_IP;
102 if (cfg->family == AF_INET6)
103 cfg->proto = IPPROTO_IPV6;
104 addr.addr_len = sizeof(addr.addr);
106 err = dnet_fill_addr(&addr, cfg->addr, cfg->port, cfg->family, cfg->sock_type, cfg->proto);
107 if (err) {
108 dnet_log(n, DNET_LOG_ERROR, "Failed to get address info for %s:%s, family: %d, err: %d: %s.\n",
109 cfg->addr, cfg->port, cfg->family, err, strerror(-err));
110 goto err_out_exit;
113 s = socket(cfg->family, cfg->sock_type, 0);
114 if (s < 0) {
115 err = -errno;
116 dnet_log_err(n, "failed to create multicast socket");
117 goto err_out_exit;
120 if (cfg->family == AF_INET6)
121 err = dnet_discovery_add_v6(n, &addr, s);
122 else
123 err = dnet_discovery_add_v4(n, &addr, s);
125 if (err)
126 goto err_out_close;
128 n->autodiscovery_socket = s;
129 n->autodiscovery_addr = addr;
130 return 0;
132 err_out_close:
133 close(s);
134 err_out_exit:
135 return err;
138 static int dnet_discovery_send(struct dnet_node *n)
140 char buf[sizeof(struct dnet_cmd) + sizeof(struct dnet_auth) + sizeof(struct dnet_addr_attr)];
141 struct dnet_cmd *cmd;
142 struct dnet_addr_attr *addr;
143 struct dnet_auth *auth;
144 int err;
146 memset(buf, 0, sizeof(buf));
148 cmd = (struct dnet_cmd *)buf;
149 addr = (struct dnet_addr_attr *)(cmd + 1);
150 auth = (struct dnet_auth *)(addr + 1);
152 cmd->id = n->id;
153 cmd->size = sizeof(struct dnet_addr_attr) + sizeof(struct dnet_auth);
154 dnet_convert_cmd(cmd);
156 addr->sock_type = n->sock_type;
157 addr->proto = n->proto;
158 addr->family = n->family;
159 addr->addr = n->addr;
160 dnet_convert_addr_attr(addr);
162 memcpy(auth->cookie, n->cookie, DNET_AUTH_COOKIE_SIZE);
163 dnet_convert_auth(auth);
165 err = sendto(n->autodiscovery_socket, buf, sizeof(buf), 0, (void *)&n->autodiscovery_addr, n->autodiscovery_addr.addr_len);
166 if (err < 0) {
167 err = -errno;
168 dnet_log_err(n, "autodiscovery sent: %s - %.*s", dnet_server_convert_dnet_addr(&addr->addr),
169 (int)sizeof(auth->cookie), auth->cookie);
170 } else {
171 dnet_log(n, DNET_LOG_NOTICE, "autodiscovery sent: %s - %.*s\n", dnet_server_convert_dnet_addr(&addr->addr),
172 (int)sizeof(auth->cookie), auth->cookie);
175 return err;
178 static int dnet_discovery_add_state(struct dnet_node *n, struct dnet_addr_attr *addr)
180 struct dnet_config cfg;
182 memset(&cfg, 0, sizeof(struct dnet_config));
184 dnet_server_convert_addr_raw((struct sockaddr *)&addr->addr, addr->addr.addr_len, cfg.addr, sizeof(cfg.addr));
185 snprintf(cfg.port, sizeof(cfg.port), "%d", dnet_server_convert_port((struct sockaddr *)&addr->addr, addr->addr.addr_len));
186 cfg.family = addr->family;
187 cfg.sock_type = addr->sock_type;
188 cfg.proto = addr->proto;
190 return dnet_add_state(n, &cfg);
193 static int dnet_discovery_recv(struct dnet_node *n)
195 char buf[sizeof(struct dnet_cmd) + sizeof(struct dnet_auth) + sizeof(struct dnet_addr_attr)];
196 struct dnet_cmd *cmd;
197 struct dnet_addr_attr *addr;
198 struct dnet_auth *auth;
199 int err;
200 struct dnet_addr remote;
201 socklen_t len = n->autodiscovery_addr.addr_len;
203 remote = n->autodiscovery_addr;
205 cmd = (struct dnet_cmd *)buf;
206 addr = (struct dnet_addr_attr *)(cmd + 1);
207 auth = (struct dnet_auth *)(addr + 1);
209 while (1) {
210 err = recvfrom(n->autodiscovery_socket, buf, sizeof(buf), MSG_DONTWAIT, (void *)&remote, &len);
211 if (err != sizeof(buf))
212 return -EAGAIN;
214 dnet_convert_cmd(cmd);
215 dnet_convert_addr_attr(addr);
216 dnet_convert_auth(auth);
218 dnet_log(n, DNET_LOG_NOTICE, "autodiscovery recv: %s - %.*s\n", dnet_server_convert_dnet_addr(&addr->addr),
219 (int)sizeof(auth->cookie), auth->cookie);
221 if (!memcmp(n->cookie, auth->cookie, DNET_AUTH_COOKIE_SIZE)) {
222 dnet_discovery_add_state(n, addr);
226 return 0;
229 int dnet_discovery(struct dnet_node *n)
231 int err;
233 if (n->autodiscovery_socket == -1)
234 return -ENOTSUP;
236 err = dnet_discovery_recv(n);
238 if (n->flags & DNET_CFG_JOIN_NETWORK)
239 err = dnet_discovery_send(n);
241 return err;