cutelyst  3.9.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
validatorip.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2017-2022 Matthias Fehring <mf@huessenbergnetz.de>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 #include "validatorip_p.h"
7 
8 #include <utility>
9 
10 #include <QHostAddress>
11 #include <QRegularExpression>
12 
13 using namespace Cutelyst;
14 
16  Constraints constraints,
17  const Cutelyst::ValidatorMessages &messages,
18  const QString &defValKey)
19  : ValidatorRule(*new ValidatorIpPrivate(field, constraints, messages, defValKey))
20 {
21 }
22 
24 {
25 }
26 
28 {
29  ValidatorReturnType result;
30 
31  Q_D(const ValidatorIp);
32 
33  const QString v = value(params);
34 
35  if (!v.isEmpty()) {
36 
37  if (ValidatorIp::validate(v, d->constraints)) {
38  result.value.setValue(v);
39  } else {
40  result.errorMessage = validationError(c);
41  qCDebug(C_VALIDATOR,
42  "ValidatorIp: Validation failed for field %s at %s::%s: not a valid IP address "
43  "within the constraints.",
44  qPrintable(field()),
45  qPrintable(c->controllerName()),
46  qPrintable(c->actionName()));
47  }
48 
49  } else {
50  defaultValue(c, &result, "ValidatorIp");
51  }
52 
53  return result;
54 }
55 
56 bool ValidatorIp::validate(const QString &value, Constraints constraints)
57 {
58  bool valid = true;
59 
60  // simple check for an IPv4 address with four parts, because QHostAddress also tolerates
61  // addresses like 192.168.2 and fills them with 0 somewhere
62  if (!value.contains(QLatin1Char(':')) && !value.contains(QRegularExpression(QStringLiteral(
63  "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$")))) {
64 
65  valid = false;
66 
67  } else {
68 
69  // private IPv4 subnets
70  static const std::vector<std::pair<QHostAddress, int>> ipv4Private(
71  {// Used for local communications within a private network
72  // https://tools.ietf.org/html/rfc1918
73  {QHostAddress(QStringLiteral("10.0.0.0")), 8},
74 
75  // Used for link-local addresses between two hosts on a single link when no IP address
76  // is otherwise specified, such as would have normally been retrieved from a DHCP
77  // server https://tools.ietf.org/html/rfc3927
78  {QHostAddress(QStringLiteral("169.254.0.0")), 16},
79 
80  // Used for local communications within a private network
81  // https://tools.ietf.org/html/rfc1918
82  {QHostAddress(QStringLiteral("172.16.0.0")), 12},
83 
84  // Used for local communications within a private network
85  // https://tools.ietf.org/html/rfc1918
86  {QHostAddress(QStringLiteral("192.168.0.0")), 12}});
87 
88  // reserved IPv4 subnets
89  static const std::vector<std::pair<QHostAddress, int>> ipv4Reserved(
90  {// Used for broadcast messages to the current ("this")
91  // https://tools.ietf.org/html/rfc1700
92  {QHostAddress(QStringLiteral("0.0.0.0")), 8},
93 
94  // Used for communications between a service provider and its subscribers when using a
95  // carrier-grade NAT https://tools.ietf.org/html/rfc6598
96  {QHostAddress(QStringLiteral("100.64.0.0")), 10},
97 
98  // Used for loopback addresses to the local host
99  // https://tools.ietf.org/html/rfc990
100  {QHostAddress(QStringLiteral("127.0.0.1")), 8},
101 
102  // Used for the IANA IPv4 Special Purpose Address Registry
103  // https://tools.ietf.org/html/rfc5736
104  {QHostAddress(QStringLiteral("192.0.0.0")), 24},
105 
106  // Assigned as "TEST-NET" for use in documentation and examples. It should not be used
107  // publicly. https://tools.ietf.org/html/rfc5737
108  {QHostAddress(QStringLiteral("192.0.2.0")), 24},
109 
110  // Used by 6to4 anycast relays
111  // https://tools.ietf.org/html/rfc3068
112  {QHostAddress(QStringLiteral("192.88.99.0")), 24},
113 
114  // Used for testing of inter-network communications between two separate subnets
115  // https://tools.ietf.org/html/rfc2544
116  {QHostAddress(QStringLiteral("198.18.0.0")), 15},
117 
118  // Assigned as "TEST-NET-2" for use in documentation and examples. It should not be
119  // used publicly. https://tools.ietf.org/html/rfc5737
120  {QHostAddress(QStringLiteral("198.51.100.0")), 24},
121 
122  // Assigned as "TEST-NET-3" for use in documentation and examples. It should not be
123  // used publicly. https://tools.ietf.org/html/rfc5737
124  {QHostAddress(QStringLiteral("203.0.113.0")), 24},
125 
126  // Reserved for future use
127  // https://tools.ietf.org/html/rfc6890
128  {QHostAddress(QStringLiteral("240.0.0.0")), 4},
129 
130  // Reserved for the "limited broadcast" destination address
131  // https://tools.ietf.org/html/rfc6890
132  {QHostAddress(QStringLiteral("255.255.255.255")), 32}});
133 
134  // private IPv6 subnets
135  static const std::vector<std::pair<QHostAddress, int>> ipv6Private(
136  {// unique local address
137  {QHostAddress(QStringLiteral("fc00::")), 7},
138 
139  // link-local address
140  {QHostAddress(QStringLiteral("fe80::")), 10}});
141 
142  // reserved IPv6 subnets
143  static const std::vector<std::pair<QHostAddress, int>> ipv6Reserved(
144  {// unspecified address
145  {QHostAddress(QStringLiteral("::")), 128},
146 
147  // loopback address to the loca host
148  {QHostAddress(QStringLiteral("::1")), 128},
149 
150  // IPv4 mapped addresses
151  {QHostAddress(QStringLiteral("::ffff:0:0")), 96},
152 
153  // discard prefix
154  // https://tools.ietf.org/html/rfc6666
155  {QHostAddress(QStringLiteral("100::")), 64},
156 
157  // IPv4/IPv6 translation
158  // https://tools.ietf.org/html/rfc6052
159  {QHostAddress(QStringLiteral("64:ff9b::")), 96},
160 
161  // Teredo tunneling
162  {QHostAddress(QStringLiteral("2001::")), 32},
163 
164  // deprected (previously ORCHID)
165  {QHostAddress(QStringLiteral("2001:10::")), 28},
166 
167  // ORCHIDv2
168  {QHostAddress(QStringLiteral("2001:20::")), 28},
169 
170  // addresses used in documentation and example source code
171  {QHostAddress(QStringLiteral("2001:db8::")), 32},
172 
173  // 6to4
174  {QHostAddress(QStringLiteral("2002::")), 16}});
175 
176  QHostAddress a;
177 
178  if (a.setAddress(value)) {
179 
180  if (!constraints.testFlag(NoConstraint)) {
181 
182  if (a.protocol() == QAbstractSocket::IPv4Protocol) {
183 
184  if (constraints.testFlag(IPv6Only)) {
185  valid = false;
186  }
187 
188  if (valid && (constraints.testFlag(NoPrivateRange) ||
189  constraints.testFlag(PublicOnly))) {
190 
191  for (const std::pair<QHostAddress, int> &subnet : ipv4Private) {
192  if (a.isInSubnet(subnet.first, subnet.second)) {
193  valid = false;
194  break;
195  }
196  }
197  }
198 
199  if (valid && (constraints.testFlag(NoReservedRange) ||
200  constraints.testFlag(PublicOnly))) {
201 
202  for (const std::pair<QHostAddress, int> &subnet : ipv4Reserved) {
203  if (a.isInSubnet(subnet.first, subnet.second)) {
204  valid = false;
205  break;
206  }
207  }
208  }
209 
210  if (valid &&
211  (constraints.testFlag(NoMultiCast) || constraints.testFlag(PublicOnly))) {
212  if (a.isInSubnet(QHostAddress(QStringLiteral("224.0.0.0")), 4)) {
213  valid = false;
214  }
215  }
216 
217  } else {
218 
219  if (constraints.testFlag(IPv4Only)) {
220  valid = false;
221  }
222 
223  if (valid && (constraints.testFlag(NoPrivateRange) ||
224  constraints.testFlag(PublicOnly))) {
225 
226  for (const std::pair<QHostAddress, int> &subnet : ipv6Private) {
227  if (a.isInSubnet(subnet.first, subnet.second)) {
228  valid = false;
229  break;
230  }
231  }
232  }
233 
234  if (valid && (constraints.testFlag(NoReservedRange) ||
235  constraints.testFlag(PublicOnly))) {
236 
237  for (const std::pair<QHostAddress, int> &subnet : ipv6Reserved) {
238  if (a.isInSubnet(subnet.first, subnet.second)) {
239  valid = false;
240  break;
241  }
242  }
243  }
244 
245  if (valid &&
246  (constraints.testFlag(NoMultiCast) || constraints.testFlag(PublicOnly))) {
247  if (a.isInSubnet(QHostAddress(QStringLiteral("ff00::")), 8)) {
248  valid = false;
249  }
250  }
251  }
252  }
253 
254  } else {
255  valid = false;
256  }
257  }
258 
259  return valid;
260 }
261 
263 {
264  QString error;
265  Q_UNUSED(errorData)
266  const QString _label = label(c);
267  if (_label.isEmpty()) {
268  error = c->translate("Cutelyst::ValidatorIp", "IP address is invalid or not acceptable.");
269  } else {
270  //: %1 will be replaced by the field label
271  error = c->translate("Cutelyst::ValidatorIp",
272  "The IP address in the “%1” field is invalid or not acceptable.")
273  .arg(_label);
274  }
275  return error;
276 }
QString validationError(Context *c, const QVariant &errorData=QVariant()) const
Returns a descriptive error message if validation failed.
Stores custom error messages and the input field label.
~ValidatorIp() override
Deconstructs the ip validator.
Definition: validatorip.cpp:23
The Cutelyst Context.
Definition: context.h:38
bool isEmpty() const const
QString translate(const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
Definition: context.cpp:490
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
Base class for all validator rules.
ValidatorIp(const QString &field, Constraints constraints=NoConstraint, const ValidatorMessages &messages=ValidatorMessages(), const QString &defValKey=QString())
Constructs a new ip validator.
Definition: validatorip.cpp:15
QString label(Context *c) const
Returns the human readable field label used for generic error messages.
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString value(const ParamsMultiMap &params) const
Returns the value of the field from the input params.
static bool validate(const QString &value, Constraints constraints=NoConstraint)
Returns true if value is a valid IP address within the constraints.
Definition: validatorip.cpp:56
QString field() const
Returns the name of the field to validate.
Contains the result of a single input parameter validation.
Definition: validatorrule.h:49
Checks if the field value is a valid IP address.
Definition: validatorip.h:33
QString arg(Args &&... args) const const
void defaultValue(Context *c, ValidatorReturnType *result, const char *validatorName) const
I a defValKey has been set in the constructor, this will try to get the default value from the stash ...
QString genericValidationError(Context *c, const QVariant &errorData=QVariant()) const override
Returns a generic error message if validation failed.
void setValue(QVariant &&value)