e4017dd54e346fdf5f8073942a3e59a09ab59141
[shibboleth/cpp-sp.git] / shibsp / util / IPRange.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * @file IPRange.cpp
23  * 
24  * Represents a range of IP addresses.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "util/IPRange.h"
30
31 #include <xmltooling/logging.h>
32
33 #ifdef WIN32
34 # include <winsock2.h>
35 # include <ws2tcpip.h>
36 #else
37 # include <netdb.h>
38 # include <netinet/in.h>
39 #endif
40
41 using namespace shibsp;
42 using namespace xmltooling::logging;
43 using namespace xmltooling;
44 using namespace std;
45
46 namespace {
47     // Gets the byte-level representation of a numeric IP address.
48     struct addrinfo* parseIPAddress(const char* s)
49     {
50         struct addrinfo* ret = nullptr;
51         struct addrinfo hints;
52
53         memset(&hints, 0, sizeof(hints));
54         hints.ai_flags = AI_NUMERICHOST;
55         hints.ai_family = AF_UNSPEC;
56
57         if (getaddrinfo(s, nullptr, &hints, &ret) != 0)
58             return nullptr;
59         if (ret) {
60             if (ret->ai_family != AF_INET
61 #ifdef AF_INET6
62                 && ret->ai_family != AF_INET6
63 #endif
64                 ) {
65                 freeaddrinfo(ret);
66                 return nullptr;
67             }
68         }
69         return ret;
70     }
71 };
72
73 IPRange::IPRange(const bitset<32>& address, int maskSize) : m_addressLength(32)
74 {
75     if (maskSize < 0 || maskSize > m_addressLength)
76         throw ConfigurationException("CIDR prefix length out of range.");
77
78     for (int i = m_addressLength - maskSize; i < m_addressLength; ++i)
79         m_mask4.set(i, true);
80
81     m_network4 = address;
82     m_network4 &= m_mask4;
83 }
84
85 IPRange::IPRange(const bitset<128>& address, int maskSize) : m_addressLength(128)
86 {
87     if (maskSize < 0 || maskSize > m_addressLength)
88         throw ConfigurationException("CIDR prefix length out of range.");
89
90     for (int i = m_addressLength - maskSize; i < m_addressLength; ++i)
91         m_mask6.set(i, true);
92
93     m_network6 = address;
94     m_network6 &= m_mask6;
95 }
96
97 bool IPRange::contains(const char* address) const
98 {
99
100     struct addrinfo* parsed = parseIPAddress(address);
101     if (!parsed)
102         return false;
103     bool ret  = contains(parsed->ai_addr);
104     freeaddrinfo(parsed);
105     return ret;
106 }
107
108 bool IPRange::contains(const struct sockaddr* address) const
109 {
110
111     Category& log = Category::getInstance(SHIBSP_LOGCAT".IPRange");
112
113     if (address->sa_family == AF_INET) {
114         if (m_addressLength != 32)
115             return false;
116         unsigned long raw = 0;
117         memcpy(&raw, &((struct sockaddr_in*)address)->sin_addr, 4);
118         bitset<32> rawbits((int)ntohl(raw));    // the bitset loads from a host-order variable
119         if (log.isDebugEnabled()) {
120             log.debug(
121                 "comparing address (%s) to network (%s) with mask (%s)",
122                 rawbits.to_string< char, char_traits<char>, allocator<char> >().c_str(),
123                 m_network4.to_string< char, char_traits<char>, allocator<char> >().c_str(),
124                 m_mask4.to_string< char, char_traits<char>, allocator<char> >().c_str()
125                 );
126         }
127         rawbits &= m_mask4;
128         return (rawbits == m_network4);
129     }
130 #ifdef AF_INET6
131     else if (address->sa_family == AF_INET6) {
132         if (m_addressLength != 128)
133             return false;
134         unsigned char raw[16];
135         memcpy(raw, &((struct sockaddr_in6*)address)->sin6_addr, 16);
136         bitset<128> rawbits(raw[0]);
137         for (int i = 1; i < 16; ++i) {
138             rawbits <<= 8;
139             rawbits |= bitset<128>(raw[i]);
140         }
141         if (log.isDebugEnabled()) {
142             log.debug(
143                 "comparing address (%s) to network (%s) with mask (%s)",
144                 rawbits.to_string< char, char_traits<char>, allocator<char> >().c_str(),
145                 m_network6.to_string< char, char_traits<char>, allocator<char> >().c_str(),
146                 m_mask6.to_string< char, char_traits<char>, allocator<char> >().c_str()
147                 );
148         }
149         rawbits &= m_mask6;
150         return (rawbits == m_network6);
151     }
152 #endif
153     return false;
154 }
155
156 IPRange IPRange::parseCIDRBlock(const char* cidrBlock)
157 {
158     string block = cidrBlock;
159     string::size_type sep = block.find("/");
160     if (sep == string::npos) {
161         if (block.find(":") == string::npos)
162             block += "/32";
163         else
164             block += "/128";
165         sep = block.find("/");
166     }
167     struct addrinfo* address = parseIPAddress(block.substr(0, sep).c_str());
168     if (!address)
169         throw ConfigurationException("Unable to parse address in CIDR block.");
170     int maskSize = atoi(block.substr(++sep).c_str());
171     if (address->ai_family == AF_INET) {
172          unsigned long raw = 0;
173          memcpy(&raw, &((struct sockaddr_in*)address->ai_addr)->sin_addr, 4);
174          freeaddrinfo(address);
175          bitset<32> rawbits((int)ntohl(raw));    // the bitset loads from a host-order variable
176          return IPRange(rawbits, maskSize);
177     }
178 #ifdef AF_INET6
179     else if (address->ai_family == AF_INET6) {
180         unsigned char raw[16];
181         memcpy(raw, &((struct sockaddr_in6*)address->ai_addr)->sin6_addr, 16);
182         freeaddrinfo(address);
183         bitset<128> rawbits(raw[0]);
184         for (int i = 1; i < 16; ++i) {
185             rawbits <<= 8;
186             rawbits |= bitset<128>(raw[i]);
187         }
188         return IPRange(rawbits, maskSize);
189     }
190 #endif
191     throw ConfigurationException("Unrecognized address type in CIDR block.");
192 }