Navigation


source: freeDiameter/libfdproto/ostr.c @ 730:e21d79595045

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

minor improvements

File size: 15.1 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/* a little less sensitive to case, slower. */
72int fd_os_almostcasecmp_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz)
73{
74        int i;
75        ASSERT( os1 && os2);
76        if (os1sz < os2sz)
77                return -1;
78        if (os1sz > os2sz)
79                return 1;
80       
81        for (i = 0; i < os1sz; i++) {
82                if (os1[i] == os2[i])
83                        continue;
84               
85                if (asciitolower(os1[i]) == asciitolower(os2[i])) 
86                        continue;
87               
88                return os1[i] < os2[i] ? -1 : 1;
89        }
90       
91        return 0;
92}
93
94/* Check if the string contains only ASCII */
95int fd_os_is_valid_DiameterIdentity(uint8_t * os, size_t ossz)
96{
97#ifdef DIAMID_IDNA_IGNORE
98       
99        /* Allow anything */
100       
101#else /* DIAMID_IDNA_IGNORE */
102       
103        int i;
104       
105        /* Allow only letters, digits, hyphen, dot */
106        for (i=0; i < ossz; i++) {
107                if (os[i] > 'z')
108                        break;
109                if (os[i] >= 'a')
110                        continue;
111                if ((os[i] >= 'A') && (os[i] <= 'Z'))
112                        continue;
113                if ((os[i] == '-') || (os[i] == '.'))
114                        continue;
115                if ((os[i] >= '0') && (os[i] <= '9'))
116                        continue;
117                break;
118        }
119        if (i < ossz) {
120                int nb = 1;
121                /* To get a better display, check if the invalid char is UTF-8 */
122                if ((os[i] & 0xE0) == 0xC0 /* 110xxxxx */) {
123                        if ((i < ossz - 1) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */))
124                                nb = 2;
125                        goto disp;
126                }
127                if ((os[i] & 0xF0) == 0xE0 /* 1110xxxx */) {
128                        if ((i < ossz - 2) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
129                                           && ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */))
130                                nb = 3;
131                        goto disp;
132                }
133                if ((os[i] & 0xF8) == 0xF0 /* 11110xxx */) {
134                        if ((i < ossz - 3) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
135                                           && ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */)
136                                           && ((os[i + 3] & 0xC0) == 0x80 /* 10xxxxxx */))
137                                nb = 4;
138                        goto disp;
139                }
140                if ((os[i] & 0xFC) == 0xF8 /* 111110xx */) {
141                        if ((i < ossz - 4) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
142                                           && ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */)
143                                           && ((os[i + 3] & 0xC0) == 0x80 /* 10xxxxxx */)
144                                           && ((os[i + 4] & 0xC0) == 0x80 /* 10xxxxxx */))
145                                nb = 5;
146                        goto disp;
147                }
148                if ((os[i] & 0xFE) == 0xFC /* 1111110x */) {
149                        if ((i < ossz - 5) && ((os[i + 1] & 0xC0) == 0x80 /* 10xxxxxx */)
150                                           && ((os[i + 2] & 0xC0) == 0x80 /* 10xxxxxx */)
151                                           && ((os[i + 3] & 0xC0) == 0x80 /* 10xxxxxx */)
152                                           && ((os[i + 4] & 0xC0) == 0x80 /* 10xxxxxx */)
153                                           && ((os[i + 5] & 0xC0) == 0x80 /* 10xxxxxx */))
154                                nb = 6;
155                        goto disp;
156                }
157                /* otherwise, we just display the hex code */
158                TRACE_DEBUG(INFO, "Invalid character (0xhhX) at offset %d in DiameterIdentity '%.*s'", os[i], i+1, ossz, os);
159                return 0;
160disp:
161                TRACE_DEBUG(INFO, "Invalid character '%.*s' at offset %d in DiameterIdentity '%.*s'", nb, os + i, i+1, ossz, os);
162                return 0;
163        }
164       
165#endif /* DIAMID_IDNA_IGNORE */
166       
167        return 1;
168}
169
170/* The following function validates a string as a Diameter Identity or applies the IDNA transformation on it
171 if *inoutsz is != 0 on entry, *id may not be \0-terminated.
172 memory has the following meaning: 0: *id can be realloc'd. 1: *id must be malloc'd on output (was static)
173*/
174int fd_os_validate_DiameterIdentity(char ** id, size_t * inoutsz, int memory)
175{
176        int gotsize = 0;
177       
178        TRACE_ENTRY("%p %p", id, inoutsz);
179        CHECK_PARAMS( id && *id && inoutsz );
180       
181        if (!*inoutsz)
182                *inoutsz = strlen(*id);
183        else
184                gotsize = 1;
185       
186#ifndef DIAMID_IDNA_IGNORE
187       
188        if (!fd_os_is_valid_DiameterIdentity((os0_t)*id, *inoutsz)) {
189       
190#ifdef DIAMID_IDNA_REJECT
191               
192                TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity!", *id);
193                TRACE_DEBUG(INFO, "Returning EINVAL since fD is compiled with option DIAMID_IDNA_REJECT.");
194                return EINVAL;
195       
196#else /* DIAMID_IDNA_REJECT */
197       
198                char *processed;
199                int ret;
200               
201                if (gotsize) { /* make it \0-terminated */
202                        if (memory) {
203                                CHECK_MALLOC( *id = os0dup(*id, *inoutsz) );
204                                memory = 0;
205                        } else {
206                                CHECK_MALLOC( *id = realloc(*id, *inoutsz + 1) );
207                                (*id)[*inoutsz] = '0';
208                        }
209                }
210               
211                ret = idna_to_ascii_8z ( *id, &processed, IDNA_USE_STD3_ASCII_RULES );
212                if (ret == IDNA_SUCCESS) {
213                        TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity, it was changed to '%s'", *id, processed);
214                        if (memory == 0)
215                                free(*id);
216                        *id = processed;
217                        *inoutsz = strlen(processed);
218                        /* Done! */
219                } else {
220                        TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity and cannot be sanitanized: %s", *id, idna_strerror (ret));
221                        return EINVAL;
222                }
223       
224#endif /* DIAMID_IDNA_REJECT */
225        } else
226#endif /* ! DIAMID_IDNA_IGNORE */
227        {
228                if (memory == 1) {
229                        CHECK_MALLOC( *id = os0dup(*id, *inoutsz) );
230                }
231        }
232        return 0;
233}
234
235/* Analyze a DiameterURI and return its components.
236  Return EINVAL if the URI is not valid.
237  *diamid is malloc'd on function return and must be freed (it is processed by fd_os_validate_DiameterIdentity).
238  *secure is 0 (no security) or 1 (security enabled) on return.
239  *port is 0 (default) or a value in host byte order on return.
240  *transport is 0 (default) or IPPROTO_* on return.
241  *proto is 0 (default) or 'd' (diameter), 'r' (radius), or 't' (tacacs+) on return.
242  */
243int 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)
244{
245        size_t offset = 0;
246        DiamId_t fqdn = NULL;
247        size_t   fqdnlen;
248        TRACE_ENTRY("%p %zd %p %p %p %p %p %p", uri, urisz, diamid, diamidlen, secure, port, transport, proto);
249        CHECK_PARAMS( uri && urisz );
250       
251        CHECK_PARAMS( urisz > 7 ); /* "aaa" + "://" + something else at least */
252       
253        /* Initialize values */
254        if (secure)
255                *secure = 0;
256        if (port)
257                *port = 0;
258        if (transport)
259                *transport = 0;
260        if (proto)
261                *proto = 0;
262       
263        /* Check the beginning */
264        if (memcmp( uri, "aaa", 3)) {
265                TRACE_DEBUG(INFO, "Invalid DiameterURI prefix: got '%.*s', expected 'aaa'", 3, uri);
266                return EINVAL;
267        }
268        offset += 3;
269       
270        /* Secure? */
271        if (uri[offset] == (uint8_t)'s') {
272                if (secure)
273                        *secure = 1;
274                offset += 1;
275        }
276       
277        /* Remaining of URI marker */
278        if (memcmp( uri + offset, "://", 3)) {
279                TRACE_DEBUG(INFO, "Invalid DiameterURI prefix: got '%.*s', expected 'aaa://' or 'aaas://'", offset + 3, uri);
280                return EINVAL;
281        }
282        offset += 3;
283       
284        /* This is the start of the FQDN */
285        fqdn = (DiamId_t)uri + offset;
286        for ( ; offset < urisz ; offset++ ) {
287                /* Stop only when we find ':' or ';' */
288                if ((uri[offset] == (uint8_t)':') || (uri[offset] == (uint8_t)';'))
289                        break;
290        }
291        fqdnlen = offset - (fqdn - (DiamId_t)uri);
292        CHECK_FCT(fd_os_validate_DiameterIdentity(&fqdn, &fqdnlen, 1));
293        if (diamid)
294                *diamid = fqdn;
295        else
296                free(fqdn);
297        if (diamidlen)
298                *diamidlen = fqdnlen;
299       
300        if (offset == urisz)
301                return 0; /* Finished */
302       
303        /* Is there a port ? */
304        if (uri[offset] == ':') {
305                uint16_t p = 0;
306                do {
307                        offset++;
308
309                        if (offset == urisz)
310                                break;
311
312                        uint32_t t = (uint32_t)((char)uri[offset] - '0');
313                        if (t > 9)
314                                break; /* we did not get a digit */
315
316                        t += p * 10; /* the port is specified in decimal base */
317                       
318                        if (t >= (1<<16)) {
319                                TRACE_DEBUG(INFO, "Invalid DiameterURI: port value is too big.");
320                                return EINVAL;
321                        }
322
323                        p = t;
324                } while (1);
325
326                if (port)
327                        *port = p;
328        }
329       
330        if (offset == urisz)
331                return 0; /* Finished */
332       
333        /* Is there a transport? */
334        if ( (urisz - offset > CONSTSTRLEN(";transport=")) 
335                && !strncasecmp((char *)uri + offset, ";transport=", CONSTSTRLEN(";transport=")) ) {
336       
337                offset += CONSTSTRLEN(";transport=");
338
339                if (urisz - offset < 3) {
340                        TRACE_DEBUG(INFO, "Invalid DiameterURI: transport string is too short, ignored.");
341                        return 0;
342                }               
343                if (!strncasecmp((char *)uri + offset, "tcp", CONSTSTRLEN("tcp"))) {
344                        if (transport)
345                                *transport = IPPROTO_TCP;
346                        offset += CONSTSTRLEN("tcp");
347                        goto after_transport;
348                }
349                if (!strncasecmp((char *)uri + offset, "udp", CONSTSTRLEN("udp"))) {
350                        if (transport)
351                                *transport = IPPROTO_UDP;
352                        offset += CONSTSTRLEN("udp");
353                        goto after_transport;
354                }
355                if ((urisz - offset > 3) && !strncasecmp((char *)uri + offset, "sctp", CONSTSTRLEN("sctp"))) {
356                        if (transport) {
357#ifndef DISABLE_SCTP
358                                *transport = IPPROTO_SCTP;
359#else /* DISABLE_SCTP */
360                                TRACE_DEBUG(INFO, "Received DiameterURI with 'transport=sctp' but DISABLE_SCTP was selected");
361                                *transport = 0;
362#endif /* DISABLE_SCTP */
363                        }
364                        offset += CONSTSTRLEN("sctp");
365                        goto after_transport;
366                }
367               
368                TRACE_DEBUG(INFO, "Invalid DiameterURI: transport string is not recognized ('%.*s').", urisz - offset, uri + offset);
369                return EINVAL;
370        }
371after_transport:
372        if (offset == urisz)
373                return 0; /* Finished */
374       
375        /* Is there a protocol? */
376        if ( ((urisz - offset) > CONSTSTRLEN(";protocol=")) 
377                && (!strncasecmp((char *)uri + offset, ";protocol=", CONSTSTRLEN(";protocol="))) ) {
378       
379                offset += CONSTSTRLEN(";protocol=");
380
381                if ( ((urisz - offset) >= CONSTSTRLEN("diameter")) 
382                    && (!strncasecmp((char *)uri + offset, "diameter", CONSTSTRLEN("diameter"))) ) {
383                        if (proto)
384                                *proto = 'd';
385                        offset += CONSTSTRLEN("diameter");
386                        goto after_proto;
387                }
388               
389                if ( ((urisz - offset) >= CONSTSTRLEN("radius")) 
390                    && (!strncasecmp((char *)uri + offset, "radius", CONSTSTRLEN("radius"))) ) {
391                        if (proto)
392                                *proto = 'r';
393                        offset += CONSTSTRLEN("radius");
394                        goto after_proto;
395                }
396               
397                if ( ((urisz - offset) >= CONSTSTRLEN("tacacs+")) 
398                    && (!strncasecmp((char *)uri + offset, "tacacs+", CONSTSTRLEN("tacacs+"))) ) {
399                        if (proto)
400                                *proto = 't';
401                        offset += CONSTSTRLEN("tacacs+");
402                        goto after_proto;
403                }
404               
405                TRACE_DEBUG(INFO, "Invalid DiameterURI: protocol string is not recognized ('%.*s').", urisz - offset, uri + offset);
406                return EINVAL;
407               
408        }
409after_proto:
410        if (offset == urisz)
411                return 0; /* Finished */
412       
413        TRACE_DEBUG(INFO, "Invalid DiameterURI: final part of string is not recognized ('%.*s').", urisz - offset, uri + offset);
414        return EINVAL;
415}
416
417
418/********************************************************************************************************/
419/* Hash function -- credits to Austin Appleby, thank you ^^ */
420/* See http://murmurhash.googlepages.com for more information on this function */
421
422/* the strings are NOT always aligned properly (ex: received in RADIUS message), so we use the aligned MurmurHash2 function as needed */
423#define _HASH_MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
424uint32_t fd_os_hash ( uint8_t * string, size_t len )
425{
426        uint32_t hash = len;
427        uint8_t * data = string;
428       
429        const unsigned int m = 0x5bd1e995;
430        const int r = 24;
431        int align = (long)string & 3;
432       
433        if (!align || (len < 4)) {
434                /* In case data is aligned, MurmurHash2 function */
435                while(len >= 4)
436                {
437                        /* Mix 4 bytes at a time into the hash */
438                        uint32_t k = *(uint32_t *)data; /* We don't care about the byte order */
439
440                        _HASH_MIX(hash, k, m);
441
442                        data += 4;
443                        len -= 4;
444                }
445
446                /* Handle the last few bytes of the input */
447                switch(len) {
448                        case 3: hash ^= data[2] << 16;
449                        case 2: hash ^= data[1] << 8;
450                        case 1: hash ^= data[0];
451                                hash *= m;
452                }
453               
454        } else {
455                /* Unaligned data, use alignment-safe slower version */
456               
457                /* Pre-load the temp registers */
458                uint32_t t = 0, d = 0;
459                switch(align)
460                {
461                        case 1: t |= data[2] << 16;
462                        case 2: t |= data[1] << 8;
463                        case 3: t |= data[0];
464                }
465                t <<= (8 * align);
466
467                data += 4-align;
468                len -= 4-align;
469               
470                /* From this point, "data" can be read by chunks of 4 bytes */
471               
472                int sl = 8 * (4-align);
473                int sr = 8 * align;
474
475                /* Mix */
476                while(len >= 4)
477                {
478                        uint32_t k;
479                       
480                        d = *(unsigned int *)data;
481                        k = (t >> sr) | (d << sl);
482
483                        _HASH_MIX(hash, k, m);
484
485                        t = d;
486
487                        data += 4;
488                        len -= 4;
489                }
490
491                /* Handle leftover data in temp registers */
492                d = 0;
493                if(len >= align)
494                {
495                        uint32_t k;
496                       
497                        switch(align)
498                        {
499                        case 3: d |= data[2] << 16;
500                        case 2: d |= data[1] << 8;
501                        case 1: d |= data[0];
502                        }
503
504                        k = (t >> sr) | (d << sl);
505                        _HASH_MIX(hash, k, m);
506
507                        data += align;
508                        len -= align;
509
510                        /* Handle tail bytes */
511
512                        switch(len)
513                        {
514                        case 3: hash ^= data[2] << 16;
515                        case 2: hash ^= data[1] << 8;
516                        case 1: hash ^= data[0];
517                                        hash *= m;
518                        };
519                }
520                else
521                {
522                        switch(len)
523                        {
524                        case 3: d |= data[2] << 16;
525                        case 2: d |= data[1] << 8;
526                        case 1: d |= data[0];
527                        case 0: hash ^= (t >> sr) | (d << sl);
528                                        hash *= m;
529                        }
530                }
531
532
533        }
534
535        /* Do a few final mixes of the hash to ensure the last few
536           bytes are well-incorporated. */
537        hash ^= hash >> 13;
538        hash *= m;
539        hash ^= hash >> 15;
540
541        return hash;
542} 
543
Note: See TracBrowser for help on using the repository browser.