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