1 // Copyright (c) 2015-2018 Josh Blum
2 // SPDX-License-Identifier: BSL-1.0
3
4 #include "SoapySocketDefs.hpp"
5 #include "SoapyURLUtils.hpp"
6 #include <cstring> //memset
7 #include <string>
8 #include <cassert>
9
SockAddrData(void)10 SockAddrData::SockAddrData(void)
11 {
12 return;
13 }
14
SockAddrData(const struct sockaddr * addr,const int addrlen)15 SockAddrData::SockAddrData(const struct sockaddr *addr, const int addrlen)
16 {
17 _storage.resize(addrlen);
18 std::memcpy(_storage.data(), addr, addrlen);
19 }
20
addr(void) const21 const struct sockaddr *SockAddrData::addr(void) const
22 {
23 return (const struct sockaddr *)_storage.data();
24 }
25
addrlen(void) const26 size_t SockAddrData::addrlen(void) const
27 {
28 return _storage.size();
29 }
30
SoapyURL(void)31 SoapyURL::SoapyURL(void)
32 {
33 return;
34 }
35
SoapyURL(const std::string & scheme,const std::string & node,const std::string & service)36 SoapyURL::SoapyURL(const std::string &scheme, const std::string &node, const std::string &service):
37 _scheme(scheme),
38 _node(node),
39 _service(service)
40 {
41 return;
42 }
43
SoapyURL(const std::string & url)44 SoapyURL::SoapyURL(const std::string &url)
45 {
46 //extract the scheme
47 std::string urlRest = url;
48 auto schemeEnd = url.find("://");
49 if (schemeEnd != std::string::npos)
50 {
51 _scheme = url.substr(0, schemeEnd);
52 urlRest = url.substr(schemeEnd+3);
53 }
54
55 //extract node name and service port
56 bool inBracket = false;
57 bool inService = false;
58 for (size_t i = 0; i < urlRest.size(); i++)
59 {
60 const char ch = urlRest[i];
61 if (inBracket and ch == ']')
62 {
63 inBracket = false;
64 continue;
65 }
66 if (not inBracket and ch == '[')
67 {
68 inBracket = true;
69 continue;
70 }
71 if (inBracket)
72 {
73 _node += ch;
74 continue;
75 }
76 if (inService)
77 {
78 _service += ch;
79 continue;
80 }
81 if (not inService and ch == ':')
82 {
83 inService = true;
84 continue;
85 }
86 if (not inService)
87 {
88 _node += ch;
89 continue;
90 }
91 }
92 }
93
SoapyURL(const struct sockaddr * addr)94 SoapyURL::SoapyURL(const struct sockaddr *addr)
95 {
96 char *s = NULL;
97 switch(addr->sa_family)
98 {
99 case AF_INET: {
100 auto *addr_in = (const struct sockaddr_in *)addr;
101 s = (char *)malloc(INET_ADDRSTRLEN);
102 inet_ntop(AF_INET, (void *)&(addr_in->sin_addr), s, INET_ADDRSTRLEN);
103 _node = s;
104 _service = std::to_string(ntohs(addr_in->sin_port));
105 break;
106 }
107 case AF_INET6: {
108 auto *addr_in6 = (const struct sockaddr_in6 *)addr;
109 s = (char *)malloc(INET6_ADDRSTRLEN);
110 inet_ntop(AF_INET6, (void *)&(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN);
111 _node = s;
112 //support scoped address node
113 if (addr_in6->sin6_scope_id != 0)
114 {
115 _node += "%" + std::to_string(addr_in6->sin6_scope_id);
116 }
117 _service = std::to_string(ntohs(addr_in6->sin6_port));
118 break;
119 }
120 default:
121 break;
122 }
123 free(s);
124 }
125
SoapyURL(const SockAddrData & addr)126 SoapyURL::SoapyURL(const SockAddrData &addr)
127 {
128 *this = SoapyURL(addr.addr());
129 }
130
toSockAddr(SockAddrData & addr) const131 std::string SoapyURL::toSockAddr(SockAddrData &addr) const
132 {
133 SockAddrData result;
134
135 //unspecified service, cant continue
136 if (_service.empty()) return "service not specified";
137
138 //configure the hint
139 struct addrinfo hints, *servinfo = NULL;
140 std::memset(&hints, 0, sizeof(hints));
141 hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
142 hints.ai_socktype = this->getType();
143
144 //get address info
145 int ret = getaddrinfo(_node.c_str(), _service.c_str(), &hints, &servinfo);
146 if (ret != 0) return gai_strerror(ret);
147
148 //iterate through possible matches
149 struct addrinfo *p = NULL;
150 for (p = servinfo; p != NULL; p = p->ai_next)
151 {
152 //eliminate unsupported family types
153 if (p->ai_family != AF_INET and p->ai_family != AF_INET6) continue;
154
155 //found a match
156 assert(p->ai_family == p->ai_addr->sa_family);
157 addr = SockAddrData(p->ai_addr, p->ai_addrlen);
158 break;
159 }
160
161 //cleanup
162 freeaddrinfo(servinfo);
163
164 //no results
165 if (p == NULL) return "no lookup results";
166
167 return ""; //OK
168 }
169
toString(void) const170 std::string SoapyURL::toString(void) const
171 {
172 std::string url;
173
174 //add the scheme
175 if (not _scheme.empty()) url += _scheme + "://";
176
177 //add the node with ipv6 escape brackets
178 if (_node.find(":") != std::string::npos) url += "[" + _node + "]";
179 else url += _node;
180
181 //and the service
182 if (not _service.empty()) url += ":" + _service;
183
184 return url;
185 }
186
getScheme(void) const187 std::string SoapyURL::getScheme(void) const
188 {
189 return _scheme;
190 }
191
getNode(void) const192 std::string SoapyURL::getNode(void) const
193 {
194 return _node;
195 }
196
getService(void) const197 std::string SoapyURL::getService(void) const
198 {
199 return _service;
200 }
201
setScheme(const std::string & scheme)202 void SoapyURL::setScheme(const std::string &scheme)
203 {
204 _scheme = scheme;
205 }
206
setNode(const std::string & node)207 void SoapyURL::setNode(const std::string &node)
208 {
209 _node = node;
210 }
211
setService(const std::string & service)212 void SoapyURL::setService(const std::string &service)
213 {
214 _service = service;
215 }
216
getType(void) const217 int SoapyURL::getType(void) const
218 {
219 if (_scheme == "tcp") return SOCK_STREAM;
220 if (_scheme == "udp") return SOCK_DGRAM;
221 return SOCK_STREAM; //assume
222 }
223