Navigation


source: freeDiameter/libfdproto/ostr.c @ 738:d666051658bd

Last change on this file since 738:d666051658bd was 738:d666051658bd, checked in by Sebastien Decugis <sdecugis@nict.go.jp>, 11 years ago

Fix broken 'almostcasecmp' logic

File size: 15.3 KB
Line 
1/*********************************************************************************************************
2* Software License Agreement (BSD License)                                                               *
3* Author: Sebastien Decugis <sdecugis@nict.go.jp>                                                        *
4*                                                                                                        *
5* Copyright (c) 2011, WIDE Project and NICT                                                              *
6* All rights reserved.                                                                                   *
7*                                                                                                        *
8* Redistribution and use of this software in source and binary forms, with or without modification, are  *
9* permitted provided that the following conditions are met:                                              *
10*                                                                                                        *
11* * Redistributions of source code must retain the above                                                 *
12*   copyright notice, this list of conditions and the                                                    *
13*   following disclaimer.                                                                                *
14*                                                                                                        *
15* * Redistributions in binary form must reproduce the above                                              *
16*   copyright notice, this list of conditions and the                                                    *
17*   following disclaimer in the documentation and/or other                                               *
18*   materials provided with the distribution.                                                            *
19*                                                                                                        *
20* * Neither the name of the WIDE Project or NICT nor the                                                 *
21*   names of its contributors may be used to endorse or                                                  *
22*   promote products derived from this software without                                                  *
23*   specific prior written permission of WIDE Project and                                                *
24*   NICT.                                                                                                *
25*                                                                                                        *
26* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT     *
30* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    *
31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
33* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                                             *
34*********************************************************************************************************/
35
36#include "fdproto-internal.h"
37
38#if (!defined(DIAMID_IDNA_IGNORE) && !defined(DIAMID_IDNA_REJECT))
39/* Process IDNA with stringprep -- See RFC5890 -- and libidn documentation... */
40#include <idna.h> /* idna_to_ascii_8z() */
41#endif /* !defined(DIAMID_IDNA_IGNORE) && !defined(DIAMID_IDNA_REJECT) */
42
43/* Similar to strdup with (must have been verified) os0_t */
44os0_t os0dup_int(os0_t s, size_t l) {
45        os0_t r;
46        CHECK_MALLOC_DO( r = malloc(l+1), return NULL );
47        memcpy(r, s, l); /* this might be faster than a strcpy or strdup because it can work with 32 or 64b blocks */
48        r[l] = '\0';
49        return r;
50}
51
52/* case sensitive comparison, fast */
53int fd_os_cmp_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz)
54{
55        ASSERT( os1 && os2);
56        if (os1sz < os2sz)
57                return -1;
58        if (os1sz > os2sz)
59                return 1;
60        return memcmp(os1, os2, os1sz);
61}
62
63/* a local version of tolower() that does not depend on LC_CTYPE locale */
64static inline uint8_t asciitolower(uint8_t a)
65{
66        if ((a >= 'A') && (a <= 'Z'))
67                return a + 32 /* == 'a' - 'A' */;
68        return a;
69}
70
71/* less sensitive to case, slower. */
72int fd_os_almostcasesrch_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz, int *maybefurther)
73{
74        int i;
75        int res = 0;
76       
77        ASSERT( os1 && os2);
78        if (maybefurther)
79                *maybefurther = 0;
80       
81        if (os1sz < os2sz)
82                return -1;
83       
84        if (maybefurther)
85                *maybefurther = 1;
86       
87        if (os1sz > os2sz)
88                return 1;
89       
90        for (i = 0; i < os1sz; i++) {
91                if (os1[i] == os2[i])
92                        continue;
93               
94                if (!res) 
95                        res = os1[i] < os2[i] ? -1 : 1;
96               
97                if (asciitolower(os1[i]) == asciitolower(os2[i])) 
98                        continue;
99               
100                return res;
101        }
102       
103        return 0;
104}
105
106/* Check if the string contains only ASCII */
107int fd_os_is_valid_DiameterIdentity(uint8_t * os, size_t ossz)
108{
109#ifdef DIAMID_IDNA_IGNORE
110       
111        /* Allow anything */
112       
113#else /* DIAMID_IDNA_IGNORE */
114       
115        int i;
116       
117        /* Allow only letters, digits, hyphen, dot */
118        for (i=0; i < ossz; i++) {
119                if (os[i] > 'z')
120                        break;
121                if (os[i] >= 'a')
122                        continue;
123                if ((os[i] >= 'A') && (os[i] <= 'Z'))
124                        continue;
125                if ((os[i] == '-') || (os[i] == '.'))
126                        continue;
127                if ((os[i] >= '0') && (os[i] <= '9'))
128                        continue;
129                break;
130        }
131        if (i < ossz) {
132                int nb = 1;
133                /* To get a better display, check if the invalid char is UTF-8 */
134                if ((os[i] & 0xE0) == 0xC0 /* 110xxxxx */) {
135                        if ((i < ossz - 1) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */))
136                                nb = 2;
137                        goto disp;
138                }
139                if ((os[i] & 0xF0) == 0xE0 /* 1110xxxx */) {
140                        if ((i < ossz - 2) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
141                                           && ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */))
142                                nb = 3;
143                        goto disp;
144                }
145                if ((os[i] & 0xF8) == 0xF0 /* 11110xxx */) {
146                        if ((i < ossz - 3) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
147                                           && ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */)
148                                           && ((os[i + 3] & 0xC0) == 0x80 /* 10xxxxxx */))
149                                nb = 4;
150                        goto disp;
151                }
152                if ((os[i] & 0xFC) == 0xF8 /* 111110xx */) {
153                        if ((i < ossz - 4) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
154                                           && ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */)
155                                           && ((os[i + 3] & 0xC0) == 0x80 /* 10xxxxxx */)
156                                           && ((os[i + 4] & 0xC0) == 0x80 /* 10xxxxxx */))
157                                nb = 5;
158                        goto disp;
159                }
160                if ((os[i] & 0xFE) == 0xFC /* 1111110x */) {
161                        if ((i < ossz - 5) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
162                                           && ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */)
163                                           && ((os[i + 3] & 0xC0) == 0x80 /* 10xxxxxx */)
164                                           && ((os[i + 4] & 0xC0) == 0x80 /* 10xxxxxx */)
165                                           && ((os[i + 5] & 0xC0) == 0x80 /* 10xxxxxx */))
166                                nb = 6;
167                        goto disp;
168                }
169                /* otherwise, we just display the hex code */
170                TRACE_DEBUG(INFO, "Invalid character (0xhhX) at offset %d in DiameterIdentity '%.*s'", os[i], i+1, ossz, os);
171                return 0;
172disp:
173                TRACE_DEBUG(INFO, "Invalid character '%.*s' at offset %d in DiameterIdentity '%.*s'", nb, os + i, i+1, ossz, os);
174                return 0;
175        }
176       
177#endif /* DIAMID_IDNA_IGNORE */
178       
179        return 1;
180}
181
182/* The following function validates a string as a Diameter Identity or applies the IDNA transformation on it
183 if *inoutsz is != 0 on entry, *id may not be \0-terminated.
184 memory has the following meaning: 0: *id can be realloc'd. 1: *id must be malloc'd on output (was static)
185*/
186int fd_os_validate_DiameterIdentity(char ** id, size_t * inoutsz, int memory)
187{
188        int gotsize = 0;
189       
190        TRACE_ENTRY("%p %p", id, inoutsz);
191        CHECK_PARAMS( id && *id && inoutsz );
192       
193        if (!*inoutsz)
194                *inoutsz = strlen(*id);
195        else
196                gotsize = 1;
197       
198#ifndef DIAMID_IDNA_IGNORE
199       
200        if (!fd_os_is_valid_DiameterIdentity((os0_t)*id, *inoutsz)) {
201       
202#ifdef DIAMID_IDNA_REJECT
203               
204                TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity!", *id);
205                TRACE_DEBUG(INFO, "Returning EINVAL since fD is compiled with option DIAMID_IDNA_REJECT.");
206                return EINVAL;
207       
208#else /* DIAMID_IDNA_REJECT */
209       
210                char *processed;
211                int ret;
212               
213                if (gotsize) { /* make it \0-terminated */
214                        if (memory) {
215                                CHECK_MALLOC( *id = os0dup(*id, *inoutsz) );
216                                memory = 0;
217                        } else {
218                                CHECK_MALLOC( *id = realloc(*id, *inoutsz + 1) );
219                                (*id)[*inoutsz] = '0';
220                        }
221                }
222               
223                ret = idna_to_ascii_8z ( *id, &processed, IDNA_USE_STD3_ASCII_RULES );
224                if (ret == IDNA_SUCCESS) {
225                        TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity, it was changed to '%s'", *id, processed);
226                        if (memory == 0)
227                                free(*id);
228                        *id = processed;
229                        *inoutsz = strlen(processed);
230                        /* Done! */
231                } else {
232                        TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity and cannot be sanitanized: %s", *id, idna_strerror (ret));
233                        return EINVAL;
234                }
235       
236#endif /* DIAMID_IDNA_REJECT */
237        } else
238#endif /* ! DIAMID_IDNA_IGNORE */
239        {
240                if (memory == 1) {
241                        CHECK_MALLOC( *id = os0dup(*id, *inoutsz) );
242                }
243        }
244        return 0;
245}
246
247/* Analyze a DiameterURI and return its components.
248  Return EINVAL if the URI is not valid.
249  *diamid is malloc'd on function return and must be freed (it is processed by fd_os_validate_DiameterIdentity).
250  *secure is 0 (no security) or 1 (security enabled) on return.
251  *port is 0 (default) or a value in host byte order on return.
252  *transport is 0 (default) or IPPROTO_* on return.
253  *proto is 0 (default) or 'd' (diameter), 'r' (radius), or 't' (tacacs+) on return.
254  */
255int fd_os_parse_DiameterURI(uint8_t * uri, size_t urisz, DiamId_t * diamid, size_t * diamidlen, int * secure, uint16_t * port, int * transport, char *proto)
256{
257        size_t offset = 0;
258        DiamId_t fqdn = NULL;
259        size_t   fqdnlen;
260        TRACE_ENTRY("%p %zd %p %p %p %p %p %p", uri, urisz, diamid, diamidlen, secure, port, transport, proto);
261        CHECK_PARAMS( uri && urisz );
262       
263        CHECK_PARAMS( urisz > 7 ); /* "aaa" + "://" + something else at least */
264       
265        /* Initialize values */
266        if (secure)
267                *secure = 0;
268        if (port)
269                *port = 0;
270        if (transport)
271                *transport = 0;
272        if (proto)
273                *proto = 0;
274       
275        /* Check the beginning */
276        if (memcmp( uri, "aaa", 3)) {
277                TRACE_DEBUG(INFO, "Invalid DiameterURI prefix: got '%.*s', expected 'aaa'", 3, uri);
278                return EINVAL;
279        }
280        offset += 3;
281       
282        /* Secure? */
283        if (uri[offset] == (uint8_t)'s') {
284                if (secure)
285                        *secure = 1;
286                offset += 1;
287        }
288       
289        /* Remaining of URI marker */
290        if (memcmp( uri + offset, "://", 3)) {
291                TRACE_DEBUG(INFO, "Invalid DiameterURI prefix: got '%.*s', expected 'aaa://' or 'aaas://'", offset + 3, uri);
292                return EINVAL;
293        }
294        offset += 3;
295       
296        /* This is the start of the FQDN */
297        fqdn = (DiamId_t)uri + offset;
298        for ( ; offset < urisz ; offset++ ) {
299                /* Stop only when we find ':' or ';' */
300                if ((uri[offset] == (uint8_t)':') || (uri[offset] == (uint8_t)';'))
301                        break;
302        }
303        fqdnlen = offset - (fqdn - (DiamId_t)uri);
304        CHECK_FCT(fd_os_validate_DiameterIdentity(&fqdn, &fqdnlen, 1));
305        if (diamid)
306                *diamid = fqdn;
307        else
308                free(fqdn);
309        if (diamidlen)
310                *diamidlen = fqdnlen;
311       
312        if (offset == urisz)
313                return 0; /* Finished */
314       
315        /* Is there a port ? */
316        if (uri[offset] == ':') {
317                uint16_t p = 0;
318                do {
319                        offset++;
320
321                        if (offset == urisz)
322                                break;
323
324                        uint32_t t = (uint32_t)((char)uri[offset] - '0');
325                        if (t > 9)
326                                break; /* we did not get a digit */
327
328                        t += p * 10; /* the port is specified in decimal base */
329                       
330                        if (t >= (1<<16)) {
331                                TRACE_DEBUG(INFO, "Invalid DiameterURI: port value is too big.");
332                                return EINVAL;
333                        }
334
335                        p = t;
336                } while (1);
337
338                if (port)
339                        *port = p;
340        }
341       
342        if (offset == urisz)
343                return 0; /* Finished */
344       
345        /* Is there a transport? */
346        if ( (urisz - offset > CONSTSTRLEN(";transport=")) 
347                && !strncasecmp((char *)uri + offset, ";transport=", CONSTSTRLEN(";transport=")) ) {
348       
349                offset += CONSTSTRLEN(";transport=");
350
351                if (urisz - offset < 3) {
352                        TRACE_DEBUG(INFO, "Invalid DiameterURI: transport string is too short, ignored.");
353                        return 0;
354                }               
355                if (!strncasecmp((char *)uri + offset, "tcp", CONSTSTRLEN("tcp"))) {
356                        if (transport)
357                                *transport = IPPROTO_TCP;
358                        offset += CONSTSTRLEN("tcp");
359                        goto after_transport;
360                }
361                if (!strncasecmp((char *)uri + offset, "udp", CONSTSTRLEN("udp"))) {
362                        if (transport)
363                                *transport = IPPROTO_UDP;
364                        offset += CONSTSTRLEN("udp");
365                        goto after_transport;
366                }
367                if ((urisz - offset > 3) && !strncasecmp((char *)uri + offset, "sctp", CONSTSTRLEN("sctp"))) {
368                        if (transport) {
369#ifndef DISABLE_SCTP
370                                *transport = IPPROTO_SCTP;
371#else /* DISABLE_SCTP */
372                                TRACE_DEBUG(INFO, "Received DiameterURI with 'transport=sctp' but DISABLE_SCTP was selected");
373                                *transport = 0;
374#endif /* DISABLE_SCTP */
375                        }
376                        offset += CONSTSTRLEN("sctp");
377                        goto after_transport;
378                }
379               
380                TRACE_DEBUG(INFO, "Invalid DiameterURI: transport string is not recognized ('%.*s').", urisz - offset, uri + offset);
381                return EINVAL;
382        }
383after_transport:
384        if (offset == urisz)
385                return 0; /* Finished */
386       
387        /* Is there a protocol? */
388        if ( ((urisz - offset) > CONSTSTRLEN(";protocol=")) 
389                && (!strncasecmp((char *)uri + offset, ";protocol=", CONSTSTRLEN(";protocol="))) ) {
390       
391                offset += CONSTSTRLEN(";protocol=");
392
393                if ( ((urisz - offset) >= CONSTSTRLEN("diameter")) 
394                    && (!strncasecmp((char *)uri + offset, "diameter", CONSTSTRLEN("diameter"))) ) {
395                        if (proto)
396                                *proto = 'd';
397                        offset += CONSTSTRLEN("diameter");
398                        goto after_proto;
399                }
400               
401                if ( ((urisz - offset) >= CONSTSTRLEN("radius")) 
402                    && (!strncasecmp((char *)uri + offset, "radius", CONSTSTRLEN("radius"))) ) {
403                        if (proto)
404                                *proto = 'r';
405                        offset += CONSTSTRLEN("radius");
406                        goto after_proto;
407                }
408               
409                if ( ((urisz - offset) >= CONSTSTRLEN("tacacs+")) 
410                    && (!strncasecmp((char *)uri + offset, "tacacs+", CONSTSTRLEN("tacacs+"))) ) {
411                        if (proto)
412                                *proto = 't';
413                        offset += CONSTSTRLEN("tacacs+");
414                        goto after_proto;
415                }
416               
417                TRACE_DEBUG(INFO, "Invalid DiameterURI: protocol string is not recognized ('%.*s').", urisz - offset, uri + offset);
418                return EINVAL;
419               
420        }
421after_proto:
422        if (offset == urisz)
423                return 0; /* Finished */
424       
425        TRACE_DEBUG(INFO, "Invalid DiameterURI: final part of string is not recognized ('%.*s').", urisz - offset, uri + offset);
426        return EINVAL;
427}
428
429
430/********************************************************************************************************/
431/* Hash function -- credits to Austin Appleby, thank you ^^ */
432/* See http://murmurhash.googlepages.com for more information on this function */
433
434/* the strings are NOT always aligned properly (ex: received in RADIUS message), so we use the aligned MurmurHash2 function as needed */
435#define _HASH_MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
436uint32_t fd_os_hash ( uint8_t * string, size_t len )
437{
438        uint32_t hash = len;
439        uint8_t * data = string;
440       
441        const unsigned int m = 0x5bd1e995;
442        const int r = 24;
443        int align = (long)string & 3;
444       
445        if (!align || (len < 4)) {
446                /* In case data is aligned, MurmurHash2 function */
447                while(len >= 4)
448                {
449                        /* Mix 4 bytes at a time into the hash */
450                        uint32_t k = *(uint32_t *)data; /* We don't care about the byte order */
451
452                        _HASH_MIX(hash, k, m);
453
454                        data += 4;
455                        len -= 4;
456                }
457
458                /* Handle the last few bytes of the input */
459                switch(len) {
460                        case 3: hash ^= data[2] << 16;
461                        case 2: hash ^= data[1] << 8;
462                        case 1: hash ^= data[0];
463                                hash *= m;
464                }
465               
466        } else {
467                /* Unaligned data, use alignment-safe slower version */
468               
469                /* Pre-load the temp registers */
470                uint32_t t = 0, d = 0;
471                switch(align)
472                {
473                        case 1: t |= data[2] << 16;
474                        case 2: t |= data[1] << 8;
475                        case 3: t |= data[0];
476                }
477                t <<= (8 * align);
478
479                data += 4-align;
480                len -= 4-align;
481               
482                /* From this point, "data" can be read by chunks of 4 bytes */
483               
484                int sl = 8 * (4-align);
485                int sr = 8 * align;
486
487                /* Mix */
488                while(len >= 4)
489                {
490                        uint32_t k;
491                       
492                        d = *(unsigned int *)data;
493                        k = (t >> sr) | (d << sl);
494
495                        _HASH_MIX(hash, k, m);
496
497                        t = d;
498
499                        data += 4;
500                        len -= 4;
501                }
502
503                /* Handle leftover data in temp registers */
504                d = 0;
505                if(len >= align)
506                {
507                        uint32_t k;
508                       
509                        switch(align)
510                        {
511                        case 3: d |= data[2] << 16;
512                        case 2: d |= data[1] << 8;
513                        case 1: d |= data[0];
514                        }
515
516                        k = (t >> sr) | (d << sl);
517                        _HASH_MIX(hash, k, m);
518
519                        data += align;
520                        len -= align;
521
522                        /* Handle tail bytes */
523
524                        switch(len)
525                        {
526                        case 3: hash ^= data[2] << 16;
527                        case 2: hash ^= data[1] << 8;
528                        case 1: hash ^= data[0];
529                                        hash *= m;
530                        };
531                }
532                else
533                {
534                        switch(len)
535                        {
536                        case 3: d |= data[2] << 16;
537                        case 2: d |= data[1] << 8;
538                        case 1: d |= data[0];
539                        case 0: hash ^= (t >> sr) | (d << sl);
540                                        hash *= m;
541                        }
542                }
543
544
545        }
546
547        /* Do a few final mixes of the hash to ensure the last few
548           bytes are well-incorporated. */
549        hash ^= hash >> 13;
550        hash *= m;
551        hash ^= hash >> 15;
552
553        return hash;
554} 
555
Note: See TracBrowser for help on using the repository browser.