1/*
2 * rdata.c
3 *
4 * rdata implementation
5 *
6 * a Net::DNS like library for C
7 *
8 * (c) NLnet Labs, 2004-2006
9 *
10 * See the file LICENSE for the license
11 */
12
13#include <ldns/config.h>
14
15#include <ldns/ldns.h>
16
17/*
18 * Access functions
19 * do this as functions to get type checking
20 */
21
22/* read */
23size_t
24ldns_rdf_size(const ldns_rdf *rd)
25{
26	assert(rd != NULL);
27	return rd->_size;
28}
29
30ldns_rdf_type
31ldns_rdf_get_type(const ldns_rdf *rd)
32{
33	assert(rd != NULL);
34	return rd->_type;
35}
36
37uint8_t *
38ldns_rdf_data(const ldns_rdf *rd)
39{
40	assert(rd != NULL);
41	return rd->_data;
42}
43
44/* write */
45void
46ldns_rdf_set_size(ldns_rdf *rd, size_t size)
47{
48	assert(rd != NULL);
49	rd->_size = size;
50}
51
52void
53ldns_rdf_set_type(ldns_rdf *rd, ldns_rdf_type type)
54{
55	assert(rd != NULL);
56	rd->_type = type;
57}
58
59void
60ldns_rdf_set_data(ldns_rdf *rd, void *data)
61{
62	/* only copy the pointer */
63	assert(rd != NULL);
64	rd->_data = data;
65}
66
67/* for types that allow it, return
68 * the native/host order type */
69uint8_t
70ldns_rdf2native_int8(const ldns_rdf *rd)
71{
72	uint8_t data;
73
74	/* only allow 8 bit rdfs */
75	if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_BYTE) {
76		return 0;
77	}
78
79	memcpy(&data, ldns_rdf_data(rd), sizeof(data));
80	return data;
81}
82
83uint16_t
84ldns_rdf2native_int16(const ldns_rdf *rd)
85{
86	uint16_t data;
87
88	/* only allow 16 bit rdfs */
89	if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_WORD) {
90		return 0;
91	}
92
93	memcpy(&data, ldns_rdf_data(rd), sizeof(data));
94	return ntohs(data);
95}
96
97uint32_t
98ldns_rdf2native_int32(const ldns_rdf *rd)
99{
100	uint32_t data;
101
102	/* only allow 32 bit rdfs */
103	if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_DOUBLEWORD) {
104		return 0;
105	}
106
107	memcpy(&data, ldns_rdf_data(rd), sizeof(data));
108	return ntohl(data);
109}
110
111time_t
112ldns_rdf2native_time_t(const ldns_rdf *rd)
113{
114	uint32_t data;
115
116	/* only allow 32 bit rdfs */
117	if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_DOUBLEWORD ||
118			ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_TIME) {
119		return 0;
120	}
121	memcpy(&data, ldns_rdf_data(rd), sizeof(data));
122	return (time_t)ntohl(data);
123}
124
125ldns_rdf *
126ldns_native2rdf_int8(ldns_rdf_type type, uint8_t value)
127{
128	return ldns_rdf_new_frm_data(type, LDNS_RDF_SIZE_BYTE, &value);
129}
130
131ldns_rdf *
132ldns_native2rdf_int16(ldns_rdf_type type, uint16_t value)
133{
134	uint16_t *rdf_data = LDNS_XMALLOC(uint16_t, 1);
135        ldns_rdf* rdf;
136	if (!rdf_data) {
137		return NULL;
138	}
139	ldns_write_uint16(rdf_data, value);
140	rdf = ldns_rdf_new(type, LDNS_RDF_SIZE_WORD, rdf_data);
141        if(!rdf)
142                LDNS_FREE(rdf_data);
143        return rdf;
144}
145
146ldns_rdf *
147ldns_native2rdf_int32(ldns_rdf_type type, uint32_t value)
148{
149	uint32_t *rdf_data = LDNS_XMALLOC(uint32_t, 1);
150        ldns_rdf* rdf;
151	if (!rdf_data) {
152		return NULL;
153	}
154	ldns_write_uint32(rdf_data, value);
155	rdf = ldns_rdf_new(type, LDNS_RDF_SIZE_DOUBLEWORD, rdf_data);
156        if(!rdf)
157                LDNS_FREE(rdf_data);
158        return rdf;
159}
160
161ldns_rdf *
162ldns_native2rdf_int16_data(size_t size, uint8_t *data)
163{
164	uint8_t *rdf_data = LDNS_XMALLOC(uint8_t, size + 2);
165        ldns_rdf* rdf;
166	if (!rdf_data) {
167		return NULL;
168	}
169	ldns_write_uint16(rdf_data, size);
170	memcpy(rdf_data + 2, data, size);
171	rdf = ldns_rdf_new(LDNS_RDF_TYPE_INT16_DATA, size + 2, rdf_data);
172        if(!rdf)
173                LDNS_FREE(rdf_data);
174        return rdf;
175}
176
177/* note: data must be allocated memory */
178ldns_rdf *
179ldns_rdf_new(ldns_rdf_type type, size_t size, void *data)
180{
181	ldns_rdf *rd;
182	rd = LDNS_MALLOC(ldns_rdf);
183	if (!rd) {
184		return NULL;
185	}
186	ldns_rdf_set_size(rd, size);
187	ldns_rdf_set_type(rd, type);
188	ldns_rdf_set_data(rd, data);
189	return rd;
190}
191
192ldns_rdf *
193ldns_rdf_new_frm_data(ldns_rdf_type type, size_t size, const void *data)
194{
195	ldns_rdf *rdf;
196
197	/* if the size is too big, fail */
198	if (size > LDNS_MAX_RDFLEN) {
199		return NULL;
200	}
201
202	/* allocate space */
203	rdf = LDNS_MALLOC(ldns_rdf);
204	if (!rdf) {
205		return NULL;
206	}
207	rdf->_data = LDNS_XMALLOC(uint8_t, size);
208	if (!rdf->_data) {
209		LDNS_FREE(rdf);
210		return NULL;
211	}
212
213	/* set the values */
214	ldns_rdf_set_type(rdf, type);
215	ldns_rdf_set_size(rdf, size);
216	memcpy(rdf->_data, data, size);
217
218	return rdf;
219}
220
221ldns_rdf *
222ldns_rdf_clone(const ldns_rdf *rd)
223{
224	assert(rd != NULL);
225	return (ldns_rdf_new_frm_data( ldns_rdf_get_type(rd),
226		ldns_rdf_size(rd), ldns_rdf_data(rd)));
227}
228
229void
230ldns_rdf_deep_free(ldns_rdf *rd)
231{
232	if (rd) {
233		if (rd->_data) {
234			LDNS_FREE(rd->_data);
235		}
236		LDNS_FREE(rd);
237	}
238}
239
240void
241ldns_rdf_free(ldns_rdf *rd)
242{
243	if (rd) {
244		LDNS_FREE(rd);
245	}
246}
247
248ldns_rdf *
249ldns_rdf_new_frm_str(ldns_rdf_type type, const char *str)
250{
251	ldns_rdf *rdf = NULL;
252	ldns_status status;
253
254	switch (type) {
255	case LDNS_RDF_TYPE_DNAME:
256		status = ldns_str2rdf_dname(&rdf, str);
257		break;
258	case LDNS_RDF_TYPE_INT8:
259		status = ldns_str2rdf_int8(&rdf, str);
260		break;
261	case LDNS_RDF_TYPE_INT16:
262		status = ldns_str2rdf_int16(&rdf, str);
263		break;
264	case LDNS_RDF_TYPE_INT32:
265		status = ldns_str2rdf_int32(&rdf, str);
266		break;
267	case LDNS_RDF_TYPE_A:
268		status = ldns_str2rdf_a(&rdf, str);
269		break;
270	case LDNS_RDF_TYPE_AAAA:
271		status = ldns_str2rdf_aaaa(&rdf, str);
272		break;
273	case LDNS_RDF_TYPE_STR:
274		status = ldns_str2rdf_str(&rdf, str);
275		break;
276	case LDNS_RDF_TYPE_APL:
277		status = ldns_str2rdf_apl(&rdf, str);
278		break;
279	case LDNS_RDF_TYPE_B64:
280		status = ldns_str2rdf_b64(&rdf, str);
281		break;
282	case LDNS_RDF_TYPE_B32_EXT:
283		status = ldns_str2rdf_b32_ext(&rdf, str);
284		break;
285	case LDNS_RDF_TYPE_HEX:
286		status = ldns_str2rdf_hex(&rdf, str);
287		break;
288	case LDNS_RDF_TYPE_NSEC:
289		status = ldns_str2rdf_nsec(&rdf, str);
290		break;
291	case LDNS_RDF_TYPE_TYPE:
292		status = ldns_str2rdf_type(&rdf, str);
293		break;
294	case LDNS_RDF_TYPE_CLASS:
295		status = ldns_str2rdf_class(&rdf, str);
296		break;
297	case LDNS_RDF_TYPE_CERT_ALG:
298		status = ldns_str2rdf_cert_alg(&rdf, str);
299		break;
300	case LDNS_RDF_TYPE_ALG:
301		status = ldns_str2rdf_alg(&rdf, str);
302		break;
303	case LDNS_RDF_TYPE_UNKNOWN:
304		status = ldns_str2rdf_unknown(&rdf, str);
305		break;
306	case LDNS_RDF_TYPE_TIME:
307		status = ldns_str2rdf_time(&rdf, str);
308		break;
309	case LDNS_RDF_TYPE_PERIOD:
310		status = ldns_str2rdf_period(&rdf, str);
311		break;
312	case LDNS_RDF_TYPE_HIP:
313		status = ldns_str2rdf_hip(&rdf, str);
314		break;
315	case LDNS_RDF_TYPE_SERVICE:
316		status = ldns_str2rdf_service(&rdf, str);
317		break;
318	case LDNS_RDF_TYPE_LOC:
319		status = ldns_str2rdf_loc(&rdf, str);
320		break;
321	case LDNS_RDF_TYPE_WKS:
322		status = ldns_str2rdf_wks(&rdf, str);
323		break;
324	case LDNS_RDF_TYPE_NSAP:
325		status = ldns_str2rdf_nsap(&rdf, str);
326		break;
327	case LDNS_RDF_TYPE_ATMA:
328		status = ldns_str2rdf_atma(&rdf, str);
329		break;
330	case LDNS_RDF_TYPE_IPSECKEY:
331		status = ldns_str2rdf_ipseckey(&rdf, str);
332		break;
333	case LDNS_RDF_TYPE_NSEC3_SALT:
334		status = ldns_str2rdf_nsec3_salt(&rdf, str);
335		break;
336	case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
337		status = ldns_str2rdf_b32_ext(&rdf, str);
338		break;
339	case LDNS_RDF_TYPE_ILNP64:
340		status = ldns_str2rdf_ilnp64(&rdf, str);
341		break;
342	case LDNS_RDF_TYPE_EUI48:
343		status = ldns_str2rdf_eui48(&rdf, str);
344		break;
345	case LDNS_RDF_TYPE_EUI64:
346		status = ldns_str2rdf_eui64(&rdf, str);
347		break;
348	case LDNS_RDF_TYPE_TAG:
349		status = ldns_str2rdf_tag(&rdf, str);
350		break;
351	case LDNS_RDF_TYPE_LONG_STR:
352		status = ldns_str2rdf_long_str(&rdf, str);
353		break;
354	case LDNS_RDF_TYPE_CERTIFICATE_USAGE:
355		status = ldns_str2rdf_certificate_usage(&rdf, str);
356		break;
357	case LDNS_RDF_TYPE_SELECTOR:
358		status = ldns_str2rdf_selector(&rdf, str);
359		break;
360	case LDNS_RDF_TYPE_MATCHING_TYPE:
361		status = ldns_str2rdf_matching_type(&rdf, str);
362		break;
363	case LDNS_RDF_TYPE_NONE:
364	default:
365		/* default default ??? */
366		status = LDNS_STATUS_ERR;
367		break;
368	}
369	if (LDNS_STATUS_OK == status) {
370		ldns_rdf_set_type(rdf, type);
371		return rdf;
372	}
373	if (rdf) {
374		LDNS_FREE(rdf);
375	}
376	return NULL;
377}
378
379ldns_status
380ldns_rdf_new_frm_fp(ldns_rdf **rdf, ldns_rdf_type type, FILE *fp)
381{
382	return ldns_rdf_new_frm_fp_l(rdf, type, fp, NULL);
383}
384
385ldns_status
386ldns_rdf_new_frm_fp_l(ldns_rdf **rdf, ldns_rdf_type type, FILE *fp, int *line_nr)
387{
388	char *line;
389	ldns_rdf *r;
390	ssize_t t;
391
392	line = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
393	if (!line) {
394		return LDNS_STATUS_MEM_ERR;
395	}
396
397	/* read an entire line in from the file */
398	if ((t = ldns_fget_token_l(fp, line, LDNS_PARSE_SKIP_SPACE, 0, line_nr)) == -1 || t == 0) {
399		LDNS_FREE(line);
400		return LDNS_STATUS_SYNTAX_RDATA_ERR;
401	}
402	r =  ldns_rdf_new_frm_str(type, (const char*) line);
403	LDNS_FREE(line);
404	if (rdf) {
405		*rdf = r;
406		return LDNS_STATUS_OK;
407	} else {
408		return LDNS_STATUS_NULL;
409	}
410}
411
412ldns_rdf *
413ldns_rdf_address_reverse(const ldns_rdf *rd)
414{
415	uint8_t buf_4[LDNS_IP4ADDRLEN];
416	uint8_t buf_6[LDNS_IP6ADDRLEN * 2];
417	ldns_rdf *rev;
418	ldns_rdf *in_addr;
419	ldns_rdf *ret_dname;
420	uint8_t octet;
421	uint8_t nnibble;
422	uint8_t nibble;
423	uint8_t i, j;
424
425	char *char_dname;
426	int nbit;
427
428	if (ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_A &&
429			ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_AAAA) {
430		return NULL;
431	}
432
433	in_addr = NULL;
434	ret_dname = NULL;
435
436	switch(ldns_rdf_get_type(rd)) {
437		case LDNS_RDF_TYPE_A:
438			/* the length of the buffer is 4 */
439			buf_4[3] = ldns_rdf_data(rd)[0];
440			buf_4[2] = ldns_rdf_data(rd)[1];
441			buf_4[1] = ldns_rdf_data(rd)[2];
442			buf_4[0] = ldns_rdf_data(rd)[3];
443			in_addr = ldns_dname_new_frm_str("in-addr.arpa.");
444			if (!in_addr) {
445				return NULL;
446			}
447			/* make a new rdf and convert that back  */
448			rev = ldns_rdf_new_frm_data( LDNS_RDF_TYPE_A,
449				LDNS_IP4ADDRLEN, (void*)&buf_4);
450			if (!rev) {
451				LDNS_FREE(in_addr);
452				return NULL;
453			}
454
455			/* convert rev to a string */
456			char_dname = ldns_rdf2str(rev);
457			if (!char_dname) {
458				LDNS_FREE(in_addr);
459				ldns_rdf_deep_free(rev);
460				return NULL;
461			}
462			/* transform back to rdf with type dname */
463			ret_dname = ldns_dname_new_frm_str(char_dname);
464			if (!ret_dname) {
465				LDNS_FREE(in_addr);
466				ldns_rdf_deep_free(rev);
467				LDNS_FREE(char_dname);
468				return NULL;
469			}
470			/* not needed anymore */
471			ldns_rdf_deep_free(rev);
472			LDNS_FREE(char_dname);
473			break;
474		case LDNS_RDF_TYPE_AAAA:
475			/* some foo magic to reverse the nibbles ... */
476
477			for (nbit = 127; nbit >= 0; nbit = nbit - 4) {
478				/* calculate octet (8 bit) */
479				octet = ( ((unsigned int) nbit) & 0x78) >> 3;
480				/* calculate nibble */
481				nnibble = ( ((unsigned int) nbit) & 0x04) >> 2;
482				/* extract nibble */
483				nibble = (ldns_rdf_data(rd)[octet] & ( 0xf << (4 * (1 -
484						 nnibble)) ) ) >> ( 4 * (1 -
485						nnibble));
486
487				buf_6[(LDNS_IP6ADDRLEN * 2 - 1) -
488					(octet * 2 + nnibble)] =
489						(uint8_t)ldns_int_to_hexdigit((int)nibble);
490			}
491
492			char_dname = LDNS_XMALLOC(char, (LDNS_IP6ADDRLEN * 4));
493			if (!char_dname) {
494				return NULL;
495			}
496			char_dname[LDNS_IP6ADDRLEN * 4 - 1] = '\0'; /* closure */
497
498			/* walk the string and add . 's */
499			for (i = 0, j = 0; i < LDNS_IP6ADDRLEN * 2; i++, j = j + 2) {
500				char_dname[j] = (char)buf_6[i];
501				if (i != LDNS_IP6ADDRLEN * 2 - 1) {
502					char_dname[j + 1] = '.';
503				}
504			}
505			in_addr = ldns_dname_new_frm_str("ip6.arpa.");
506			if (!in_addr) {
507				LDNS_FREE(char_dname);
508				return NULL;
509			}
510
511			/* convert rev to a string */
512			ret_dname = ldns_dname_new_frm_str(char_dname);
513			LDNS_FREE(char_dname);
514			if (!ret_dname) {
515				ldns_rdf_deep_free(in_addr);
516				return NULL;
517			}
518			break;
519		default:
520			break;
521	}
522	/* add the suffix */
523	rev = ldns_dname_cat_clone(ret_dname, in_addr);
524
525	ldns_rdf_deep_free(ret_dname);
526	ldns_rdf_deep_free(in_addr);
527	return rev;
528}
529
530ldns_status
531ldns_rdf_hip_get_alg_hit_pk(ldns_rdf *rdf, uint8_t* alg,
532                            uint8_t *hit_size, uint8_t** hit,
533                            uint16_t *pk_size, uint8_t** pk)
534{
535	uint8_t *data;
536	size_t rdf_size;
537
538	if (! rdf || ! alg || ! hit || ! hit_size || ! pk || ! pk_size) {
539		return LDNS_STATUS_INVALID_POINTER;
540	} else if (ldns_rdf_get_type(rdf) != LDNS_RDF_TYPE_HIP) {
541		return LDNS_STATUS_INVALID_RDF_TYPE;
542	} else if ((rdf_size = ldns_rdf_size(rdf)) < 6) {
543		return LDNS_STATUS_WIRE_RDATA_ERR;
544	}
545	data = ldns_rdf_data(rdf);
546	*hit_size = data[0];
547	*alg      = data[1];
548	*pk_size  = ldns_read_uint16(data + 2);
549	*hit      = data + 4;
550	*pk       = data + 4 + *hit_size;
551	if (*hit_size == 0 || *pk_size == 0 ||
552			rdf_size < (size_t) *hit_size + *pk_size + 4) {
553		return LDNS_STATUS_WIRE_RDATA_ERR;
554	}
555	return LDNS_STATUS_OK;
556}
557
558ldns_status
559ldns_rdf_hip_new_frm_alg_hit_pk(ldns_rdf** rdf, uint8_t alg,
560                                uint8_t hit_size, uint8_t *hit,
561				uint16_t pk_size, uint8_t *pk)
562{
563	uint8_t *data;
564
565	if (! rdf) {
566		return LDNS_STATUS_INVALID_POINTER;
567	}
568	if (4 + hit_size + pk_size > LDNS_MAX_RDFLEN) {
569		return LDNS_STATUS_RDATA_OVERFLOW;
570	}
571	data = LDNS_XMALLOC(uint8_t, 4 + hit_size + pk_size);
572	if (data == NULL) {
573		return LDNS_STATUS_MEM_ERR;
574	}
575	data[0] = hit_size;
576	data[1] = alg;
577	ldns_write_uint16(data + 2, pk_size);
578	memcpy(data + 4, hit, hit_size);
579	memcpy(data + 4 + hit_size, pk, pk_size);
580	*rdf = ldns_rdf_new(LDNS_RDF_TYPE_HIP, 4 + hit_size + pk_size, data);
581	if (! *rdf) {
582		LDNS_FREE(data);
583		return LDNS_STATUS_MEM_ERR;
584	}
585	return LDNS_STATUS_OK;
586}
587
588ldns_status
589ldns_octet(char *word, size_t *length)
590{
591    char *s;
592    char *p;
593    *length = 0;
594
595    for (s = p = word; *s != '\0'; s++,p++) {
596        switch (*s) {
597            case '.':
598                if (s[1] == '.') {
599		    return LDNS_STATUS_EMPTY_LABEL;
600                }
601                *p = *s;
602                (*length)++;
603                break;
604            case '\\':
605                if ('0' <= s[1] && s[1] <= '9' &&
606                    '0' <= s[2] && s[2] <= '9' &&
607                    '0' <= s[3] && s[3] <= '9') {
608                    /* \DDD seen */
609                    int val = ((s[1] - '0') * 100 +
610                           (s[2] - '0') * 10 + (s[3] - '0'));
611
612                    if (0 <= val && val <= 255) {
613                        /* this also handles \0 */
614                        s += 3;
615                        *p = val;
616                        (*length)++;
617                    } else {
618                        return LDNS_STATUS_DDD_OVERFLOW;
619                    }
620                } else {
621                    /* an espaced character, like \<space> ?
622                    * remove the '\' keep the rest */
623                    *p = *++s;
624                    (*length)++;
625                }
626                break;
627            case '\"':
628                /* non quoted " Is either first or the last character in
629                 * the string */
630
631                *p = *++s; /* skip it */
632                (*length)++;
633		/* I'm not sure if this is needed in libdns... MG */
634                if ( *s == '\0' ) {
635                    /* ok, it was the last one */
636                    *p  = '\0';
637		    return LDNS_STATUS_OK;
638                }
639                break;
640            default:
641                *p = *s;
642                (*length)++;
643                break;
644        }
645    }
646    *p = '\0';
647    return LDNS_STATUS_OK;
648}
649
650int
651ldns_rdf_compare(const ldns_rdf *rd1, const ldns_rdf *rd2)
652{
653	uint16_t i1, i2, i;
654	uint8_t *d1, *d2;
655
656	/* only when both are not NULL we can say anything about them */
657	if (!rd1 && !rd2) {
658		return 0;
659	}
660	if (!rd1 || !rd2) {
661		return -1;
662	}
663	i1 = ldns_rdf_size(rd1);
664	i2 = ldns_rdf_size(rd2);
665
666	if (i1 < i2) {
667		return -1;
668	} else if (i1 > i2) {
669		return +1;
670	} else {
671		d1 = (uint8_t*)ldns_rdf_data(rd1);
672		d2 = (uint8_t*)ldns_rdf_data(rd2);
673		for(i = 0; i < i1; i++) {
674			if (d1[i] < d2[i]) {
675				return -1;
676			} else if (d1[i] > d2[i]) {
677				return +1;
678			}
679		}
680	}
681	return 0;
682}
683
684uint32_t
685ldns_str2period(const char *nptr, const char **endptr)
686{
687	int sign = 0;
688	uint32_t i = 0;
689	uint32_t seconds = 0;
690
691	for(*endptr = nptr; **endptr; (*endptr)++) {
692		switch (**endptr) {
693			case ' ':
694			case '\t':
695				break;
696			case '-':
697				if(sign == 0) {
698					sign = -1;
699				} else {
700					return seconds;
701				}
702				break;
703			case '+':
704				if(sign == 0) {
705					sign = 1;
706				} else {
707					return seconds;
708				}
709				break;
710			case 's':
711			case 'S':
712				seconds += i;
713				i = 0;
714				break;
715			case 'm':
716			case 'M':
717				seconds += i * 60;
718				i = 0;
719				break;
720			case 'h':
721			case 'H':
722				seconds += i * 60 * 60;
723				i = 0;
724				break;
725			case 'd':
726			case 'D':
727				seconds += i * 60 * 60 * 24;
728				i = 0;
729				break;
730			case 'w':
731			case 'W':
732				seconds += i * 60 * 60 * 24 * 7;
733				i = 0;
734				break;
735			case '0':
736			case '1':
737			case '2':
738			case '3':
739			case '4':
740			case '5':
741			case '6':
742			case '7':
743			case '8':
744			case '9':
745				i *= 10;
746				i += (**endptr - '0');
747				break;
748			default:
749				seconds += i;
750				/* disregard signedness */
751				return seconds;
752		}
753	}
754	seconds += i;
755	/* disregard signedness */
756	return seconds;
757}
758