forked from Mirrors/freeswitch
070546335d
* bnf: using <sofia-sip/su_string.h> git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@11801 d0543943-73ff-0310-b7d9-9358b9ac24b2
923 lines
21 KiB
C
923 lines
21 KiB
C
/*
|
|
* This file is part of the Sofia-SIP package
|
|
*
|
|
* Copyright (C) 2005,2006 Nokia Corporation.
|
|
*
|
|
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
/**@CFILE bnf.c
|
|
* @brief Character syntax table for HTTP-like protocols.
|
|
*
|
|
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
|
|
* @author Kai Vehmanen <Kai.Vehmanen@nokia.com>
|
|
*
|
|
* @date Created: Thu Jun 8 19:28:55 2000 ppessi
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "sofia-sip/bnf.h"
|
|
#include "sofia-sip/su_string.h"
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
|
|
#define ws bnf_ws
|
|
#define crlf bnf_crlf
|
|
#define alpha bnf_alpha
|
|
#define digit bnf_mark|bnf_token0|bnf_safe
|
|
#define sep bnf_separator
|
|
#define msep bnf_mark|bnf_separator
|
|
#define psep bnf_param0|bnf_separator
|
|
#define tok bnf_token0
|
|
#define mtok bnf_mark|bnf_token0
|
|
#define smtok bnf_mark|bnf_token0|bnf_safe
|
|
#define safe bnf_safe
|
|
|
|
/** Table for determining class of a character */
|
|
unsigned char const _bnf_table[256] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, ws, crlf, 0, 0, crlf, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
ws, mtok, sep, 0, safe, mtok, 0, mtok, /* !"#$%&' */
|
|
msep, msep, mtok, tok, sep, smtok, smtok, psep, /* ()*+,-./ */
|
|
digit, digit, digit, digit, digit, digit, digit, digit, /* 01234567 */
|
|
digit, digit, psep, sep, sep, sep, sep, sep, /* 89:;<=>? */
|
|
sep, alpha, alpha, alpha, alpha, alpha, alpha, alpha, /* @ABCDEFG */
|
|
alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, /* HIJKLMNO */
|
|
alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, /* PQRSTUVW */
|
|
alpha, alpha, alpha, psep, sep, psep, 0, smtok, /* XYZ[\]^_ */
|
|
tok, alpha, alpha, alpha, alpha, alpha, alpha, alpha, /* `abcdefg */
|
|
alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, /* hijklmno */
|
|
alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, /* pqrstuvw */
|
|
alpha, alpha, alpha, sep, 0, sep, mtok, 0, /* xyz{|}~ */
|
|
};
|
|
|
|
#if 0 /* This escaped lab */
|
|
|
|
#define BM(c, m00, m32, m64, m96) \
|
|
((c < 64) \
|
|
? ((c < 32) \
|
|
? (m00 & (1 << (31 - c))) \
|
|
: (m32 & (1 << (63 - c)))) \
|
|
: ((c < 96) \
|
|
? (m64 & (1 << (95 - c))) \
|
|
: (m96 & (1 << (127 - c)))))
|
|
|
|
/** Span of a token */
|
|
size_t bnf_span_token(char const *s)
|
|
{
|
|
char const *e = s;
|
|
unsigned const m32 = 0x4536FFC0U, m64 = 0x7FFFFFE1U, m96 = 0xFFFFFFE2U;
|
|
|
|
while (BM(*e, 0, m32, m64, m96))
|
|
e++;
|
|
|
|
return e - s;
|
|
}
|
|
|
|
/** Span of a token */
|
|
size_t bnf_span_token4(char const *s)
|
|
{
|
|
char const *e = s;
|
|
while (_bnf_table[(unsigned char)(*e)] & bnf_token)
|
|
e++;
|
|
return e - s;
|
|
}
|
|
|
|
char * bnf_span_token_end(char const *s)
|
|
{
|
|
return (char *)s;
|
|
}
|
|
#endif
|
|
|
|
/** Return length of decimal-octet */
|
|
su_inline int span_ip4_octet(char const *host)
|
|
{
|
|
/*
|
|
decimal-octet = DIGIT
|
|
/ DIGIT DIGIT
|
|
/ (("0"/"1") 2*(DIGIT))
|
|
/ ("2" ("0"/"1"/"2"/"3"/"4") DIGIT)
|
|
/ ("2" "5" ("0"/"1"/"2"/"3"/"4"/"5"))
|
|
*/
|
|
|
|
if (!IS_DIGIT(host[0]))
|
|
return 0;
|
|
|
|
/* DIGIT */
|
|
if (!IS_DIGIT(host[1]))
|
|
return 1;
|
|
|
|
if (host[0] == '2') {
|
|
/* ("2" "5" ("0"/"1"/"2"/"3"/"4"/"5")) */
|
|
if (host[1] == '5' && host[2] >= '0' && host[2] <= '5')
|
|
return 3;
|
|
|
|
/* ("2" ("0"/"1"/"2"/"3"/"4") DIGIT) */
|
|
if (host[1] >= '0' && host[1] <= '4' &&
|
|
host[2] >= '0' && host[2] <= '9')
|
|
return 3;
|
|
}
|
|
else if (host[0] == '0' || host[0] == '1') {
|
|
if (IS_DIGIT(host[2]))
|
|
/* ("1" 2*(DIGIT)) ... or "0" 2*(DIGIT) */
|
|
return 3;
|
|
}
|
|
|
|
/* POS-DIGIT DIGIT */
|
|
return 2;
|
|
}
|
|
|
|
/** Return length of valid IP4 address */
|
|
static
|
|
int span_canonic_ip4_address(char const *host, int *return_canonize)
|
|
{
|
|
int n, len, canonize = 0;
|
|
|
|
if (host == NULL)
|
|
return 0;
|
|
|
|
/* IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet */
|
|
len = span_ip4_octet(host);
|
|
if (len == 0 || host[len] != '.')
|
|
return 0;
|
|
if (len > 1 && host[0] == '0')
|
|
canonize = 1;
|
|
n = len + 1;
|
|
|
|
len = span_ip4_octet(host + n);
|
|
if (len == 0 || host[n + len] != '.')
|
|
return 0;
|
|
if (len > 1 && host[n] == '0')
|
|
canonize = 1;
|
|
n += len + 1;
|
|
|
|
len = span_ip4_octet(host + n);
|
|
if (len == 0 || host[n + len] != '.')
|
|
return 0;
|
|
if (len > 1 && host[n] == '0')
|
|
canonize = 1;
|
|
n += len + 1;
|
|
|
|
len = span_ip4_octet(host + n);
|
|
if (len == 0 || IS_DIGIT(host[n + len]) || host[n + len] == '.')
|
|
return 0;
|
|
if (len > 1 && host[n] == '0')
|
|
canonize = 1;
|
|
n += len;
|
|
|
|
if (canonize && return_canonize)
|
|
*return_canonize = 1;
|
|
|
|
return n;
|
|
}
|
|
|
|
/** Return length of valid IP4 address.
|
|
*
|
|
* Note that we accept here up to two leading zeroes
|
|
* which makes "dotted decimal" notation ambiguous:
|
|
* 127.000.000.001 is interpreted same as 127.0.0.1
|
|
*
|
|
* Note that traditionally IP address octets starting
|
|
* with zero have been interpreted as octal:
|
|
* 172.055.055.001 has been same as 172.45.45.1
|
|
*
|
|
* @b However, we interpret them as @b decimal,
|
|
* 172.055.055.001 is same as 172.55.55.1.
|
|
*/
|
|
int span_ip4_address(char const *host)
|
|
{
|
|
return span_canonic_ip4_address(host, NULL);
|
|
}
|
|
|
|
/** Scan and canonize a valid IP4 address. */
|
|
int scan_ip4_address(char **inout_host)
|
|
{
|
|
char *src = *inout_host, *dst = src;
|
|
issize_t n;
|
|
int canonize = 0;
|
|
|
|
if (src == NULL)
|
|
return -1;
|
|
|
|
n = span_canonic_ip4_address(src, &canonize);
|
|
if (n == 0)
|
|
return -1;
|
|
|
|
*inout_host += n;
|
|
|
|
if (!canonize)
|
|
return n;
|
|
|
|
for (;;) {
|
|
char c = *dst++ = *src++;
|
|
|
|
if (IS_DIGIT(*src)) {
|
|
if (canonize && c == '0')
|
|
dst--;
|
|
else if (c == '.')
|
|
canonize = 1;
|
|
else
|
|
canonize = 0;
|
|
}
|
|
else if (*src != '.') {
|
|
break;
|
|
}
|
|
}
|
|
*dst = '\0';
|
|
|
|
return n;
|
|
}
|
|
|
|
/** Return length of hex4 */
|
|
su_inline int span_hex4(char const *host)
|
|
{
|
|
if (!IS_HEX(host[0]))
|
|
return 0;
|
|
if (!IS_HEX(host[1]))
|
|
return 1;
|
|
if (!IS_HEX(host[2]))
|
|
return 2;
|
|
if (!IS_HEX(host[3]))
|
|
return 3;
|
|
return 4;
|
|
}
|
|
|
|
/** Return length of valid IP6 address */
|
|
su_inline
|
|
int span_canonic_ip6_address(char const *host,
|
|
int *return_canonize,
|
|
char *hexparts[9])
|
|
{
|
|
int n = 0, len, hex4, doublecolon = 0, canonize = 0;
|
|
|
|
/*
|
|
IPv6address = hexpart [ ":" IPv4address ]
|
|
hexpart = hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ]
|
|
hexseq = hex4 *( ":" hex4)
|
|
hex4 = 1*4HEXDIG
|
|
|
|
There is at most 8 hex4, 6 hex4 if IPv4address is included.
|
|
*/
|
|
|
|
if (host == NULL)
|
|
return 0;
|
|
|
|
for (hex4 = 0; hex4 < 8; ) {
|
|
len = span_hex4(host + n);
|
|
|
|
if (return_canonize) {
|
|
if ((len > 1 && host[n + 1] == '0') || host[n] == '0')
|
|
canonize = 1;
|
|
if (hexparts)
|
|
hexparts[hex4 + doublecolon] = (char *)(host + n);
|
|
}
|
|
|
|
if (host[n + len] == ':') {
|
|
if (len != 0) {
|
|
hex4++;
|
|
n += len + 1;
|
|
if (!doublecolon && host[n] == ':') {
|
|
if (return_canonize && hexparts) {
|
|
hexparts[hex4] = (char *)(host + n - 1);
|
|
}
|
|
doublecolon++, n++;
|
|
}
|
|
}
|
|
else if (n == 0 && host[1] == ':') {
|
|
doublecolon++, n = 2;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else if (host[n + len] == '.') {
|
|
len = span_canonic_ip4_address(host + n, return_canonize);
|
|
|
|
if (len == 0 || hex4 > 6 || !(doublecolon || hex4 == 6))
|
|
return 0;
|
|
|
|
if (canonize && return_canonize)
|
|
*return_canonize = 1;
|
|
|
|
return n + len;
|
|
}
|
|
else {
|
|
if (len != 0)
|
|
hex4++;
|
|
n += len;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hex4 != 8 && !doublecolon)
|
|
return 0;
|
|
|
|
if (IS_HEX(host[n]) || host[n] == ':')
|
|
return 0;
|
|
|
|
if (canonize && return_canonize)
|
|
*return_canonize = canonize;
|
|
|
|
return n;
|
|
}
|
|
|
|
/** Canonize scanned IP6 address.
|
|
*
|
|
* @retval Length of canonized IP6 address.
|
|
*/
|
|
su_inline
|
|
int canonize_ip6_address(char *host, char *hexparts[9])
|
|
{
|
|
char *dst, *hex, *ip4 = NULL;
|
|
int i, doublecolon, j, maxparts, maxspan, span, len;
|
|
|
|
char buf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
|
|
|
|
/*
|
|
Canonic representation has fewest chars
|
|
- except for mapped/compatible IP4 addresses, like
|
|
::15.21.117.42 or ::ffff:15.21.117.42 which have non-canonic forms of
|
|
::f15:752a or ::ffff:f15:752a
|
|
=> we just canonize hexparts and ip4part separately
|
|
and select optimal place for doublecolon
|
|
(with expection of ::1 and ::, which are canonized)
|
|
*/
|
|
for (i = 0, doublecolon = -1; i < 9; i++) {
|
|
hex = hexparts[i];
|
|
if (!hex)
|
|
break;
|
|
if (hex[0] == ':')
|
|
doublecolon = i;
|
|
while (hex[0] == '0' && IS_HEX(hex[1]))
|
|
hex++;
|
|
hexparts[i] = hex;
|
|
}
|
|
assert(i > 0);
|
|
|
|
if (hexparts[i - 1][span_hex4(hexparts[i - 1])] == '.')
|
|
ip4 = hexparts[--i];
|
|
|
|
maxparts = ip4 ? 6 : 8;
|
|
|
|
if (doublecolon >= 0) {
|
|
/* Order at most 8 (or 6) hexparts */
|
|
assert(i <= maxparts + 1);
|
|
|
|
if (i == maxparts + 1) {
|
|
/* There is an extra doublecolon */
|
|
for (j = doublecolon; j + 1 < i; j++)
|
|
hexparts[j] = hexparts[j + 1];
|
|
i--;
|
|
}
|
|
else {
|
|
for (j = maxparts; i > doublecolon + 1; )
|
|
hexparts[--j] = hexparts[--i];
|
|
for (;j > doublecolon;)
|
|
hexparts[--j] = "0:";
|
|
i = maxparts;
|
|
}
|
|
}
|
|
assert(i == maxparts);
|
|
|
|
/* Scan for optimal place for "::" */
|
|
for (i = 0, maxspan = 0, span = 0, doublecolon = 0; i < maxparts; i++) {
|
|
if (hexparts[i][0] == '0')
|
|
span++;
|
|
else if (span > maxspan)
|
|
doublecolon = i - span, maxspan = span, span = 0;
|
|
else
|
|
span = 0;
|
|
}
|
|
|
|
if (span > maxspan)
|
|
doublecolon = i - span, maxspan = span;
|
|
|
|
dst = buf;
|
|
|
|
for (i = 0; i < maxparts; i++) {
|
|
if (i == doublecolon)
|
|
hex = i == 0 ? "::" : ":", len = 1;
|
|
else if (i > doublecolon && i < doublecolon + maxspan)
|
|
continue;
|
|
else
|
|
hex = hexparts[i], len = span_hex4(hex);
|
|
if (hex[len] == ':')
|
|
len++;
|
|
memcpy(dst, hex, len);
|
|
dst += len;
|
|
}
|
|
|
|
if (ip4) {
|
|
hex = ip4;
|
|
len = scan_ip4_address(&hex); assert(len > 0);
|
|
|
|
/* Canonize :: and ::1 */
|
|
if (doublecolon == 0 && maxspan == 6) {
|
|
if (len == 7 && strncmp(ip4, "0.0.0.0", len) == 0)
|
|
ip4 = "", len = 0;
|
|
else if (len == 7 && strncmp(ip4, "0.0.0.1", len) == 0)
|
|
ip4 = "1", len = 1;
|
|
}
|
|
|
|
memcpy(dst, ip4, len);
|
|
dst += len;
|
|
}
|
|
|
|
len = dst - buf;
|
|
|
|
memcpy(host, buf, len);
|
|
|
|
return len;
|
|
}
|
|
|
|
/** Return length of valid IP6 address */
|
|
int span_ip6_address(char const *host)
|
|
{
|
|
return span_canonic_ip6_address(host, NULL, NULL);
|
|
}
|
|
|
|
/** Scan and canonize valid IP6 address.
|
|
*
|
|
* @param inout_host input pointer to string to scan
|
|
* output pointer to first character after valid IP6 address
|
|
*
|
|
* @retval Length of valid IP6 address or -1 upon an error.
|
|
*
|
|
* @note Scanned IP6 is not always NUL-terminated.
|
|
*/
|
|
int scan_ip6_address(char **inout_host)
|
|
{
|
|
int n, canonize = 0;
|
|
char *host = *inout_host;
|
|
char *hexparts[9] = { NULL };
|
|
|
|
n = span_canonic_ip6_address(host, &canonize, hexparts);
|
|
|
|
if (n == 0)
|
|
return -1;
|
|
|
|
*inout_host += n;
|
|
|
|
if (canonize) {
|
|
int len = canonize_ip6_address(host, hexparts);
|
|
assert(len <= n);
|
|
if (len < n)
|
|
host[len] = '\0';
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/** Return length of valid IP6 reference. */
|
|
int span_ip6_reference(char const *host)
|
|
{
|
|
/* IPv6reference = "[" IPv6address "]" */
|
|
|
|
if (host && host[0] == '[') {
|
|
int n = span_ip6_address(host + 1);
|
|
if (n > 0 && host[n + 1] == ']')
|
|
return n + 2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Scan valid IP6 reference. */
|
|
int scan_ip6_reference(char **inout_host)
|
|
{
|
|
int n, canonize = 0;
|
|
char *host = *inout_host;
|
|
char *hexparts[9] = { NULL };
|
|
|
|
/* IPv6reference = "[" IPv6address "]" */
|
|
|
|
if (host == NULL ||
|
|
host[0] != '[' ||
|
|
(n = span_canonic_ip6_address(host + 1, &canonize, hexparts)) == 0 ||
|
|
host[n + 1] != ']')
|
|
return -1;
|
|
|
|
*inout_host += n + 2;
|
|
|
|
if (canonize) {
|
|
int len = canonize_ip6_address(host + 1, hexparts);
|
|
|
|
assert(len <= n);
|
|
|
|
host[len + 1] = ']';
|
|
if (len + 2 < n + 2)
|
|
host[len + 2] = '\0';
|
|
}
|
|
|
|
return n + 2;
|
|
}
|
|
|
|
/** Return length of valid IP4 or IP6 address. */
|
|
int span_ip_address(char const *host)
|
|
{
|
|
if (!host || !host[0])
|
|
return 0;
|
|
|
|
/* IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet */
|
|
if (IS_DIGIT(host[0])) {
|
|
int n = span_ip4_address(host);
|
|
if (n)
|
|
return n;
|
|
}
|
|
|
|
if (host[0] == '[')
|
|
return span_ip6_reference(host);
|
|
else
|
|
return span_ip6_address(host);
|
|
}
|
|
|
|
/** Scan valid IP4/IP6 address. */
|
|
int scan_ip_address(char **inout_host)
|
|
{
|
|
char *host = *inout_host;
|
|
|
|
if (host == NULL)
|
|
return -1;
|
|
|
|
/* IPv6reference = "[" IPv6address "]" */
|
|
if (host[0] == '[')
|
|
return scan_ip6_reference(inout_host);
|
|
|
|
if (IS_DIGIT(host[0])) {
|
|
int n = scan_ip4_address(inout_host);
|
|
if (n > 0)
|
|
return n;
|
|
}
|
|
|
|
return scan_ip6_address(inout_host);
|
|
}
|
|
|
|
/** Return length of a valid domain label */
|
|
su_inline
|
|
size_t span_domain_label(char const *label)
|
|
{
|
|
/* domainlabel = alphanum / alphanum *( alphanum / "-" ) alphanum */
|
|
if (IS_ALPHANUM(*label)) {
|
|
size_t n;
|
|
for (n = 1; IS_ALPHANUM(label[n]) || label[n] == '-'; n++)
|
|
;
|
|
if (IS_ALPHANUM(label[n - 1]))
|
|
return n;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Scan valid domain name and count number of labels in it. */
|
|
su_inline
|
|
size_t span_domain_labels(char const *host, size_t *return_labels)
|
|
{
|
|
size_t len, n, labels;
|
|
int c;
|
|
|
|
if (!host || !host[0])
|
|
return 0;
|
|
|
|
for (n = 0, labels = 0; ; n += len) {
|
|
len = span_domain_label(host + n);
|
|
if (len == 0)
|
|
return 0;
|
|
|
|
labels++;
|
|
|
|
if (host[n + len] != '.')
|
|
break;
|
|
len++;
|
|
if (!IS_ALPHANUM(host[n + len]))
|
|
break;
|
|
}
|
|
|
|
/* Check that last label does not start with number */
|
|
if (!IS_ALPHA(host[n]))
|
|
return 0;
|
|
|
|
c = host[n + len];
|
|
if (IS_ALPHANUM(c) || c == '-' || c == '.')
|
|
return 0;
|
|
|
|
if (return_labels)
|
|
*return_labels = labels;
|
|
|
|
return n + len;
|
|
}
|
|
|
|
/** Return length of a valid domain name.
|
|
*
|
|
* @code
|
|
* hostname = *( domainlabel "." ) toplabel [ "." ]
|
|
* domainlabel = alphanum
|
|
* / alphanum *( alphanum / "-" ) alphanum
|
|
* toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum
|
|
* @endcode
|
|
*/
|
|
isize_t span_domain(char const *host)
|
|
{
|
|
return span_domain_labels(host, NULL);
|
|
}
|
|
|
|
/** Scan valid domain name. */
|
|
issize_t scan_domain(char **inout_host)
|
|
{
|
|
char *host;
|
|
size_t n, labels;
|
|
|
|
n = span_domain_labels(host = *inout_host, &labels);
|
|
if (n == 0)
|
|
return -1;
|
|
|
|
/* Remove extra dot at the end of hostname */
|
|
if (labels > 1 && host[n - 1] == '.')
|
|
host[n - 1] = '\0';
|
|
|
|
*inout_host += n;
|
|
|
|
return n;
|
|
}
|
|
|
|
/** Return length of a valid domain name or IP address. */
|
|
isize_t span_host(char const *host)
|
|
{
|
|
if (!host || !host[0])
|
|
return 0;
|
|
|
|
if (host[0] == '[')
|
|
return span_ip6_reference(host);
|
|
|
|
if (IS_DIGIT(host[0])) {
|
|
int n = span_ip4_address(host);
|
|
if (n)
|
|
return (isize_t)n;
|
|
}
|
|
|
|
return span_domain(host);
|
|
}
|
|
|
|
/** Scan valid domain name or IP address. */
|
|
issize_t scan_host(char **inout_host)
|
|
{
|
|
char *host = *inout_host;
|
|
|
|
if (host == NULL)
|
|
return -1;
|
|
|
|
/* IPv6reference = "[" IPv6address "]" */
|
|
if (host[0] == '[')
|
|
return scan_ip6_reference(inout_host);
|
|
|
|
if (IS_DIGIT(host[0])) {
|
|
int n = scan_ip4_address(inout_host);
|
|
if (n > 0)
|
|
return (issize_t)n;
|
|
}
|
|
|
|
return scan_domain(inout_host);
|
|
}
|
|
|
|
#include <sofia-sip/hostdomain.h>
|
|
|
|
/** Return true if @a string is valid IP4 address in dot-notation.
|
|
*
|
|
* @note Only 4-octet form is accepted, e.g., @c 127.1 is not considered
|
|
* valid IP4 address.
|
|
*/
|
|
int host_is_ip4_address(char const *string)
|
|
{
|
|
int n = span_ip4_address(string);
|
|
return n > 0 && string[n] == '\0';
|
|
}
|
|
|
|
/** Return true if @a string is valid IP6 address in hex notation.
|
|
*
|
|
* E.g., fe80::1 is a valid IP6 address.
|
|
*/
|
|
int host_is_ip6_address(char const *string)
|
|
{
|
|
int n = span_ip6_address(string);
|
|
return n > 0 && string[n] == '\0';
|
|
}
|
|
|
|
int host_ip6_reference(char const *string)
|
|
{
|
|
return host_is_ip6_reference(string);
|
|
}
|
|
|
|
/** Return true if @a string is valid IP6 reference,
|
|
* i.e. hex notation in square brackets.
|
|
*
|
|
* E.g., [::1] is a valid IP6 reference.
|
|
*/
|
|
int host_is_ip6_reference(char const *string)
|
|
{
|
|
int n = span_ip6_reference(string);
|
|
return n > 0 && string[n] == '\0';
|
|
}
|
|
|
|
/** Return true if @a string is valid IP address.
|
|
*
|
|
* Valid IP address is either a IP4 adddress in quad-octet notation,
|
|
* IP6 hex address or IP6 reference in square brackets ([]).
|
|
*/
|
|
int host_is_ip_address(char const *string)
|
|
{
|
|
int n = span_ip_address(string);
|
|
return n > 0 && string[n] == '\0';
|
|
}
|
|
|
|
/** Return true if @a string is valid a domain name.
|
|
*
|
|
* Valid domain name consists of alphanumeric labels separated with
|
|
* dot ("."). There can be a "-" in the middle of label.
|
|
* The last label must start with a letter.
|
|
*
|
|
* @code
|
|
* hostname = *( domainlabel "." ) toplabel [ "." ]
|
|
* domainlabel = alphanum
|
|
* / alphanum *( alphanum / "-" ) alphanum
|
|
* toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum
|
|
* @endcode
|
|
*/
|
|
int host_is_domain(char const *string)
|
|
{
|
|
size_t n = string ? span_domain(string) : 0;
|
|
return string && n > 0 && string[n] == '\0';
|
|
}
|
|
|
|
/** Return true if @a string is valid a host name.
|
|
*
|
|
* Check if the @a string is a domain name, IP address or IP6 reference.
|
|
*/
|
|
int host_is_valid(char const *string)
|
|
{
|
|
size_t n = span_host(string);
|
|
return n > 0 && string[n] == '\0';
|
|
}
|
|
|
|
/** Returns true if @a string is describing a local address.
|
|
*
|
|
* Uses the definitions of local addresses found in RFC1700 and
|
|
* RFC4291.
|
|
*/
|
|
int host_is_local(char const *host)
|
|
{
|
|
size_t n;
|
|
|
|
if (host_is_ip6_reference(host))
|
|
return (strcmp(host, "[::1]") == 0);
|
|
else if (host_is_ip6_address(host))
|
|
return (strcmp(host, "::1") == 0);
|
|
else if (host_is_ip4_address(host))
|
|
return (strncmp(host, "127.", 4) == 0);
|
|
|
|
n = span_domain(host);
|
|
|
|
return
|
|
n >= 9 /* strlen("localhost") */ &&
|
|
su_casenmatch(host, "localhost", 9) &&
|
|
(n == 9 ||
|
|
((n == 10 || /* localhost. */
|
|
n == 21 || /* strlen("localhost.localdomain") */
|
|
n == 22) && /* strlen("localhost.localdomain.") */
|
|
su_casenmatch(host + 9, ".localdomain.", n - 9)));
|
|
}
|
|
|
|
/** Return true if @a string has domain name in "invalid." domain.
|
|
*
|
|
*/
|
|
int host_has_domain_invalid(char const *string)
|
|
{
|
|
size_t n = span_domain(string);
|
|
|
|
if (n >= 7 && string[n] == '\0') {
|
|
static char const invalid[] = ".invalid";
|
|
if (string[n - 1] == '.') /* .invalid. perhaps? */
|
|
n--;
|
|
if (n == 7 /* strlen("invalid") */)
|
|
return su_casenmatch(string, invalid + 1, 7);
|
|
else
|
|
return su_casenmatch(string + n - 8, invalid, 8);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#include <sofia-sip/su.h>
|
|
|
|
static size_t convert_ip_address(char const *s,
|
|
uint8_t addr[16],
|
|
size_t *return_addrlen)
|
|
{
|
|
size_t len;
|
|
int canonize = 0;
|
|
char buf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
|
|
|
|
#if SU_HAVE_IN6
|
|
|
|
len = span_ip6_reference(s);
|
|
if (len) {
|
|
assert(len - 2 < sizeof buf); assert(len > 2);
|
|
|
|
if (s[len])
|
|
return 0;
|
|
|
|
len = len - 2;
|
|
s = memcpy(buf, s + 1, len), buf[len] = '\0';
|
|
}
|
|
else
|
|
len = span_ip6_address(s);
|
|
|
|
if (len) {
|
|
if (s[len] == '\0' && su_inet_pton(AF_INET6, s, addr) == 1) {
|
|
if (SU_IN6_IS_ADDR_V4MAPPED(addr) ||
|
|
SU_IN6_IS_ADDR_V4COMPAT(addr)) {
|
|
memcpy(addr, addr + 12, 4);
|
|
return (void)(*return_addrlen = 4), len;
|
|
}
|
|
return (void)(*return_addrlen = 16), len;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
len = span_canonic_ip4_address(s, &canonize);
|
|
|
|
if (len) {
|
|
if (canonize) {
|
|
char *tmp = buf;
|
|
s = memcpy(tmp, s, len + 1);
|
|
scan_ip4_address(&tmp);
|
|
}
|
|
if (s[len] == '\0' && su_inet_pton(AF_INET, s, addr) == 1)
|
|
return (void)(*return_addrlen = 4), len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Compare two host names or IP addresses
|
|
*
|
|
* Converts valid IP addresses to the binary format before comparing them.
|
|
* Note that IP6-mapped IP4 addresses and IP6-compatible IP4 addresses are
|
|
* compared as IP4 addresses; that is, ::ffff:127.0.0.1, ::127.0.0.1 and
|
|
* 127.0.0.1 all are all equal.
|
|
*
|
|
* @param a IP address or domain name
|
|
* @param b IP address or domain name
|
|
*
|
|
* @retval -1 if a < b
|
|
* @retval 0 if a == b
|
|
* @retval 1 if a > b
|
|
*
|
|
* @since New in @VERSION_1_12_4.
|
|
*/
|
|
int host_cmp(char const *a, char const *b)
|
|
{
|
|
uint8_t a6[16], b6[16];
|
|
size_t alen, blen, asize = 0, bsize = 0;
|
|
int retval;
|
|
|
|
if (a == NULL || b == NULL) {
|
|
retval = (a != NULL) - (b != NULL);
|
|
}
|
|
else {
|
|
alen = convert_ip_address(a, a6, &asize);
|
|
blen = convert_ip_address(b, b6, &bsize);
|
|
|
|
if (alen > 0 && blen > 0) {
|
|
if (asize < bsize)
|
|
retval = -1;
|
|
else if (asize > bsize)
|
|
retval = 1;
|
|
else
|
|
retval = memcmp(a6, b6, asize);
|
|
}
|
|
else {
|
|
retval = su_strcasecmp(a, b);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|