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