e707fa7cdbd58cc464ad43a06cf4fe249020b725
[anna.git] / source / diameter / codec / basetypes / Address.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 // Local
10 #include <anna/diameter/codec/basetypes/Address.hpp>
11
12 // Standard
13 #include <arpa/inet.h>
14
15 #include <anna/core/functions.hpp>
16
17
18 //------------------------------------------------------------------------------
19 //------------------------------------------------------- Address::updateBasic()
20 //------------------------------------------------------------------------------
21 void anna::diameter::codec::basetypes::Address::updateBasic() throw(anna::RuntimeException) {
22   std::string result;
23   // address version
24   result.push_back((S8)(a_address.Version >> 8));
25   result.push_back((S8)a_address.Version);
26
27   // address value
28   if(a_address.isIPv4()) {
29     if(a_abbreviatePresentation) {
30       unsigned char buf[sizeof(struct in6_addr)];
31       int s = inet_pton(AF_INET, a_address.Value.c_str(), buf);
32
33       if(s > 0) {
34         result.push_back((S8) buf[0]);
35         result.push_back((S8) buf[1]);
36         result.push_back((S8) buf[2]);
37         result.push_back((S8) buf[3]);
38       } else {
39         if(s < 0) perror("inet_pton");
40
41         throw anna::RuntimeException("Address::setValue | Wrong IPv4 address format", ANNA_FILE_LOCATION);
42       }
43     } else {
44       if(!anna::functions::isIPv4(a_address.Value))
45         throw anna::RuntimeException("Address::setValue | Wrong IPv4 address format", ANNA_FILE_LOCATION);
46
47       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/)
48       sscanf(a_address.Value.c_str(), "%d.%d.%d.%d", &dec1, &dec2, &dec3, &dec4);
49       result.push_back((S8) dec1);
50       result.push_back((S8) dec2);
51       result.push_back((S8) dec3);
52       result.push_back((S8) dec4);
53     }
54   } else if(a_address.isIPv6()) {
55     if(a_abbreviatePresentation) {
56       unsigned char buf[sizeof(struct in6_addr)];
57       int s = inet_pton(AF_INET6, a_address.Value.c_str(), buf);
58
59       if(s > 0) {
60         result.push_back((S8) buf[0]);
61         result.push_back((S8) buf[1]);
62         result.push_back((S8) buf[2]);
63         result.push_back((S8) buf[3]);
64         result.push_back((S8) buf[4]);
65         result.push_back((S8) buf[5]);
66         result.push_back((S8) buf[6]);
67         result.push_back((S8) buf[7]);
68         result.push_back((S8) buf[8]);
69         result.push_back((S8) buf[9]);
70         result.push_back((S8) buf[10]);
71         result.push_back((S8) buf[11]);
72         result.push_back((S8) buf[12]);
73         result.push_back((S8) buf[13]);
74         result.push_back((S8) buf[14]);
75         result.push_back((S8) buf[15]);
76       } else {
77         if(s < 0) perror("inet_pton");
78
79         throw anna::RuntimeException("Address::setValue | Wrong IPv6 address format", ANNA_FILE_LOCATION);
80       }
81     } else {
82       if(!anna::functions::isIPv6(a_address.Value))
83         throw anna::RuntimeException("Address::setValue | Wrong IPv6 address format", ANNA_FILE_LOCATION);
84
85       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/)
86       sscanf(a_address.Value.c_str(), "%X:%X:%X:%X:%X:%X:%X:%X", &hex1, &hex2, &hex3, &hex4, &hex5, &hex6, &hex7, &hex8);
87       result.push_back((S8)(hex1 >> 8));
88       result.push_back((S8)(hex1 & 0x00FF));
89       result.push_back((S8)(hex2 >> 8));
90       result.push_back((S8)(hex2 & 0x00FF));
91       result.push_back((S8)(hex3 >> 8));
92       result.push_back((S8)(hex3 & 0x00FF));
93       result.push_back((S8)(hex4 >> 8));
94       result.push_back((S8)(hex4 & 0x00FF));
95       result.push_back((S8)(hex5 >> 8));
96       result.push_back((S8)(hex5 & 0x00FF));
97       result.push_back((S8)(hex6 >> 8));
98       result.push_back((S8)(hex6 & 0x00FF));
99       result.push_back((S8)(hex7 >> 8));
100       result.push_back((S8)(hex7 & 0x00FF));
101       result.push_back((S8)(hex8 >> 8));
102       result.push_back((S8)(hex8 & 0x00FF));
103     }
104   } else if(a_address.isE164()) {
105     result += a_address.Value; // direct assignment
106   } else {
107     result += a_address.Value; // direct assignment
108   }
109
110   OctetString::setValue(result);
111 }
112
113
114 //------------------------------------------------------------------------------
115 //------------------------------------------------ Address::setPrintableString()
116 //------------------------------------------------------------------------------
117 void anna::diameter::codec::basetypes::Address::setPrintableString(const char * printableString) throw(anna::RuntimeException) {
118   // First: User will update child class members
119   // Auxiliary
120   iana_address_t address;
121   int version;
122   std::string value;
123   // From message.dtd:
124   // [ Address ] ('<type (IANA Address Family Number)>|<value>' representation; i.e. '1|192.168.0.1'(IPv4), '8|34616279266'(E164), etc.
125   //             Type (and pipe) field could be avoided for IPv4 and IPv6 address types (a light parse checking is done: one colon for
126   //             IPv6, one dot for IPv4). Internal engine always includes type on data field, which is also recommended for inputs.
127   //             Currently, only IPv4, IPv6 and E164 address types have a known printable presentation)
128   std::string source = printableString;
129   size_t pipePos = source.find("|");
130
131   if(pipePos != std::string::npos) {
132     if(pipePos == 0)
133       throw anna::RuntimeException("Address::fromPrintableString | Unreconized printable source: missing type before pipe (see 'Diameter message DTD' for [ Address ] printable format)", ANNA_FILE_LOCATION);
134
135     std::string beforePipe(printableString, pipePos);
136     address.Version = atoi(beforePipe.c_str());
137     address.Value.assign(printableString, pipePos + 1, source.size() - pipePos - 1);
138   }
139   // For backward compatibility (configurations, tests cases, etc.)
140   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
141     if(strstr(printableString, ":"))
142       address.setIPv6(printableString);
143     else if(strstr(printableString, "."))
144       address.setIPv4(printableString);
145     else
146       throw anna::RuntimeException("Address::fromPrintableString | Unreconized printable source (see 'Diameter message DTD' for [ Address ] printable format)", ANNA_FILE_LOCATION);
147   }
148
149   // Assignments
150   setIANAAddress(address);
151 }
152
153
154 //------------------------------------------------------------------------------
155 //------------------------------------------------ Address::setPrintableString()
156 //------------------------------------------------------------------------------
157 std::string anna::diameter::codec::basetypes::Address::asPrintableString() throw(anna::RuntimeException) {
158   bool knownPrintablePresentation = (a_address.isIPv4() || a_address.isIPv6() || a_address.isE164());
159
160   if(!knownPrintablePresentation)
161     throw anna::RuntimeException("Address::asPrintableString | Only IPv4, IPv6 and E164 have a known printable presentation", ANNA_FILE_LOCATION);
162
163   return anna::functions::asString("%d|%s", a_address.getVersion(), a_address.getValue());
164 }
165
166
167 //------------------------------------------------------------------------------
168 //------------------------------------------------------------ Address::decode()
169 //------------------------------------------------------------------------------
170 void anna::diameter::codec::basetypes::Address::decode(const char* buffer, const int size) throw(anna::RuntimeException) {
171   if(!buffer)
172     throw anna::RuntimeException("Address::decode | Null Buffer provided", ANNA_FILE_LOCATION);
173
174   char str[INET6_ADDRSTRLEN];
175
176   // IP Version
177   if(size < 2)
178     throw anna::RuntimeException("Address::decode | At least 2 bytes needed to decode address version", ANNA_FILE_LOCATION);
179
180   a_address.Version = (((U16)buffer[0] << 8) & 0xFF00) +
181                       ((U16)buffer[1]        & 0x00FF); // two first octets
182
183   // IP Address
184   if(a_address.isIPv4()) {  // 6 bytes
185     if(size != 6)
186       throw anna::RuntimeException("Address::decode | 2+4(=6) bytes needed to decode IPv4 address", ANNA_FILE_LOCATION);
187
188     if(a_abbreviatePresentation) {
189       if(inet_ntop(AF_INET, buffer + 2, str, INET_ADDRSTRLEN) != NULL) {
190         a_address.Value = str;
191       } else {
192         throw anna::RuntimeException("Address::decode | Error decoding IPv4 address", ANNA_FILE_LOCATION);
193       }
194     } else {
195       a_address.Value = anna::functions::asString("%d.%d.%d.%d", (U8)buffer[2], (U8)buffer[3], (U8)buffer[4], (U8)buffer[5]);
196     }
197   } else if(a_address.isIPv6()) {  // 18 bytes
198     if(size != 18)
199       throw anna::RuntimeException("Address::decode | 2+16(=18) bytes needed to decode IPv6 address", ANNA_FILE_LOCATION);
200
201     if(a_abbreviatePresentation) {
202       if(inet_ntop(AF_INET6, buffer + 2, str, INET6_ADDRSTRLEN) != NULL) {
203         a_address.Value = str;
204       } else {
205         throw anna::RuntimeException("Address::decode | Error decoding IPv6 address", ANNA_FILE_LOCATION);
206       }
207     } else {
208       a_address.Value = anna::functions::asString("%X:%X:%X:%X:%X:%X:%X:%X",
209                         ((((U8)buffer[2]) << 8) & 0xFF00 /* same as (U16 cast)*/) +
210                         (((U8)buffer[3])        & 0x00FF),
211                         ((((U8)buffer[4]) << 8) & 0xFF00) +
212                         (((U8)buffer[5])        & 0x00FF),
213                         ((((U8)buffer[6]) << 8) & 0xFF00) +
214                         (((U8)buffer[7])        & 0x00FF),
215                         ((((U8)buffer[8]) << 8) & 0xFF00) +
216                         (((U8)buffer[9])        & 0x00FF),
217                         ((((U8)buffer[10]) << 8) & 0xFF00) +
218                         (((U8)buffer[11])        & 0x00FF),
219                         ((((U8)buffer[12]) << 8) & 0xFF00) +
220                         (((U8)buffer[13])        & 0x00FF),
221                         ((((U8)buffer[14]) << 8) & 0xFF00) +
222                         (((U8)buffer[15])        & 0x00FF),
223                         ((((U8)buffer[16]) << 8) & 0xFF00) +
224                         (((U8)buffer[17])        & 0x00FF));
225     }
226   } else if(a_address.isE164()) {  // n bytes = 2 + address length
227     a_address.Value.assign(buffer + 2, size - 2);  // Direct assignment
228   } else { // Generic decoding (could support non printable addresses but they must be...)
229     a_address.Value.assign(buffer + 2, size - 2);
230   }
231
232   // Base class decode()
233   OctetString::decode(buffer, size);
234 }