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#include <sstream>
33
34#include "macros.hpp"
35#include "udp_address.hpp"
36#include "stdint.hpp"
37#include "err.hpp"
38#include "ip.hpp"
39
40#ifndef ZMQ_HAVE_WINDOWS
41#include <sys/types.h>
42#include <arpa/inet.h>
43#include <netdb.h>
44#include <net/if.h>
45#include <ctype.h>
46#endif
47
48zmq::udp_address_t::udp_address_t () :
49 _bind_interface (-1),
50 _is_multicast (false)
51{
52 _bind_address = ip_addr_t::any (AF_INET);
53 _target_address = ip_addr_t::any (AF_INET);
54}
55
56zmq::udp_address_t::~udp_address_t ()
57{
58}
59
60int zmq::udp_address_t::resolve (const char *name_, bool bind_, bool ipv6_)
61{
62 // No IPv6 support yet
63 bool has_interface = false;
64
65 _address = name_;
66
67 // If we have a semicolon then we should have an interface specifier in the
68 // URL
69 const char *src_delimiter = strrchr (name_, ';');
70 if (src_delimiter) {
71 std::string src_name (name_, src_delimiter - name_);
72
73 ip_resolver_options_t src_resolver_opts;
74
75 src_resolver_opts
76 .bindable (true)
77 // Restrict hostname/service to literals to avoid any DNS
78 // lookups or service-name irregularity due to
79 // indeterminate socktype.
80 .allow_dns (false)
81 .allow_nic_name (true)
82 .ipv6 (ipv6_)
83 .expect_port (false);
84
85 ip_resolver_t src_resolver (src_resolver_opts);
86
87 const int rc = src_resolver.resolve (&_bind_address, src_name.c_str ());
88
89 if (rc != 0) {
90 return -1;
91 }
92
93 if (_bind_address.is_multicast ()) {
94 // It doesn't make sense to have a multicast address as a source
95 errno = EINVAL;
96 return -1;
97 }
98
99 // This is a hack because we need the interface index when binding
100 // multicast IPv6, we can't do it by address. Unfortunately for the
101 // time being we don't have a generic platform-independent function to
102 // resolve an interface index from an address, so we only support it
103 // when an actual interface name is provided.
104 if (src_name == "*") {
105 _bind_interface = 0;
106 } else {
107#if _WIN32_WINNT > _WIN32_WINNT_WINXP && !defined ZMQ_HAVE_WINDOWS_UWP \
108 && !defined ZMQ_HAVE_VXWORKS
109 _bind_interface = if_nametoindex (src_name.c_str ());
110 if (_bind_interface == 0) {
111 // Error, probably not an interface name.
112 _bind_interface = -1;
113 }
114#endif
115 }
116
117 has_interface = true;
118 name_ = src_delimiter + 1;
119 }
120
121 ip_resolver_options_t resolver_opts;
122
123 resolver_opts.bindable (bind_)
124 .allow_dns (!bind_)
125 .allow_nic_name (bind_)
126 .expect_port (true)
127 .ipv6 (ipv6_);
128
129 ip_resolver_t resolver (resolver_opts);
130
131 int rc = resolver.resolve (&_target_address, name_);
132 if (rc != 0) {
133 return -1;
134 }
135
136 _is_multicast = _target_address.is_multicast ();
137 uint16_t port = _target_address.port ();
138
139 if (has_interface) {
140 // If we have an interface specifier then the target address must be a
141 // multicast address
142 if (!_is_multicast) {
143 errno = EINVAL;
144 return -1;
145 }
146
147 _bind_address.set_port (port);
148 } else {
149 // If we don't have an explicit interface specifier then the URL is
150 // ambiguous: if the target address is multicast then it's the
151 // destination address and the bind address is ANY, if it's unicast
152 // then it's the bind address when 'bind_' is true and the destination
153 // otherwise
154 if (_is_multicast || !bind_) {
155 _bind_address = ip_addr_t::any (_target_address.family ());
156 _bind_address.set_port (port);
157 _bind_interface = 0;
158 } else {
159 // If we were asked for a bind socket and the address
160 // provided was not multicast then it was really meant as
161 // a bind address and the target_address is useless.
162 _bind_address = _target_address;
163 }
164 }
165
166 if (_bind_address.family () != _target_address.family ()) {
167 errno = EINVAL;
168 return -1;
169 }
170
171 // For IPv6 multicast we *must* have an interface index since we can't
172 // bind by address.
173 if (ipv6_ && _is_multicast && _bind_interface < 0) {
174 errno = ENODEV;
175 return -1;
176 }
177
178 return 0;
179}
180
181int zmq::udp_address_t::family () const
182{
183 return _bind_address.family ();
184}
185
186bool zmq::udp_address_t::is_mcast () const
187{
188 return _is_multicast;
189}
190
191const zmq::ip_addr_t *zmq::udp_address_t::bind_addr () const
192{
193 return &_bind_address;
194}
195
196int zmq::udp_address_t::bind_if () const
197{
198 return _bind_interface;
199}
200
201const zmq::ip_addr_t *zmq::udp_address_t::target_addr () const
202{
203 return &_target_address;
204}
205
206int zmq::udp_address_t::to_string (std::string &addr_)
207{
208 // XXX what do (factor TCP code?)
209 addr_ = _address;
210 return 0;
211}
212