Botan  2.19.1
Crypto and TLS for C++11
uri.cpp
Go to the documentation of this file.
1 /*
2 * (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
3 *
4 * Botan is released under the Simplified BSD License (see license.txt)
5 */
6 
7 #include <botan/internal/uri.h>
8 #include <botan/exceptn.h>
9 
10 #include <regex>
11 
12 #if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
13  #include <arpa/inet.h>
14  #include <sys/socket.h>
15  #include <netinet/in.h>
16 #elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
17  #include <ws2tcpip.h>
18 #endif
19 
20 #if defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
21 
22 namespace {
23 
24 constexpr bool isdigit(char ch)
25  {
26  return ch >= '0' && ch <= '9';
27  }
28 
29 bool isDomain(const std::string& domain)
30  {
31 #if defined(__GLIBCXX__) && (__GLIBCXX__ < 20160726)
32  // GCC 4.8 does not support regex
33  BOTAN_UNUSED(domain);
34  return true;
35 #else
36  std::regex re(
37  R"(^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$)");
38  std::cmatch m;
39  return std::regex_match(domain.c_str(), m, re);
40 #endif
41  }
42 
43 bool isIPv4(const std::string& ip)
44  {
45  sockaddr_storage inaddr;
46  return !!inet_pton(AF_INET, ip.c_str(), &inaddr);
47  }
48 
49 bool isIPv6(const std::string& ip)
50  {
51  sockaddr_storage in6addr;
52  return !!inet_pton(AF_INET6, ip.c_str(), &in6addr);
53  }
54 }
55 
56 namespace Botan {
57 
58 URI URI::fromDomain(const std::string& uri)
59  {
60  unsigned port = 0;
61  const auto port_pos = uri.find(':');
62  if(port_pos != std::string::npos)
63  {
64  for(char c : uri.substr(port_pos+1))
65  {
66  if(!isdigit(c))
67  { throw Invalid_Argument("invalid"); }
68  port = port*10 + c - '0';
69  if(port > 65535)
70  { throw Invalid_Argument("invalid"); }
71  }
72  }
73  const auto domain = uri.substr(0, port_pos);
74  if(isIPv4(domain))
75  { throw Invalid_Argument("invalid"); }
76  if(!isDomain(domain))
77  { throw Invalid_Argument("invalid"); }
78  return {Type::Domain, domain, uint16_t(port)};
79  }
80 
81 URI URI::fromIPv4(const std::string& uri)
82  {
83  unsigned port = 0;
84  const auto port_pos = uri.find(':');
85  if(port_pos != std::string::npos)
86  {
87  for(char c : uri.substr(port_pos+1))
88  {
89  if(!isdigit(c))
90  { throw Invalid_Argument("invalid"); }
91  port = port*10 + c - '0';
92  if(port > 65535)
93  { throw Invalid_Argument("invalid"); }
94  }
95  }
96  const auto ip = uri.substr(0, port_pos);
97  if(!isIPv4(ip))
98  { throw Invalid_Argument("invalid"); }
99  return { Type::IPv4, ip, uint16_t(port) };
100  }
101 
102 URI URI::fromIPv6(const std::string& uri)
103  {
104  unsigned port = 0;
105  const auto port_pos = uri.find(']');
106  const bool with_braces = (port_pos != std::string::npos);
107  if((uri[0]=='[') != with_braces)
108  { throw Invalid_Argument("invalid"); }
109 
110  if(with_braces && (uri.size() > port_pos + 1))
111  {
112  if(uri[port_pos+1]!=':')
113  { throw Invalid_Argument("invalid"); }
114  for(char c : uri.substr(port_pos+2))
115  {
116  if(!isdigit(c))
117  { throw Invalid_Argument("invalid"); }
118  port = port*10 + c - '0';
119  if(port > 65535)
120  { throw Invalid_Argument("invalid"); }
121  }
122  }
123  const auto ip = uri.substr((with_braces ? 1 : 0), port_pos - with_braces);
124  if(!isIPv6(ip))
125  { throw Invalid_Argument("invalid"); }
126  return { Type::IPv6, ip, uint16_t(port) };
127  }
128 
129 URI URI::fromAny(const std::string& uri)
130  {
131 
132  bool colon_seen=false;
133  bool non_number=false;
134  if(uri[0]=='[')
135  { return fromIPv6(uri); }
136  for(auto c : uri)
137  {
138  if(c == ':')
139  {
140  if(colon_seen) //seen two ':'
141  { return fromIPv6(uri); }
142  colon_seen = true;
143  }
144  else if(!isdigit(c) && c != '.')
145  {
146  non_number=true;
147  }
148  }
149  if(!non_number)
150  {
151  if(isIPv4(uri.substr(0, uri.find(':'))))
152  {
153  return fromIPv4(uri);
154  }
155  }
156  return fromDomain(uri);
157  }
158 
159 std::string URI::to_string() const
160  {
161  if(type == Type::NotSet)
162  {
163  throw Invalid_Argument("not set");
164  }
165 
166  if(port != 0)
167  {
168  if(type == Type::IPv6)
169  { return "[" + host + "]:" + std::to_string(port); }
170  return host + ":" + std::to_string(port);
171  }
172  return host;
173  }
174 
175 }
176 
177 #else
178 
179 namespace Botan {
180 
181 URI URI::fromDomain(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
182 URI URI::fromIPv4(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
183 URI URI::fromIPv6(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
184 URI URI::fromAny(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
185 
186 }
187 
188 #endif
static URI fromDomain(const std::string &uri)
Definition: uri.cpp:181
std::string to_string() const
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:213
static URI fromIPv6(const std::string &uri)
Definition: uri.cpp:183
const std::string host
Definition: uri.h:43
Definition: alg_id.cpp:13
#define BOTAN_UNUSED(...)
Definition: assert.h:142
static URI fromIPv4(const std::string &uri)
Definition: uri.cpp:182
const uint16_t port
Definition: uri.h:44
Definition: uri.h:17
const Type type
Definition: uri.h:42
static URI fromAny(const std::string &uri)
Definition: uri.cpp:184