Cutelyst  2.3.0
validatorip.cpp
1 /*
2  * Copyright (C) 2017-2018 Matthias Fehring <kontakt@buschmann23.de>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include "validatorip_p.h"
20 #include <QHostAddress>
21 #include <QRegularExpression>
22 #include <utility>
23 
24 using namespace Cutelyst;
25 
26 ValidatorIp::ValidatorIp(const QString &field, Constraints constraints, const Cutelyst::ValidatorMessages &messages, const QString &defValKey) :
27  ValidatorRule(*new ValidatorIpPrivate(field, constraints, messages, defValKey))
28 {
29 }
30 
32 {
33 }
34 
36 {
37  ValidatorReturnType result;
38 
39  Q_D(const ValidatorIp);
40 
41  const QString v = value(params);
42 
43  if (!v.isEmpty()) {
44 
45  if (ValidatorIp::validate(v, d->constraints)) {
46  result.value.setValue<QString>(v);
47  } else {
48  result.errorMessage = validationError(c);
49  qCDebug(C_VALIDATOR, "ValidatorIp: Validation failed for field %s at %s::%s: not a valid IP address within the constraints.", qPrintable(field()), qPrintable(c->controllerName()), qPrintable(c->actionName()));
50  }
51 
52  } else {
53  defaultValue(c, &result, "ValidatorIp");
54  }
55 
56  return result;
57 }
58 
59 bool ValidatorIp::validate(const QString &value, Constraints constraints)
60 {
61  bool valid = true;
62 
63  // simple check for an IPv4 address with four parts, because QHostAddress also tolerates addresses like 192.168.2 and fills them with 0 somewhere
64  if (!value.contains(QLatin1Char(':')) && !value.contains(QRegularExpression(QStringLiteral("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$")))) {
65 
66  valid = false;
67 
68  } else {
69 
70  // private IPv4 subnets
71  static const std::vector<std::pair<QHostAddress,int>> ipv4Private({
72  // Used for local communications within a private network
73  // https://tools.ietf.org/html/rfc1918
74  {QHostAddress(QStringLiteral("10.0.0.0")), 8},
75 
76  // Used for link-local addresses between two hosts on a single link when no IP address
77  // is otherwise specified, such as would have normally been retrieved from a DHCP server
78  // https://tools.ietf.org/html/rfc3927
79  {QHostAddress(QStringLiteral("169.254.0.0")), 16},
80 
81  // Used for local communications within a private network
82  // https://tools.ietf.org/html/rfc1918
83  {QHostAddress(QStringLiteral("172.16.0.0")), 12},
84 
85  // Used for local communications within a private network
86  // https://tools.ietf.org/html/rfc1918
87  {QHostAddress(QStringLiteral("192.168.0.0")), 12}
88  });
89 
90  // reserved IPv4 subnets
91  static const std::vector<std::pair<QHostAddress,int>> ipv4Reserved({
92  // Used for broadcast messages to the current ("this")
93  // https://tools.ietf.org/html/rfc1700
94  {QHostAddress(QStringLiteral("0.0.0.0")), 8},
95 
96  // Used for communications between a service provider and its subscribers when using a carrier-grade NAT
97  // https://tools.ietf.org/html/rfc6598
98  {QHostAddress(QStringLiteral("100.64.0.0")), 10},
99 
100  // Used for loopback addresses to the local host
101  // https://tools.ietf.org/html/rfc990
102  {QHostAddress(QStringLiteral("127.0.0.1")), 8},
103 
104  // Used for the IANA IPv4 Special Purpose Address Registry
105  // https://tools.ietf.org/html/rfc5736
106  {QHostAddress(QStringLiteral("192.0.0.0")), 24},
107 
108  // Assigned as "TEST-NET" for use in documentation and examples. It should not be used publicly.
109  // https://tools.ietf.org/html/rfc5737
110  {QHostAddress(QStringLiteral("192.0.2.0")), 24},
111 
112  // Used by 6to4 anycast relays
113  // https://tools.ietf.org/html/rfc3068
114  {QHostAddress(QStringLiteral("192.88.99.0")), 24},
115 
116  // Used for testing of inter-network communications between two separate subnets
117  // https://tools.ietf.org/html/rfc2544
118  {QHostAddress(QStringLiteral("198.18.0.0")), 15},
119 
120  // Assigned as "TEST-NET-2" for use in documentation and examples. It should not be used publicly.
121  // https://tools.ietf.org/html/rfc5737
122  {QHostAddress(QStringLiteral("198.51.100.0")), 24},
123 
124  // Assigned as "TEST-NET-3" for use in documentation and examples. It should not be used publicly.
125  // https://tools.ietf.org/html/rfc5737
126  {QHostAddress(QStringLiteral("203.0.113.0")), 24},
127 
128  // Reserved for future use
129  // https://tools.ietf.org/html/rfc6890
130  {QHostAddress(QStringLiteral("240.0.0.0")), 4},
131 
132  // Reserved for the "limited broadcast" destination address
133  // https://tools.ietf.org/html/rfc6890
134  {QHostAddress(QStringLiteral("255.255.255.255")), 32}
135  });
136 
137 
138  // private IPv6 subnets
139  static const std::vector<std::pair<QHostAddress,int>> ipv6Private({
140  // unique local address
141  {QHostAddress(QStringLiteral("fc00::")), 7},
142 
143  // link-local address
144  {QHostAddress(QStringLiteral("fe80::")), 10}
145  });
146 
147  // reserved IPv6 subnets
148  static const std::vector<std::pair<QHostAddress,int>> ipv6Reserved({
149  // unspecified address
150  {QHostAddress(QStringLiteral("::")), 128},
151 
152  // loopback address to the loca host
153  {QHostAddress(QStringLiteral("::1")), 128},
154 
155  // IPv4 mapped addresses
156  {QHostAddress(QStringLiteral("::ffff:0:0")), 96},
157 
158  // discard prefix
159  // https://tools.ietf.org/html/rfc6666
160  {QHostAddress(QStringLiteral("100::")), 64},
161 
162  // IPv4/IPv6 translation
163  // https://tools.ietf.org/html/rfc6052
164  {QHostAddress(QStringLiteral("64:ff9b::")), 96},
165 
166  // Teredo tunneling
167  {QHostAddress(QStringLiteral("2001::")), 32},
168 
169  // deprected (previously ORCHID)
170  {QHostAddress(QStringLiteral("2001:10::")), 28},
171 
172  // ORCHIDv2
173  {QHostAddress(QStringLiteral("2001:20::")), 28},
174 
175  // addresses used in documentation and example source code
176  {QHostAddress(QStringLiteral("2001:db8::")), 32},
177 
178  // 6to4
179  {QHostAddress(QStringLiteral("2002::")), 16}
180  });
181 
182  QHostAddress a;
183 
184  if (a.setAddress(value)) {
185 
186  if (!constraints.testFlag(NoConstraint)) {
187 
188  if (a.protocol() == QAbstractSocket::IPv4Protocol) {
189 
190  if (constraints.testFlag(IPv6Only)) {
191  valid = false;
192  }
193 
194  if (valid && (constraints.testFlag(NoPrivateRange) || constraints.testFlag(PublicOnly))) {
195 
196  for (const std::pair<QHostAddress,int> &subnet : ipv4Private) {
197  if (a.isInSubnet(subnet.first, subnet.second)) {
198  valid = false;
199  break;
200  }
201  }
202  }
203 
204  if (valid && (constraints.testFlag(NoReservedRange) || constraints.testFlag(PublicOnly))) {
205 
206  for (const std::pair<QHostAddress,int> &subnet : ipv4Reserved) {
207  if (a.isInSubnet(subnet.first, subnet.second)) {
208  valid = false;
209  break;
210  }
211  }
212  }
213 
214  if (valid && (constraints.testFlag(NoMultiCast) || constraints.testFlag(PublicOnly))) {
215  if (a.isInSubnet(QHostAddress(QStringLiteral("224.0.0.0")), 4)) {
216  valid = false;
217  }
218  }
219 
220  } else {
221 
222  if (constraints.testFlag(IPv4Only)) {
223  valid = false;
224  }
225 
226  if (valid && (constraints.testFlag(NoPrivateRange) || constraints.testFlag(PublicOnly))) {
227 
228  for (const std::pair<QHostAddress,int> &subnet : ipv6Private) {
229  if (a.isInSubnet(subnet.first, subnet.second)) {
230  valid = false;
231  break;
232  }
233  }
234  }
235 
236  if (valid && (constraints.testFlag(NoReservedRange) || constraints.testFlag(PublicOnly))) {
237 
238  for (const std::pair<QHostAddress,int> &subnet : ipv6Reserved) {
239  if (a.isInSubnet(subnet.first, subnet.second)) {
240  valid = false;
241  break;
242  }
243  }
244  }
245 
246  if (valid && (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 
262 QString ValidatorIp::genericValidationError(Context *c, const QVariant &errorData) const
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  error = c->translate("Cutelyst::ValidatorIp", "The IP address in the “%1” field is invalid or not acceptable.").arg(_label);
271  }
272  return error;
273 }
QMap< QString, QString > ParamsMultiMap
~ValidatorIp()
Deconstructs the ip validator.
Definition: validatorip.cpp:31
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.
The Cutelyst Context.
Definition: context.h:50
QString translate(const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
Definition: context.cpp:414
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:26
QString label(Context *c) const
Returns the human readable field label used for generic error messages.
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:59
QString field() const
Returns the name of the field to validate.
Contains the result of a single input parameter validation.
Definition: validatorrule.h:62
Checks if the field value is a valid IP address.
Definition: validatorip.h:44
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.