1 /*
2 * Project : ipv6calc
3 * File : librfc1886.c
4 * Version : $Id: 4f186565385f82991d93dd6dee130de55acf1004 $
5 * Copyright : 2002-2021 by Peter Bieringer <pb (at) bieringer.de>
6 *
7 * Information:
8 * RFC 1886 conform reverse nibble format string
9 *
10 * Function to format a given address to reverse nibble-by-nibble ip6.int|arpa format
11 *
12 * Intention from the Perl program "ip6_int" written by Keith Owens <kaos at ocs dot com dot au>
13 * some hints taken from ifconfig.c (net-tools)
14 *
15 * Credits to:
16 * Keith Owens <kaos at ocs dot com dot au>
17 * net-tools authors
18 */
19
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <string.h>
23 #include <stdlib.h>
24
25 #include "libipv6calcdebug.h"
26 #include "ipv6calctypes.h"
27 #include "libipv6addr.h"
28 #include "libipv6calc.h"
29 #include "librfc1886.h"
30
31
32 /*
33 * converts IPv6addr_structure to a reverse nibble format string
34 *
35 * in : *ipv6addrp = IPv6 address structure
36 * out: *resultstring = result
37 * ret: ==0: ok, !=0: error
38 */
librfc1886_addr_to_nibblestring(ipv6calc_ipv6addr * ipv6addrp,char * resultstring,const size_t resultstring_length,const uint32_t formatoptions,const char * domain)39 int librfc1886_addr_to_nibblestring(ipv6calc_ipv6addr *ipv6addrp, char *resultstring, const size_t resultstring_length, const uint32_t formatoptions, const char *domain) {
40 int retval = 1;
41 unsigned int nibble;
42 int bit_start, bit_end, nbit;
43 char tempstring[IPV6CALC_STRING_MAX];
44 unsigned int nnibble, noctet;
45
46 DEBUGPRINT_WA(DEBUG_librfc1886, "flag_prefixuse %d", (*ipv6addrp).flag_prefixuse);
47
48 /* 20100909: take care of prefix length before printing the nibbles, but break old behavior */
49 /* 20140331: mask prefix only in case of no printsuffix,printstart,printend */
50 if ((*ipv6addrp).flag_prefixuse != 0 && ((formatoptions & (FORMATOPTION_printsuffix | FORMATOPTION_printstart | FORMATOPTION_printend)) == 0)) {
51 ipv6addrstruct_maskprefix(ipv6addrp);
52 };
53
54 if ( ((formatoptions & (FORMATOPTION_printprefix | FORMATOPTION_printsuffix | FORMATOPTION_printstart | FORMATOPTION_printend)) == 0 ) && ((*ipv6addrp).flag_prefixuse != 0) ) {
55 /* simulate old behavior */
56 bit_start = 1;
57 bit_end = (int) (*ipv6addrp).prefixlength;
58 DEBUGPRINT_NA(DEBUG_librfc1886, "simulate old behavior");
59 } else if ( (*ipv6addrp).flag_startend_use != 0 ) {
60 /* check start and end */
61 if ( (((*ipv6addrp).bit_start - 1) & 0x03) != 0 ) {
62 snprintf(resultstring, resultstring_length, "Start bit number '%u' is not supported because of non-unique representation (value-1 must be dividable by 4)", (unsigned int) (*ipv6addrp).bit_start);
63 retval = 1;
64 return (retval);
65 };
66 if ( ((*ipv6addrp).bit_end & 0x03) != 0 ) {
67 snprintf(resultstring, resultstring_length, "End bit number '%u' is not supported because of non-unique representation (value must be dividable by 4)", (unsigned int) (*ipv6addrp).bit_end);
68 retval = 1;
69 return (retval);
70 };
71
72 bit_start = (int) (*ipv6addrp).bit_start;
73 bit_end = (int) (*ipv6addrp).bit_end;
74 } else {
75 bit_start = 1;
76 bit_end = 128;
77 };
78
79 DEBUGPRINT_WA(DEBUG_librfc1886, "start bit %d end bit %d", bit_start, bit_end);
80
81 /* print out nibble format */
82 /* 127 is lowest bit, 0 is highest bit */
83 resultstring[0] = '\0';
84
85 for (nbit = bit_end - 1; nbit >= bit_start - 1; nbit = nbit - 4) {
86 /* calculate octet (8 bit) */
87 noctet = ( ((unsigned int) nbit) & 0x78) >> 3;
88
89 /* calculate nibble */
90 nnibble = ( ((unsigned int) nbit) & 0x04) >> 2;
91
92 /* extract nibble */
93 nibble = ( (*ipv6addrp).in6_addr.s6_addr[noctet] & ( 0xf << (unsigned int) (4 * (1 - nnibble)) ) ) >> (unsigned int) ( 4 * (1 - nnibble));
94
95 DEBUGPRINT_WA(DEBUG_librfc1886, "bit: %d = noctet: %u, nnibble: %u, octet: %02x, value: %x", nbit, noctet, nnibble, (unsigned int) (*ipv6addrp).in6_addr.s6_addr[noctet], nibble);
96
97 snprintf(tempstring, sizeof(tempstring), "%s%x", resultstring, nibble);
98 if ((nbit < bit_start) && (bit_start != 1)) {
99 /* don't print trailing "." on middle part end */
100 snprintf(resultstring, resultstring_length, "%s", tempstring);
101 } else {
102 snprintf(resultstring, resultstring_length, "%s.", tempstring);
103 };
104 };
105
106 if (bit_start == 1) {
107 snprintf(tempstring, sizeof(tempstring), "%s%s", resultstring, domain);
108 };
109
110 snprintf(resultstring, resultstring_length, "%s", tempstring);
111
112 if ( (formatoptions & FORMATOPTION_printuppercase) != 0 ) {
113 string_to_upcase(resultstring);
114 };
115
116 if ( (formatoptions & FORMATOPTION_printmirrored) != 0 ) {
117 string_to_reverse_dotted(resultstring, resultstring_length);
118 };
119
120 DEBUGPRINT_WA(DEBUG_librfc1886, "Print out: %s", resultstring);
121
122 retval = 0;
123 return (retval);
124 };
125
126
127 /*
128 * function a reverse nibble format string into IPv6addr_structure
129 *
130 * in : inputstring
131 * mod: *ipv6addrp = IPv6 address structure
132 * ret: ==0: ok, !=0: error
133 */
librfc1886_nibblestring_to_ipv6addrstruct(const char * inputstring,ipv6calc_ipv6addr * ipv6addrp,char * resultstring,const size_t resultstring_length)134 int librfc1886_nibblestring_to_ipv6addrstruct(const char *inputstring, ipv6calc_ipv6addr *ipv6addrp, char *resultstring, const size_t resultstring_length) {
135 int retval = 1;
136 char tempstring[IPV6CALC_STRING_MAX], *token, *cptr, **ptrptr;
137 int flag_tld = 0, flag_nld = 0, tokencounter = 0;
138 int n, offset;
139 unsigned int noctet, nibblecounter = 0;
140 uint32_t xdigit;
141
142 if ((strlen(inputstring) < 4) || (strlen(inputstring) > 73)) {
143 /* min: .int */
144 /* max: f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.ip6.arpa. */
145 snprintf(resultstring, resultstring_length, "Error in given nibble string, has not 4 to 73 chars!");
146 return (1);
147 };
148
149 ptrptr = &cptr;
150
151 /* clear output structure */
152 ipv6addr_clearall(ipv6addrp);
153
154 /* reverse copy of string */
155 snprintf(tempstring, sizeof(tempstring), "%s", inputstring);
156 string_to_lowcase(tempstring);
157
158 /* check string */
159 retval = librfc1886_formatcheck(tempstring, resultstring, resultstring_length);
160 if (retval != 0) {
161 return (1);
162 };
163
164 string_to_reverse(tempstring);
165
166 DEBUGPRINT_WA(DEBUG_librfc1886, "reverse copied string: %s", tempstring);
167
168 /* run through nibbles */
169 token = strtok_r(tempstring, ".", ptrptr);
170
171 while(token != NULL) {
172 if (strcmp(token, "apra") == 0) {
173 if (flag_tld == 0) {
174 flag_tld = 1;
175 goto NEXT_token_nibblestring_to_ipv6addrstruct;
176 } else {
177 snprintf(resultstring, resultstring_length, "Top level domain 'arpa' is in wrong place");
178 return (1);
179 };
180 };
181 if (strcmp(token, "tni") == 0) {
182 if (flag_tld == 0) {
183 flag_tld = 1;
184 goto NEXT_token_nibblestring_to_ipv6addrstruct;
185 } else {
186 snprintf(resultstring, resultstring_length, "Top level domain 'int' is in wrong place");
187 return (1);
188 };
189 };
190 if (tokencounter == 1 && flag_tld == 1 && flag_nld == 0) {
191 if (strcmp(token, "6pi") == 0) {
192 flag_nld = 1;
193 goto NEXT_token_nibblestring_to_ipv6addrstruct;
194 } else {
195 snprintf(resultstring, resultstring_length, "Next level domain 'ip6' is in wrong place or missing");
196 return (1);
197 };
198 };
199
200 /* now proceed nibbles */
201 if (strlen(token) > 1) {
202 string_to_reverse(token);
203 snprintf(resultstring, resultstring_length, "Nibble '%s' on dot position %d (from right side) is longer than one char", token, tokencounter + 1);
204 return (1);
205 };
206
207 if (! isxdigit((int) token[0])) {
208 snprintf(resultstring, resultstring_length, "Nibble '%s' on dot position %d (from right side) is not a valid hexdigit", token, tokencounter + 1);
209 return (1);
210 };
211
212 retval = sscanf(token, "%x", &xdigit);
213 if (retval != 1) {
214 snprintf(resultstring, resultstring_length, "Nibble '%s' on dot position %d (from right side) cannot be parsed", token, tokencounter + 1);
215 return (1);
216 };
217
218 if (xdigit > 0xf) {
219 snprintf(resultstring, resultstring_length, "Nibble '%s' on dot position %d (from right side) is out of range", token, tokencounter + 1);
220 return (1);
221 };
222
223 noctet = nibblecounter >> 1; /* divided by 2 */
224
225 if (noctet > 15) {
226 snprintf(resultstring, resultstring_length, "Too many nibbles");
227 return (1);
228 };
229
230 if ( (nibblecounter & 0x01) != 0 ) {
231 /* most significant bits */
232 (*ipv6addrp).in6_addr.s6_addr[noctet] = ((*ipv6addrp).in6_addr.s6_addr[noctet] & 0xf0) | xdigit;
233 } else {
234 /* least significant bits */
235 (*ipv6addrp).in6_addr.s6_addr[noctet] = ((*ipv6addrp).in6_addr.s6_addr[noctet] & 0x0f) | ((uint8_t) xdigit << 4);
236 };
237
238 nibblecounter++;
239
240 NEXT_token_nibblestring_to_ipv6addrstruct:
241 token = strtok_r(NULL, ".", ptrptr);
242 tokencounter++;
243 };
244
245 ipv6addrp->flag_valid = 1;
246 if ((flag_tld == 1) && (flag_nld == 1)) {
247 // started with TLD and NLD, so from top
248 ipv6addrp->flag_prefixuse = 1;
249 ipv6addrp->prefixlength = (uint8_t) nibblecounter << 2;
250 } else {
251 ipv6addrp->flag_prefixuse = 1;
252 ipv6addrp->prefixlength = 128 - ((uint8_t) nibblecounter << 2);
253 // shift now to the right
254 if (nibblecounter < 32) {
255 // shift byte with offset to the right
256 offset = 16 - (nibblecounter >> 1);
257
258 if ( (nibblecounter & 0x01) != 0 ) {
259 offset--;
260 };
261
262 for (n = 15; n >= 15 - (int) (nibblecounter >> 1); n--) {
263 (*ipv6addrp).in6_addr.s6_addr[n] = (*ipv6addrp).in6_addr.s6_addr[n - offset];
264 };
265
266 // clear prefix
267 for (n = 0; n < offset; n++) {
268 (*ipv6addrp).in6_addr.s6_addr[n] = 0;
269 };
270 if ( (nibblecounter & 0x01) != 0 ) {
271 // shift additional one nibble right
272 for (n = 15; n >= 15 - (int) (nibblecounter >> 1); n--) {
273 // save rightmost nibble
274 xdigit = (*ipv6addrp).in6_addr.s6_addr[n] & 0xf;
275 // shift by one nibble right
276 (*ipv6addrp).in6_addr.s6_addr[n] = (*ipv6addrp).in6_addr.s6_addr[n] >> 4;
277 if (n < 15) {
278 // implant saved rightmost nibble on leftmost
279 (*ipv6addrp).in6_addr.s6_addr[n + 1] |= xdigit << 4;
280 };
281 };
282 };
283 };
284 };
285
286 retval = 0;
287 return (retval);
288 };
289
290 /*
291 * checks for proper format of a nibble string
292 * suffix has to be ip6.int or ip6.arpa
293 *
294 * in : string
295 * ret: ==0: ok, !=0: error
296 */
librfc1886_formatcheck(const char * string,char * infostring,const size_t infostring_length)297 int librfc1886_formatcheck(const char *string, char *infostring, const size_t infostring_length) {
298 int nibblecounter = 0, flag_tld = 0, flag_nld = 0, tokencounter = 0;
299 char tempstring[IPV6CALC_STRING_MAX], *token, *cptr, **ptrptr;
300
301 ptrptr = &cptr;
302
303 infostring[0] = '\0'; /* clear string */
304
305 if (strlen(string) >= sizeof(tempstring)) {
306 fprintf(stderr, "Input too long: %s\n", string);
307 return (1);
308 };
309
310 snprintf(tempstring, sizeof(tempstring), "%s", string);
311
312 DEBUGPRINT_WA(DEBUG_librfc1886, "check %s", tempstring);
313
314 string_to_reverse(tempstring);
315
316 /* run through nibbles */
317 token = strtok_r(tempstring, ".", ptrptr);
318
319 while(token != NULL) {
320 DEBUGPRINT_WA(DEBUG_librfc1886, "check token: %s (tld: %d, nld: %d, tokencounter: %d)", token, flag_tld, flag_nld, tokencounter + 1);
321
322 if (strcmp(token, "apra") == 0) {
323 /* arpa (reverse) */
324 DEBUGPRINT_NA(DEBUG_librfc1886, "found: arpa");
325 if (flag_tld == 0 && tokencounter == 0) {
326 DEBUGPRINT_NA(DEBUG_librfc1886, "found TLD: arpa");
327 flag_tld = 1;
328 goto NEXT_librfc1886_formatcheck;
329 } else {
330 snprintf(infostring, infostring_length, "Top level domain 'arpa' is in wrong place");
331 return (1);
332 };
333 };
334 if (strcmp(token, "tni") == 0) {
335 /* int (reverse) */
336 DEBUGPRINT_NA(DEBUG_librfc1886, "found: int");
337 if (flag_tld == 0 && tokencounter == 0) {
338 DEBUGPRINT_NA(DEBUG_librfc1886, "found TLD: int");
339 flag_tld = 1;
340 goto NEXT_librfc1886_formatcheck;
341 } else {
342 snprintf(infostring, infostring_length, "Top level domain 'int' is in wrong place");
343 return (1);
344 };
345 };
346 if (strcmp(token, "6pi") == 0) {
347 /* ip6 (reverse) */
348 DEBUGPRINT_NA(DEBUG_librfc1886, "found: ip6");
349 if (tokencounter == 1 && flag_tld == 1 && flag_nld == 0) {
350 DEBUGPRINT_NA(DEBUG_librfc1886, "found NLD: ip6");
351 flag_nld = 1;
352 goto NEXT_librfc1886_formatcheck;
353 } else {
354 snprintf(infostring, infostring_length, "Next level domain 'ip6' is in wrong place or missing");
355 return (1);
356 };
357 };
358
359 if ((flag_tld != 1) && (flag_nld != 1) && (strlen(string) < 7)) {
360 snprintf(infostring, infostring_length, "Nibble string misses suffix ip6.int or ip6.arpa or is less than 7 chars long");
361 return (1);
362 };
363
364 /* now proceed nibbles */
365 if (strlen(token) > 1) {
366 string_to_reverse(token);
367 snprintf(infostring, infostring_length, "Nibble '%s' on dot position %d (from right side) is longer than one char", token, tokencounter + 1);
368 return (1);
369 };
370
371 if (! isxdigit((int) token[0])) {
372 snprintf(infostring, infostring_length, "Nibble '%s' on dot position %d (from right side) is not a valid hexdigit", token, tokencounter + 1);
373 return (1);
374 };
375
376 nibblecounter++;
377
378 if (nibblecounter > 32) {
379 snprintf(infostring, infostring_length, "Too many nibbles (more than 32)");
380 return (1);
381 };
382
383 NEXT_librfc1886_formatcheck:
384 token = strtok_r(NULL, ".", ptrptr);
385 tokencounter++;
386 };
387
388 DEBUGPRINT_WA(DEBUG_librfc1886, "check %s is ok", string);
389
390 return (0);
391 };
392