1/*
2 Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
3
4 This file is part of libzmq, the ZeroMQ core engine in C++.
5
6 libzmq is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License (LGPL) as published
8 by the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 As a special exception, the Contributors give you permission to link
12 this library with independent modules to produce an executable,
13 regardless of the license terms of these independent modules, and to
14 copy and distribute the resulting executable under terms of your choice,
15 provided that you also meet, for each linked independent module, the
16 terms and conditions of the license of that module. An independent
17 module is a module which is not derived from or based on this library.
18 If you modify this library, you must extend this exception to your
19 version of the library.
20
21 libzmq is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24 License for more details.
25
26 You should have received a copy of the GNU Lesser General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
28*/
29
30#include "precompiled.hpp"
31#include <string>
32
33#include "macros.hpp"
34#include "tcp_address.hpp"
35#include "stdint.hpp"
36#include "err.hpp"
37#include "ip.hpp"
38
39#ifndef ZMQ_HAVE_WINDOWS
40#include <sys/types.h>
41#include <arpa/inet.h>
42#include <netinet/tcp.h>
43#include <net/if.h>
44#include <netdb.h>
45#include <ctype.h>
46#include <unistd.h>
47#include <stdlib.h>
48#endif
49
50#include <limits.h>
51
52zmq::tcp_address_t::tcp_address_t () : _has_src_addr (false)
53{
54 memset (&_address, 0, sizeof (_address));
55 memset (&_source_address, 0, sizeof (_source_address));
56}
57
58zmq::tcp_address_t::tcp_address_t (const sockaddr *sa_, socklen_t sa_len_) :
59 _has_src_addr (false)
60{
61 zmq_assert (sa_ && sa_len_ > 0);
62
63 memset (&_address, 0, sizeof (_address));
64 memset (&_source_address, 0, sizeof (_source_address));
65 if (sa_->sa_family == AF_INET
66 && sa_len_ >= static_cast<socklen_t> (sizeof (_address.ipv4)))
67 memcpy (&_address.ipv4, sa_, sizeof (_address.ipv4));
68 else if (sa_->sa_family == AF_INET6
69 && sa_len_ >= static_cast<socklen_t> (sizeof (_address.ipv6)))
70 memcpy (&_address.ipv6, sa_, sizeof (_address.ipv6));
71}
72
73int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv6_)
74{
75 // Test the ';' to know if we have a source address in name_
76 const char *src_delimiter = strrchr (name_, ';');
77 if (src_delimiter) {
78 std::string src_name (name_, src_delimiter - name_);
79
80 ip_resolver_options_t src_resolver_opts;
81
82 src_resolver_opts
83 .bindable (true)
84 // Restrict hostname/service to literals to avoid any DNS
85 // lookups or service-name irregularity due to
86 // indeterminate socktype.
87 .allow_dns (false)
88 .allow_nic_name (true)
89 .ipv6 (ipv6_)
90 .expect_port (true);
91
92 ip_resolver_t src_resolver (src_resolver_opts);
93
94 const int rc =
95 src_resolver.resolve (&_source_address, src_name.c_str ());
96 if (rc != 0)
97 return -1;
98 name_ = src_delimiter + 1;
99 _has_src_addr = true;
100 }
101
102 ip_resolver_options_t resolver_opts;
103
104 resolver_opts.bindable (local_)
105 .allow_dns (!local_)
106 .allow_nic_name (local_)
107 .ipv6 (ipv6_)
108 .expect_port (true);
109
110 ip_resolver_t resolver (resolver_opts);
111
112 return resolver.resolve (&_address, name_);
113}
114
115template <size_t N1, size_t N2>
116static std::string make_address_string (const char *hbuf_,
117 uint16_t port_,
118 const char (&ipv6_prefix_)[N1],
119 const char (&ipv6_suffix_)[N2])
120{
121 const size_t max_port_str_length = 5;
122 char buf[NI_MAXHOST + sizeof ipv6_prefix_ + sizeof ipv6_suffix_
123 + max_port_str_length];
124 char *pos = buf;
125 memcpy (pos, ipv6_prefix_, sizeof ipv6_prefix_ - 1);
126 pos += sizeof ipv6_prefix_ - 1;
127 const size_t hbuf_len = strlen (hbuf_);
128 memcpy (pos, hbuf_, hbuf_len);
129 pos += hbuf_len;
130 memcpy (pos, ipv6_suffix_, sizeof ipv6_suffix_ - 1);
131 pos += sizeof ipv6_suffix_ - 1;
132 pos += sprintf (pos, "%d", ntohs (port_));
133 return std::string (buf, pos - buf);
134}
135
136int zmq::tcp_address_t::to_string (std::string &addr_) const
137{
138 if (_address.family () != AF_INET && _address.family () != AF_INET6) {
139 addr_.clear ();
140 return -1;
141 }
142
143 // Not using service resolving because of
144 // https://github.com/zeromq/libzmq/commit/1824574f9b5a8ce786853320e3ea09fe1f822bc4
145 char hbuf[NI_MAXHOST];
146 const int rc = getnameinfo (addr (), addrlen (), hbuf, sizeof (hbuf), NULL,
147 0, NI_NUMERICHOST);
148 if (rc != 0) {
149 addr_.clear ();
150 return rc;
151 }
152
153 const char ipv4_prefix[] = "tcp://";
154 const char ipv4_suffix[] = ":";
155 const char ipv6_prefix[] = "tcp://[";
156 const char ipv6_suffix[] = "]:";
157 if (_address.family () == AF_INET6) {
158 addr_ = make_address_string (hbuf, _address.ipv6.sin6_port, ipv6_prefix,
159 ipv6_suffix);
160 } else {
161 addr_ = make_address_string (hbuf, _address.ipv4.sin_port, ipv4_prefix,
162 ipv4_suffix);
163 }
164 return 0;
165}
166
167const sockaddr *zmq::tcp_address_t::addr () const
168{
169 return _address.as_sockaddr ();
170}
171
172socklen_t zmq::tcp_address_t::addrlen () const
173{
174 return _address.sockaddr_len ();
175}
176
177const sockaddr *zmq::tcp_address_t::src_addr () const
178{
179 return _source_address.as_sockaddr ();
180}
181
182socklen_t zmq::tcp_address_t::src_addrlen () const
183{
184 return _source_address.sockaddr_len ();
185}
186
187bool zmq::tcp_address_t::has_src_addr () const
188{
189 return _has_src_addr;
190}
191
192#if defined ZMQ_HAVE_WINDOWS
193unsigned short zmq::tcp_address_t::family () const
194#else
195sa_family_t zmq::tcp_address_t::family () const
196#endif
197{
198 return _address.family ();
199}
200
201zmq::tcp_address_mask_t::tcp_address_mask_t () : _address_mask (-1)
202{
203 memset (&_network_address, 0, sizeof (_network_address));
204}
205
206int zmq::tcp_address_mask_t::mask () const
207{
208 return _address_mask;
209}
210
211int zmq::tcp_address_mask_t::resolve (const char *name_, bool ipv6_)
212{
213 // Find '/' at the end that separates address from the cidr mask number.
214 // Allow empty mask clause and treat it like '/32' for ipv4 or '/128' for ipv6.
215 std::string addr_str, mask_str;
216 const char *delimiter = strrchr (name_, '/');
217 if (delimiter != NULL) {
218 addr_str.assign (name_, delimiter - name_);
219 mask_str.assign (delimiter + 1);
220 if (mask_str.empty ()) {
221 errno = EINVAL;
222 return -1;
223 }
224 } else
225 addr_str.assign (name_);
226
227 // Parse address part using standard routines.
228 ip_resolver_options_t resolver_opts;
229
230 resolver_opts.bindable (false)
231 .allow_dns (false)
232 .allow_nic_name (false)
233 .ipv6 (ipv6_)
234 .expect_port (false);
235
236 ip_resolver_t resolver (resolver_opts);
237
238 const int rc = resolver.resolve (&_network_address, addr_str.c_str ());
239 if (rc != 0)
240 return rc;
241
242 // Parse the cidr mask number.
243 const int full_mask_ipv4 =
244 sizeof (_network_address.ipv4.sin_addr) * CHAR_BIT;
245 const int full_mask_ipv6 =
246 sizeof (_network_address.ipv6.sin6_addr) * CHAR_BIT;
247 if (mask_str.empty ()) {
248 _address_mask = _network_address.family () == AF_INET6 ? full_mask_ipv6
249 : full_mask_ipv4;
250 } else if (mask_str == "0")
251 _address_mask = 0;
252 else {
253 const long mask = strtol (mask_str.c_str (), NULL, 10);
254 if ((mask < 1)
255 || (_network_address.family () == AF_INET6 && mask > full_mask_ipv6)
256 || (_network_address.family () != AF_INET6
257 && mask > full_mask_ipv4)) {
258 errno = EINVAL;
259 return -1;
260 }
261 _address_mask = static_cast<int> (mask);
262 }
263
264 return 0;
265}
266
267int zmq::tcp_address_mask_t::to_string (std::string &addr_) const
268{
269 if (_network_address.family () != AF_INET
270 && _network_address.family () != AF_INET6) {
271 addr_.clear ();
272 return -1;
273 }
274 if (_address_mask == -1) {
275 addr_.clear ();
276 return -1;
277 }
278
279 char hbuf[NI_MAXHOST];
280 const int rc = getnameinfo (_network_address.as_sockaddr (),
281 _network_address.sockaddr_len (), hbuf,
282 sizeof (hbuf), NULL, 0, NI_NUMERICHOST);
283 if (rc != 0) {
284 addr_.clear ();
285 return rc;
286 }
287
288 const size_t max_mask_len = 4;
289 const char ipv6_prefix[] = "[";
290 const char ipv6_suffix[] = "]/";
291 const char ipv4_suffix[] = "/";
292 char
293 buf[NI_MAXHOST + sizeof ipv6_prefix + sizeof ipv6_suffix + max_mask_len];
294 char *pos = buf;
295 if (_network_address.family () == AF_INET6) {
296 memcpy (pos, ipv6_prefix, sizeof ipv6_prefix - 1);
297 pos += sizeof ipv6_prefix - 1;
298 }
299 const size_t hbuf_len = strlen (hbuf);
300 memcpy (pos, hbuf, hbuf_len);
301 pos += hbuf_len;
302 if (_network_address.family () == AF_INET6) {
303 memcpy (pos, ipv6_suffix, sizeof ipv6_suffix - 1);
304 pos += sizeof ipv6_suffix - 1;
305 } else {
306 memcpy (pos, ipv4_suffix, sizeof ipv4_suffix - 1);
307 pos += sizeof ipv4_suffix - 1;
308 }
309 pos += sprintf (pos, "%d", _address_mask);
310 addr_.assign (buf, pos - buf);
311 return 0;
312}
313
314bool zmq::tcp_address_mask_t::match_address (const struct sockaddr *ss_,
315 const socklen_t ss_len_) const
316{
317 zmq_assert (_address_mask != -1 && ss_ != NULL
318 && ss_len_
319 >= static_cast<socklen_t> (sizeof (struct sockaddr)));
320
321 if (ss_->sa_family != _network_address.generic.sa_family)
322 return false;
323
324 if (_address_mask > 0) {
325 int mask;
326 const uint8_t *our_bytes, *their_bytes;
327 if (ss_->sa_family == AF_INET6) {
328 zmq_assert (ss_len_ == sizeof (struct sockaddr_in6));
329 their_bytes = reinterpret_cast<const uint8_t *> (
330 &((reinterpret_cast<const struct sockaddr_in6 *> (ss_))
331 ->sin6_addr));
332 our_bytes = reinterpret_cast<const uint8_t *> (
333 &_network_address.ipv6.sin6_addr);
334 mask = sizeof (struct in6_addr) * 8;
335 } else {
336 zmq_assert (ss_len_ == sizeof (struct sockaddr_in));
337 their_bytes = reinterpret_cast<const uint8_t *> (&(
338 (reinterpret_cast<const struct sockaddr_in *> (ss_))->sin_addr));
339 our_bytes = reinterpret_cast<const uint8_t *> (
340 &_network_address.ipv4.sin_addr);
341 mask = sizeof (struct in_addr) * 8;
342 }
343 if (_address_mask < mask)
344 mask = _address_mask;
345
346 const size_t full_bytes = mask / 8;
347 if (memcmp (our_bytes, their_bytes, full_bytes) != 0)
348 return false;
349
350 const uint8_t last_byte_bits = 0xffU << (8 - mask % 8);
351 if (last_byte_bits) {
352 if ((their_bytes[full_bytes] & last_byte_bits)
353 != (our_bytes[full_bytes] & last_byte_bits))
354 return false;
355 }
356 }
357
358 return true;
359}
360