Updated license
[anna.git] / source / diameter / codec / basetypes / Address.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 // Local
38 #include <anna/diameter/codec/basetypes/Address.hpp>
39
40 // Standard
41 #include <arpa/inet.h>
42
43 #include <anna/core/functions.hpp>
44
45
46 //------------------------------------------------------------------------------
47 //------------------------------------------------------- Address::updateBasic()
48 //------------------------------------------------------------------------------
49 void anna::diameter::codec::basetypes::Address::updateBasic() throw(anna::RuntimeException) {
50   std::string result;
51   // address version
52   result.push_back((S8)(a_address.Version >> 8));
53   result.push_back((S8)a_address.Version);
54
55   // address value
56   if(a_address.isIPv4()) {
57     if(a_abbreviatePresentation) {
58       unsigned char buf[sizeof(struct in6_addr)];
59       int s = inet_pton(AF_INET, a_address.Value.c_str(), buf);
60
61       if(s > 0) {
62         result.push_back((S8) buf[0]);
63         result.push_back((S8) buf[1]);
64         result.push_back((S8) buf[2]);
65         result.push_back((S8) buf[3]);
66       } else {
67         if(s < 0) perror("inet_pton");
68
69         throw anna::RuntimeException("Address::setValue | Wrong IPv4 address format", ANNA_FILE_LOCATION);
70       }
71     } else {
72       if(!anna::functions::isIPv4(a_address.Value))
73         throw anna::RuntimeException("Address::setValue | Wrong IPv4 address format", ANNA_FILE_LOCATION);
74
75       int dec1, dec2, dec3, dec4; // U32 type is valid, 'int' better: argument for %d must be 'int *' (http://www.cplusplus.com/reference/clibrary/cstdio/sscanf/)
76       sscanf(a_address.Value.c_str(), "%d.%d.%d.%d", &dec1, &dec2, &dec3, &dec4);
77       result.push_back((S8) dec1);
78       result.push_back((S8) dec2);
79       result.push_back((S8) dec3);
80       result.push_back((S8) dec4);
81     }
82   } else if(a_address.isIPv6()) {
83     if(a_abbreviatePresentation) {
84       unsigned char buf[sizeof(struct in6_addr)];
85       int s = inet_pton(AF_INET6, a_address.Value.c_str(), buf);
86
87       if(s > 0) {
88         result.push_back((S8) buf[0]);
89         result.push_back((S8) buf[1]);
90         result.push_back((S8) buf[2]);
91         result.push_back((S8) buf[3]);
92         result.push_back((S8) buf[4]);
93         result.push_back((S8) buf[5]);
94         result.push_back((S8) buf[6]);
95         result.push_back((S8) buf[7]);
96         result.push_back((S8) buf[8]);
97         result.push_back((S8) buf[9]);
98         result.push_back((S8) buf[10]);
99         result.push_back((S8) buf[11]);
100         result.push_back((S8) buf[12]);
101         result.push_back((S8) buf[13]);
102         result.push_back((S8) buf[14]);
103         result.push_back((S8) buf[15]);
104       } else {
105         if(s < 0) perror("inet_pton");
106
107         throw anna::RuntimeException("Address::setValue | Wrong IPv6 address format", ANNA_FILE_LOCATION);
108       }
109     } else {
110       if(!anna::functions::isIPv6(a_address.Value))
111         throw anna::RuntimeException("Address::setValue | Wrong IPv6 address format", ANNA_FILE_LOCATION);
112
113       int hex1, hex2, hex3, hex4, hex5, hex6, hex7, hex8; // U16 type is not valid: argument for %X must be 'int *' (http://www.cplusplus.com/reference/clibrary/cstdio/sscanf/)
114       sscanf(a_address.Value.c_str(), "%X:%X:%X:%X:%X:%X:%X:%X", &hex1, &hex2, &hex3, &hex4, &hex5, &hex6, &hex7, &hex8);
115       result.push_back((S8)(hex1 >> 8));
116       result.push_back((S8)(hex1 & 0x00FF));
117       result.push_back((S8)(hex2 >> 8));
118       result.push_back((S8)(hex2 & 0x00FF));
119       result.push_back((S8)(hex3 >> 8));
120       result.push_back((S8)(hex3 & 0x00FF));
121       result.push_back((S8)(hex4 >> 8));
122       result.push_back((S8)(hex4 & 0x00FF));
123       result.push_back((S8)(hex5 >> 8));
124       result.push_back((S8)(hex5 & 0x00FF));
125       result.push_back((S8)(hex6 >> 8));
126       result.push_back((S8)(hex6 & 0x00FF));
127       result.push_back((S8)(hex7 >> 8));
128       result.push_back((S8)(hex7 & 0x00FF));
129       result.push_back((S8)(hex8 >> 8));
130       result.push_back((S8)(hex8 & 0x00FF));
131     }
132   } else if(a_address.isE164()) {
133     result += a_address.Value; // direct assignment
134   } else {
135     result += a_address.Value; // direct assignment
136   }
137
138   OctetString::setValue(result);
139 }
140
141
142 //------------------------------------------------------------------------------
143 //------------------------------------------------ Address::setPrintableString()
144 //------------------------------------------------------------------------------
145 void anna::diameter::codec::basetypes::Address::setPrintableString(const char * printableString) throw(anna::RuntimeException) {
146   // First: User will update child class members
147   // Auxiliary
148   iana_address_t address;
149   int version;
150   std::string value;
151   // From message.dtd:
152   // [ Address ] ('<type (IANA Address Family Number)>|<value>' representation; i.e. '1|192.168.0.1'(IPv4), '8|34616279266'(E164), etc.
153   //             Type (and pipe) field could be avoided for IPv4 and IPv6 address types (a light parse checking is done: one colon for
154   //             IPv6, one dot for IPv4). Internal engine always includes type on data field, which is also recommended for inputs.
155   //             Currently, only IPv4, IPv6 and E164 address types have a known printable presentation)
156   std::string source = printableString;
157   size_t pipePos = source.find("|");
158
159   if(pipePos != std::string::npos) {
160     if(pipePos == 0)
161       throw anna::RuntimeException("Address::fromPrintableString | Unreconized printable source: missing type before pipe (see 'Diameter message DTD' for [ Address ] printable format)", ANNA_FILE_LOCATION);
162
163     std::string beforePipe(printableString, pipePos);
164     address.Version = atoi(beforePipe.c_str());
165     address.Value.assign(printableString, pipePos + 1, source.size() - pipePos - 1);
166   }
167   // For backward compatibility (configurations, tests cases, etc.)
168   else { // only IPv4/v6 addresses are supported with a light checking (version pre-assumption): dot for IPv4, colon for IPv6. Anyway, updateBasic() will do regexp for supposed version
169     if(strstr(printableString, ":"))
170       address.setIPv6(printableString);
171     else if(strstr(printableString, "."))
172       address.setIPv4(printableString);
173     else
174       throw anna::RuntimeException("Address::fromPrintableString | Unreconized printable source (see 'Diameter message DTD' for [ Address ] printable format)", ANNA_FILE_LOCATION);
175   }
176
177   // Assignments
178   setIANAAddress(address);
179 }
180
181
182 //------------------------------------------------------------------------------
183 //------------------------------------------------ Address::setPrintableString()
184 //------------------------------------------------------------------------------
185 std::string anna::diameter::codec::basetypes::Address::asPrintableString() throw(anna::RuntimeException) {
186   bool knownPrintablePresentation = (a_address.isIPv4() || a_address.isIPv6() || a_address.isE164());
187
188   if(!knownPrintablePresentation)
189     throw anna::RuntimeException("Address::asPrintableString | Only IPv4, IPv6 and E164 have a known printable presentation", ANNA_FILE_LOCATION);
190
191   return anna::functions::asString("%d|%s", a_address.getVersion(), a_address.getValue());
192 }
193
194
195 //------------------------------------------------------------------------------
196 //------------------------------------------------------------ Address::decode()
197 //------------------------------------------------------------------------------
198 void anna::diameter::codec::basetypes::Address::decode(const char* buffer, const int size) throw(anna::RuntimeException) {
199   if(!buffer)
200     throw anna::RuntimeException("Address::decode | Null Buffer provided", ANNA_FILE_LOCATION);
201
202   char str[INET6_ADDRSTRLEN];
203
204   // IP Version
205   if(size < 2)
206     throw anna::RuntimeException("Address::decode | At least 2 bytes needed to decode address version", ANNA_FILE_LOCATION);
207
208   a_address.Version = (((U16)buffer[0] << 8) & 0xFF00) +
209                       ((U16)buffer[1]        & 0x00FF); // two first octets
210
211   // IP Address
212   if(a_address.isIPv4()) {  // 6 bytes
213     if(size != 6)
214       throw anna::RuntimeException("Address::decode | 2+4(=6) bytes needed to decode IPv4 address", ANNA_FILE_LOCATION);
215
216     if(a_abbreviatePresentation) {
217       if(inet_ntop(AF_INET, buffer + 2, str, INET_ADDRSTRLEN) != NULL) {
218         a_address.Value = str;
219       } else {
220         throw anna::RuntimeException("Address::decode | Error decoding IPv4 address", ANNA_FILE_LOCATION);
221       }
222     } else {
223       a_address.Value = anna::functions::asString("%d.%d.%d.%d", (U8)buffer[2], (U8)buffer[3], (U8)buffer[4], (U8)buffer[5]);
224     }
225   } else if(a_address.isIPv6()) {  // 18 bytes
226     if(size != 18)
227       throw anna::RuntimeException("Address::decode | 2+16(=18) bytes needed to decode IPv6 address", ANNA_FILE_LOCATION);
228
229     if(a_abbreviatePresentation) {
230       if(inet_ntop(AF_INET6, buffer + 2, str, INET6_ADDRSTRLEN) != NULL) {
231         a_address.Value = str;
232       } else {
233         throw anna::RuntimeException("Address::decode | Error decoding IPv6 address", ANNA_FILE_LOCATION);
234       }
235     } else {
236       a_address.Value = anna::functions::asString("%X:%X:%X:%X:%X:%X:%X:%X",
237                         ((((U8)buffer[2]) << 8) & 0xFF00 /* same as (U16 cast)*/) +
238                         (((U8)buffer[3])        & 0x00FF),
239                         ((((U8)buffer[4]) << 8) & 0xFF00) +
240                         (((U8)buffer[5])        & 0x00FF),
241                         ((((U8)buffer[6]) << 8) & 0xFF00) +
242                         (((U8)buffer[7])        & 0x00FF),
243                         ((((U8)buffer[8]) << 8) & 0xFF00) +
244                         (((U8)buffer[9])        & 0x00FF),
245                         ((((U8)buffer[10]) << 8) & 0xFF00) +
246                         (((U8)buffer[11])        & 0x00FF),
247                         ((((U8)buffer[12]) << 8) & 0xFF00) +
248                         (((U8)buffer[13])        & 0x00FF),
249                         ((((U8)buffer[14]) << 8) & 0xFF00) +
250                         (((U8)buffer[15])        & 0x00FF),
251                         ((((U8)buffer[16]) << 8) & 0xFF00) +
252                         (((U8)buffer[17])        & 0x00FF));
253     }
254   } else if(a_address.isE164()) {  // n bytes = 2 + address length
255     a_address.Value.assign(buffer + 2, size - 2);  // Direct assignment
256   } else { // Generic decoding (could support non printable addresses but they must be...)
257     a_address.Value.assign(buffer + 2, size - 2);
258   }
259
260   // Base class decode()
261   OctetString::decode(buffer, size);
262 }