<p>George Joseph has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/7120">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">AST-2017-009: pjproject: Add validation of numeric header values<br><br>Parsing the numeric header fields like cseq, ttl, port, etc. all<br>had the potential to overflow, either causing unintended values to<br>be captured or, if the values were subsequently converted back to<br>strings, a buffer overrun. To address this, new "strto" functions<br>have been created that do range checking and those functions are<br>used wherever possible in the parser.<br><br> * Created pjlib/include/limits.h and pjlib/include/compat/limits.h<br> to either include the system limits.h or define common numeric<br> limits if there is no system limits.h.<br><br> * Created strto*_validate functions in sip_parser that take bounds<br> and on failure call the on_str_parse_error function which prints<br> an error message and calls PJ_THROW.<br><br> * Updated sip_parser to validate the numeric fields.<br><br> * Fixed an issue in sip_transport that prevented error messages<br> from being properly displayed.<br><br> * Added "volatile" to some variables referenced in PJ_CATCH blocks<br> as the optimizer was sometimes optimizing them away.<br><br> * Fixed length calculation in sip_transaction/create_tsx_key_2543<br> to account for signed ints being 11 characters, not 9.<br><br>ASTERISK-27319<br>Reported by: Youngsung Kim at LINE Corporation<br><br>Change-Id: I48de2e4ccf196990906304e8d7061f4ffdd772ff<br>---<br>A third-party/pjproject/patches/0090-sip_parser-Add-validity-checking-for-numeric-header-.patch<br>1 file changed, 973 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/20/7120/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/third-party/pjproject/patches/0090-sip_parser-Add-validity-checking-for-numeric-header-.patch b/third-party/pjproject/patches/0090-sip_parser-Add-validity-checking-for-numeric-header-.patch<br>new file mode 100644<br>index 0000000..dfee4b2<br>--- /dev/null<br>+++ b/third-party/pjproject/patches/0090-sip_parser-Add-validity-checking-for-numeric-header-.patch<br>@@ -0,0 +1,973 @@<br>+From b21042956dc4d3526052d5030953e5c565bb0895 Mon Sep 17 00:00:00 2001<br>+From: George Joseph <gjoseph@digium.com><br>+Date: Thu, 2 Nov 2017 08:23:00 -0600<br>+Subject: [PATCH] sip_parser: Add validity checking for numeric header values<br>+<br>+Parsing the numeric header fields like cseq, ttl, port, etc. all<br>+had the potential to overflow, either causing unintended values to<br>+be captured or, if the values were subsequently converted back to<br>+strings, a buffer overrun. To address this, new "strto" functions<br>+have been created that do range checking and those functions are<br>+used wherever possible in the parser.<br>+<br>+ * Created pjlib/include/limits.h and pjlib/include/compat/limits.h<br>+ to either include the system limits.h or define common numeric<br>+ limits if there is no system limits.h.<br>+<br>+ * Created strto*_validate functions in sip_parser that take bounds<br>+ and on failure call the on_str_parse_error function which prints<br>+ an error message and calls PJ_THROW.<br>+<br>+ * Updated sip_parser to validate the numeric fields.<br>+<br>+ * Fixed an issue in sip_transport that prevented error messages<br>+ from being properly displayed.<br>+<br>+ * Added "volatile" to some variables referenced in PJ_CATCH blocks<br>+ as the optimizer was sometimes optimizing them away.<br>+<br>+ * Fixed length calculation in sip_transaction/create_tsx_key_2543<br>+ to account for signed ints being 11 characters, not 9.<br>+<br>+Reported by: Youngsung Kim at LINE Corporation<br>+---<br>+ pjlib/build/pjlib.vcproj | 14 +++-<br>+ pjlib/build/pjlib.vcxproj | 4 +-<br>+ pjlib/build/pjlib.vcxproj.filters | 6 ++<br>+ pjlib/include/pj/compat/limits.h | 65 +++++++++++++++<br>+ pjlib/include/pj/compat/os_win32.h | 1 +<br>+ pjlib/include/pj/limits.h | 51 ++++++++++++<br>+ pjlib/include/pj/string.h | 46 +++++++++-<br>+ pjlib/include/pj/types.h | 3 -<br>+ pjlib/src/pj/string.c | 119 +++++++++++++++++++++++++-<br>+ pjlib/src/pj/timer.c | 1 +<br>+ pjsip/include/pjsip/sip_parser.h | 25 ++++++<br>+ pjsip/src/pjsip/sip_parser.c | 166 +++++++++++++++++++++++++++++--------<br>+ pjsip/src/pjsip/sip_transaction.c | 4 +-<br>+ pjsip/src/pjsip/sip_transport.c | 7 +-<br>+ 14 files changed, 463 insertions(+), 49 deletions(-)<br>+ create mode 100644 pjlib/include/pj/compat/limits.h<br>+ create mode 100644 pjlib/include/pj/limits.h<br>+<br>+diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj<br>+index 6a217a0b7..12592ef94 100644<br>+--- a/pjlib/build/pjlib.vcproj<br>++++ b/pjlib/build/pjlib.vcproj<br>+@@ -14967,7 +14967,11 @@<br>+ </File><br>+ <File<br>+ RelativePath="..\include\pj\ip_helper.h"<br>+- ><br>++ ><br>++ </File><br>++ <File<br>++ RelativePath="..\include\pj\limits.h"<br>++ ><br>+ </File><br>+ <File<br>+ RelativePath="..\include\pj\list.h"<br>+@@ -15070,8 +15074,12 @@<br>+ </File><br>+ <File<br>+ RelativePath="..\include\pj\compat\high_precision.h"<br>+- ><br>+- </File><br>++ ><br>++ </File><br>++ <File<br>++ RelativePath="..\include\pj\compat\limits.h"<br>++ ><br>++ </File><br>+ <File<br>+ RelativePath="..\include\pj\compat\m_alpha.h"<br>+ ><br>+diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj<br>+index abf09ec44..e41731e3c 100644<br>+--- a/pjlib/build/pjlib.vcxproj<br>++++ b/pjlib/build/pjlib.vcxproj<br>+@@ -494,7 +494,7 @@<br>+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild><br>+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild><br>+ </ClCompile><br>+- <ClCompile Include="..\src\pj\file_io_win32.c" /> <br>++ <ClCompile Include="..\src\pj\file_io_win32.c" /><br>+ <ClCompile Include="..\src\pj\guid.c" /><br>+ <ClCompile Include="..\src\pj\guid_simple.c"><br>+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|Win32'">true</ExcludedFromBuild><br>+@@ -890,6 +890,7 @@<br>+ <ClInclude Include="..\include\pj\compat\ctype.h" /><br>+ <ClInclude Include="..\include\pj\compat\errno.h" /><br>+ <ClInclude Include="..\include\pj\compat\high_precision.h" /><br>++ <ClInclude Include="..\include\pj\compat\limits.h" /><br>+ <ClInclude Include="..\include\pj\compat\malloc.h" /><br>+ <ClInclude Include="..\include\pj\compat\m_alpha.h" /><br>+ <ClInclude Include="..\include\pj\compat\m_i386.h" /><br>+@@ -925,6 +926,7 @@<br>+ <ClInclude Include="..\include\pj\hash.h" /><br>+ <ClInclude Include="..\include\pj\ioqueue.h" /><br>+ <ClInclude Include="..\include\pj\ip_helper.h" /><br>++ <ClInclude Include="..\include\pj\limits.h" /><br>+ <ClInclude Include="..\include\pj\list.h" /><br>+ <ClInclude Include="..\include\pj\list_i.h" /><br>+ <ClInclude Include="..\include\pj\lock.h" /><br>+diff --git a/pjlib/build/pjlib.vcxproj.filters b/pjlib/build/pjlib.vcxproj.filters<br>+index 0b5cbf109..6f343b019 100644<br>+--- a/pjlib/build/pjlib.vcxproj.filters<br>++++ b/pjlib/build/pjlib.vcxproj.filters<br>+@@ -439,5 +439,11 @@<br>+ <ClInclude Include="..\include\pj\compat\os_winuwp.h"><br>+ <Filter>Header Files\compat</Filter><br>+ </ClInclude><br>++ <ClInclude Include="..\include\pj\limits.h"><br>++ <Filter>Header Files</Filter><br>++ </ClInclude><br>++ <ClInclude Include="..\include\pj\compat\limits.h"><br>++ <Filter>Header Files\compat</Filter><br>++ </ClInclude><br>+ </ItemGroup><br>+ </Project><br>+\ No newline at end of file<br>+diff --git a/pjlib/include/pj/compat/limits.h b/pjlib/include/pj/compat/limits.h<br>+new file mode 100644<br>+index 000000000..fba0625df<br>+--- /dev/null<br>++++ b/pjlib/include/pj/compat/limits.h<br>+@@ -0,0 +1,65 @@<br>++/* $Id$ */<br>++/*<br>++ * Copyright (C) 2017 Teluu Inc. (http://www.teluu.com)<br>++ * Copyright (C) 2017 George Joseph <gjoseph@digium.com><br>++ *<br>++ * This program is free software; you can redistribute it and/or modify<br>++ * it under the terms of the GNU General Public License as published by<br>++ * the Free Software Foundation; either version 2 of the License, or<br>++ * (at your option) any later version.<br>++ *<br>++ * This program is distributed in the hope that it will be useful,<br>++ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>++ * GNU General Public License for more details.<br>++ *<br>++ * You should have received a copy of the GNU General Public License<br>++ * along with this program; if not, write to the Free Software<br>++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA <br>++ */<br>++#ifndef __PJ_COMPAT_LIMITS_H__<br>++#define __PJ_COMPAT_LIMITS_H__<br>++<br>++/**<br>++ * @file limits.h<br>++ * @brief Provides integer limits normally found in limits.h.<br>++ */<br>++<br>++#if defined(PJ_HAS_LIMITS_H) && PJ_HAS_LIMITS_H != 0<br>++# include <limits.h><br>++#else<br>++<br>++# ifdef _MSC_VER<br>++# pragma message("limits.h is not found or not supported. LONG_MIN and "\<br>++ "LONG_MAX will be defined by the library in "\<br>++ "pj/compats/limits.h and overridable in config_site.h")<br>++# else<br>++# warning "limits.h is not found or not supported. LONG_MIN and LONG_MAX " \<br>++ "will be defined by the library in pj/compats/limits.h and "\<br>++ "overridable in config_site.h"<br>++# endif<br>++<br>++/* Minimum and maximum values a `signed long int' can hold. */<br>++# ifndef LONG_MAX<br>++# if __WORDSIZE == 64<br>++# define LONG_MAX 9223372036854775807L<br>++# else<br>++# define LONG_MAX 2147483647L<br>++# endif<br>++# endif<br>++<br>++# ifndef LONG_MIN<br>++# define LONG_MIN (-LONG_MAX - 1L)<br>++# endif<br>++<br>++/* Maximum value an `unsigned long int' can hold. (Minimum is 0.) */<br>++# ifndef ULONG_MAX<br>++# if __WORDSIZE == 64<br>++# define ULONG_MAX 18446744073709551615UL<br>++# else <br>++# define ULONG_MAX 4294967295UL<br>++# endif<br>++# endif<br>++#endif<br>++<br>++#endif /* __PJ_COMPAT_LIMITS_H__ */<br>+diff --git a/pjlib/include/pj/compat/os_win32.h b/pjlib/include/pj/compat/os_win32.h<br>+index 4fa8b21ea..9b18e4eb1 100644<br>+--- a/pjlib/include/pj/compat/os_win32.h<br>++++ b/pjlib/include/pj/compat/os_win32.h<br>+@@ -57,6 +57,7 @@<br>+ #define PJ_HAS_SYS_TYPES_H 1<br>+ #define PJ_HAS_TIME_H 1<br>+ #define PJ_HAS_UNISTD_H 0<br>++#define PJ_HAS_LIMITS_H 1<br>+ <br>+ #define PJ_HAS_MSWSOCK_H 1<br>+ #define PJ_HAS_WINSOCK_H 0<br>+diff --git a/pjlib/include/pj/limits.h b/pjlib/include/pj/limits.h<br>+new file mode 100644<br>+index 000000000..8b00ae52a<br>+--- /dev/null<br>++++ b/pjlib/include/pj/limits.h<br>+@@ -0,0 +1,51 @@<br>++/* $Id$ */<br>++/*<br>++ * Copyright (C) 2017 Teluu Inc. (http://www.teluu.com)<br>++ * Copyright (C) 2017 George Joseph <gjoseph@digium.com><br>++ *<br>++ * This program is free software; you can redistribute it and/or modify<br>++ * it under the terms of the GNU General Public License as published by<br>++ * the Free Software Foundation; either version 2 of the License, or<br>++ * (at your option) any later version.<br>++ *<br>++ * This program is distributed in the hope that it will be useful,<br>++ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>++ * GNU General Public License for more details.<br>++ *<br>++ * You should have received a copy of the GNU General Public License<br>++ * along with this program; if not, write to the Free Software<br>++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA <br>++ */<br>++#ifndef __PJ_LIMITS_H__<br>++#define __PJ_LIMITS_H__<br>++<br>++/**<br>++ * @file limits.h<br>++ * @brief Common min and max values<br>++ */<br>++<br>++#include <pj/compat/limits.h><br>++<br>++/** Maximum value for signed 32-bit integer. */<br>++#define PJ_MAXINT32 0x7fffffff<br>++<br>++/** Minimum value for signed 32-bit integer. */<br>++#define PJ_MININT32 0x80000000<br>++<br>++/** Maximum value for unsigned 16-bit integer. */<br>++#define PJ_MAXUINT16 0xffff<br>++<br>++/** Maximum value for unsigned char. */<br>++#define PJ_MAXUINT8 0xff<br>++<br>++/** Maximum value for long. */<br>++#define PJ_MAXLONG LONG_MAX<br>++<br>++/** Minimum value for long. */<br>++#define PJ_MINLONG LONG_MIN<br>++<br>++/** Minimum value for unsigned long. */<br>++#define PJ_MAXULONG ULONG_MAX<br>++<br>++#endif /* __PJ_LIMITS_H__ */<br>+diff --git a/pjlib/include/pj/string.h b/pjlib/include/pj/string.h<br>+index 70a1d6c8c..5de236a65 100644<br>+--- a/pjlib/include/pj/string.h<br>++++ b/pjlib/include/pj/string.h<br>+@@ -28,7 +28,6 @@<br>+ #include <pj/types.h><br>+ #include <pj/compat/string.h><br>+ <br>+-<br>+ PJ_BEGIN_DECL<br>+ <br>+ /**<br>+@@ -636,6 +635,29 @@ PJ_DECL(char*) pj_create_random_string(char *str, pj_size_t length);<br>+ PJ_DECL(long) pj_strtol(const pj_str_t *str);<br>+ <br>+ /**<br>++ * Convert string to signed long integer. The conversion will stop as<br>++ * soon as non-digit character is found or all the characters have<br>++ * been processed.<br>++ *<br>++ * @param str the string.<br>++ * @param value Pointer to a long to receive the value.<br>++ *<br>++ * @return PJ_SUCCESS if successful. Otherwise:<br>++ * PJ_ETOOSMALL if the value was an impossibly long negative number.<br>++ * In this case *value will be set to LONG_MIN.<br>++ * \n<br>++ * PJ_ETOOBIG if the value was an impossibly long positive number.<br>++ * In this case, *value will be set to LONG_MAX.<br>++ * \n<br>++ * PJ_EINVAL if the input string was NULL, the value pointer was NULL <br>++ * or the input string could not be parsed at all such as starting with<br>++ * a character other than a '+', '-' or not in the '0' - '9' range.<br>++ * In this case, *value will be left untouched.<br>++ */<br>++PJ_DECL(pj_status_t) pj_strtol2(const pj_str_t *str, long *value);<br>++<br>++<br>++/**<br>+ * Convert string to unsigned integer. The conversion will stop as<br>+ * soon as non-digit character is found or all the characters have<br>+ * been processed.<br>+@@ -664,6 +686,27 @@ PJ_DECL(unsigned long) pj_strtoul2(const pj_str_t *str, pj_str_t *endptr,<br>+ unsigned base);<br>+ <br>+ /**<br>++ * Convert string to unsigned long integer. The conversion will stop as<br>++ * soon as non-digit character is found or all the characters have<br>++ * been processed.<br>++ *<br>++ * @param str The input string.<br>++ * @param value Pointer to an unsigned long to receive the value.<br>++ * @param base Number base to use.<br>++ *<br>++ * @return PJ_SUCCESS if successful. Otherwise:<br>++ * PJ_ETOOBIG if the value was an impossibly long positive number.<br>++ * In this case, *value will be set to ULONG_MAX.<br>++ * \n<br>++ * PJ_EINVAL if the input string was NULL, the value pointer was NULL <br>++ * or the input string could not be parsed at all such as starting <br>++ * with a character outside the base character range. In this case,<br>++ * *value will be left untouched.<br>++ */<br>++PJ_DECL(pj_status_t) pj_strtoul3(const pj_str_t *str, unsigned long *value,<br>++ unsigned base);<br>++<br>++/**<br>+ * Convert string to float.<br>+ *<br>+ * @param str the string.<br>+@@ -786,7 +829,6 @@ PJ_INLINE(void*) pj_memchr(const void *buf, int c, pj_size_t size)<br>+ return (void*)memchr((void*)buf, c, size);<br>+ }<br>+ <br>+-<br>+ /**<br>+ * @}<br>+ */<br>+diff --git a/pjlib/include/pj/types.h b/pjlib/include/pj/types.h<br>+index 0e0e2d9a7..8c9f78238 100644<br>+--- a/pjlib/include/pj/types.h<br>++++ b/pjlib/include/pj/types.h<br>+@@ -280,9 +280,6 @@ typedef int pj_exception_id_t;<br>+ /** Utility macro to compute the number of elements in static array. */<br>+ #define PJ_ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))<br>+ <br>+-/** Maximum value for signed 32-bit integer. */<br>+-#define PJ_MAXINT32 0x7FFFFFFFL<br>+-<br>+ /**<br>+ * Length of object names.<br>+ */<br>+diff --git a/pjlib/src/pj/string.c b/pjlib/src/pj/string.c<br>+index 307cfb47e..b95f141be 100644<br>+--- a/pjlib/src/pj/string.c<br>++++ b/pjlib/src/pj/string.c<br>+@@ -23,11 +23,14 @@<br>+ #include <pj/ctype.h><br>+ #include <pj/rand.h><br>+ #include <pj/os.h><br>++#include <pj/errno.h><br>++#include <pj/limits.h><br>+ <br>+ #if PJ_FUNCTIONS_ARE_INLINED==0<br>+ # include <pj/string_i.h><br>+ #endif<br>+ <br>++<br>+ PJ_DEF(pj_ssize_t) pj_strspn(const pj_str_t *str, const pj_str_t *set_char)<br>+ {<br>+ pj_ssize_t i, j, count = 0;<br>+@@ -230,6 +233,55 @@ PJ_DEF(long) pj_strtol(const pj_str_t *str)<br>+ return pj_strtoul(str);<br>+ }<br>+ <br>++<br>++PJ_DEF(pj_status_t) pj_strtol2(const pj_str_t *str, long *value)<br>++{<br>++ pj_str_t s;<br>++ unsigned long retval = 0;<br>++ pj_bool_t is_negative = PJ_FALSE;<br>++ int rc = 0;<br>++<br>++ PJ_CHECK_STACK();<br>++<br>++ if (!str || !value) {<br>++ return PJ_EINVAL;<br>++ }<br>++<br>++ s = *str;<br>++ pj_strltrim(&s);<br>++<br>++ if (s.slen == 0)<br>++ return PJ_EINVAL;<br>++<br>++ if (s.ptr[0] == '+' || s.ptr[0] == '-') {<br>++ is_negative = (s.ptr[0] == '-');<br>++ s.ptr += 1;<br>++ s.slen -= 1;<br>++ }<br>++<br>++ rc = pj_strtoul3(&s, &retval, 10);<br>++ if (rc == PJ_EINVAL) {<br>++ return rc;<br>++ } else if (rc != PJ_SUCCESS) {<br>++ *value = is_negative ? PJ_MINLONG : PJ_MAXLONG;<br>++ return is_negative ? PJ_ETOOSMALL : PJ_ETOOBIG;<br>++ }<br>++<br>++ if (retval > PJ_MAXLONG && !is_negative) {<br>++ *value = PJ_MAXLONG;<br>++ return PJ_ETOOBIG;<br>++ }<br>++<br>++ if (retval > (PJ_MAXLONG + 1UL) && is_negative) {<br>++ *value = PJ_MINLONG;<br>++ return PJ_ETOOSMALL;<br>++ }<br>++<br>++ *value = is_negative ? -(long)retval : retval;<br>++<br>++ return PJ_SUCCESS;<br>++}<br>++<br>+ PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str)<br>+ {<br>+ unsigned long value;<br>+@@ -282,6 +334,71 @@ PJ_DEF(unsigned long) pj_strtoul2(const pj_str_t *str, pj_str_t *endptr,<br>+ return value;<br>+ }<br>+ <br>++PJ_DEF(pj_status_t) pj_strtoul3(const pj_str_t *str, unsigned long *value,<br>++ unsigned base)<br>++{<br>++ pj_str_t s;<br>++ unsigned i;<br>++<br>++ PJ_CHECK_STACK();<br>++<br>++ if (!str || !value) {<br>++ return PJ_EINVAL;<br>++ }<br>++<br>++ s = *str;<br>++ pj_strltrim(&s);<br>++<br>++ if (s.slen == 0 || s.ptr[0] < '0' ||<br>++ (base <= 10 && (unsigned)s.ptr[0] > ('0' - 1) + base) ||<br>++ (base == 16 && !pj_isxdigit(s.ptr[0])))<br>++ {<br>++ return PJ_EINVAL;<br>++ }<br>++<br>++ *value = 0;<br>++ if (base <= 10) {<br>++ for (i=0; i<(unsigned)s.slen; ++i) {<br>++ unsigned c = s.ptr[i] - '0';<br>++ if (s.ptr[i] < '0' || (unsigned)s.ptr[i] > ('0' - 1) + base) {<br>++ break;<br>++ }<br>++ if (*value > PJ_MAXULONG / base) {<br>++ *value = PJ_MAXULONG;<br>++ return PJ_ETOOBIG;<br>++ }<br>++<br>++ *value *= base;<br>++ if ((PJ_MAXULONG - *value) < c) {<br>++ *value = PJ_MAXULONG;<br>++ return PJ_ETOOBIG;<br>++ }<br>++ *value += c;<br>++ }<br>++ } else if (base == 16) {<br>++ for (i=0; i<(unsigned)s.slen; ++i) {<br>++ unsigned c = pj_hex_digit_to_val(s.ptr[i]);<br>++ if (!pj_isxdigit(s.ptr[i]))<br>++ break;<br>++<br>++ if (*value > PJ_MAXULONG / base) {<br>++ *value = PJ_MAXULONG;<br>++ return PJ_ETOOBIG;<br>++ }<br>++ *value *= base;<br>++ if ((PJ_MAXULONG - *value) < c) {<br>++ *value = PJ_MAXULONG;<br>++ return PJ_ETOOBIG;<br>++ }<br>++ *value += c;<br>++ }<br>++ } else {<br>++ pj_assert(!"Unsupported base");<br>++ return PJ_EINVAL;<br>++ }<br>++ return PJ_SUCCESS;<br>++}<br>++<br>+ PJ_DEF(float) pj_strtof(const pj_str_t *str)<br>+ {<br>+ pj_str_t part;<br>+@@ -356,5 +473,3 @@ PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad)<br>+ <br>+ return len;<br>+ }<br>+-<br>+-<br>+diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c<br>+index 225be4498..399e114a8 100644<br>+--- a/pjlib/src/pj/timer.c<br>++++ b/pjlib/src/pj/timer.c<br>+@@ -36,6 +36,7 @@<br>+ #include <pj/lock.h><br>+ #include <pj/log.h><br>+ #include <pj/rand.h><br>++#include <pj/limits.h><br>+ <br>+ #define THIS_FILE "timer.c"<br>+ <br>+diff --git a/pjsip/include/pjsip/sip_parser.h b/pjsip/include/pjsip/sip_parser.h<br>+index 0d767f0ad..5691fed3a 100644<br>+--- a/pjsip/include/pjsip/sip_parser.h<br>++++ b/pjsip/include/pjsip/sip_parser.h<br>+@@ -39,6 +39,26 @@ PJ_BEGIN_DECL<br>+ */<br>+ <br>+ /**<br>++ * Contants for limit checks<br>++ */<br>++#define PJSIP_MIN_CONTENT_LENGTH 0<br>++#define PJSIP_MAX_CONTENT_LENGTH PJ_MAXINT32<br>++#define PJSIP_MIN_PORT 0<br>++#define PJSIP_MAX_PORT PJ_MAXUINT16<br>++#define PJSIP_MIN_TTL 0<br>++#define PJSIP_MAX_TTL PJ_MAXUINT8<br>++#define PJSIP_MIN_STATUS_CODE 100<br>++#define PJSIP_MAX_STATUS_CODE 999<br>++#define PJSIP_MIN_Q1000 0<br>++#define PJSIP_MAX_Q1000 PJ_MAXINT32 / 1000<br>++#define PJSIP_MIN_EXPIRES 0<br>++#define PJSIP_MAX_EXPIRES PJ_MAXINT32<br>++#define PJSIP_MIN_CSEQ 0<br>++#define PJSIP_MAX_CSEQ PJ_MAXINT32<br>++#define PJSIP_MIN_RETRY_AFTER 0<br>++#define PJSIP_MAX_RETRY_AFTER PJ_MAXINT32<br>++<br>++/**<br>+ * URI Parsing options.<br>+ */<br>+ enum<br>+@@ -64,6 +84,11 @@ enum<br>+ extern int PJSIP_SYN_ERR_EXCEPTION;<br>+ <br>+ /**<br>++ * Invalid value error exception value.<br>++ */<br>++extern int PJSIP_EINVAL_ERR_EXCEPTION;<br>++<br>++/**<br>+ * This structure is used to get error reporting from parser.<br>+ */<br>+ typedef struct pjsip_parser_err_report<br>+diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c<br>+index cf3b879f6..f9a0e65b5 100644<br>+--- a/pjsip/src/pjsip/sip_parser.c<br>++++ b/pjsip/src/pjsip/sip_parser.c<br>+@@ -34,6 +34,7 @@<br>+ #include <pj/string.h><br>+ #include <pj/ctype.h><br>+ #include <pj/assert.h><br>++#include <pj/limits.h><br>+ <br>+ #define THIS_FILE "sip_parser.c"<br>+ <br>+@@ -93,6 +94,7 @@ static unsigned uri_handler_count;<br>+ * Global vars (also extern).<br>+ */<br>+ int PJSIP_SYN_ERR_EXCEPTION = -1;<br>++int PJSIP_EINVAL_ERR_EXCEPTION = -2;<br>+ <br>+ /* Parser constants */<br>+ static pjsip_parser_const_t pconst =<br>+@@ -205,7 +207,6 @@ static unsigned long pj_strtoul_mindigit(const pj_str_t *str,<br>+ /* Case insensitive comparison */<br>+ #define parser_stricmp(s1, s2) (s1.slen!=s2.slen || pj_stricmp_alnum(&s1, &s2))<br>+ <br>+-<br>+ /* Get a token and unescape */<br>+ PJ_INLINE(void) parser_get_and_unescape(pj_scanner *scanner, pj_pool_t *pool,<br>+ const pj_cis_t *spec, <br>+@@ -223,8 +224,6 @@ PJ_INLINE(void) parser_get_and_unescape(pj_scanner *scanner, pj_pool_t *pool,<br>+ #endif<br>+ }<br>+ <br>+-<br>+-<br>+ /* Syntax error handler for parser. */<br>+ static void on_syntax_error(pj_scanner *scanner)<br>+ {<br>+@@ -232,6 +231,60 @@ static void on_syntax_error(pj_scanner *scanner)<br>+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);<br>+ }<br>+ <br>++/* Syntax error handler for parser. */<br>++static void on_str_parse_error(const pj_str_t *str, int rc)<br>++{<br>++ char *s;<br>++<br>++ switch(rc) {<br>++ case PJ_EINVAL:<br>++ s = "NULL input string, invalid input string, or NULL return "\<br>++ "value pointer";<br>++ break;<br>++ case PJ_ETOOSMALL:<br>++ s = "String value was less than the minimum allowed value.";<br>++ break;<br>++ case PJ_ETOOBIG:<br>++ s = "String value was greater than the maximum allowed value.";<br>++ break;<br>++ default:<br>++ s = "Unknown error";<br>++ }<br>++<br>++ if (str) {<br>++ PJ_LOG(1, (THIS_FILE, "Error parsing '%.*s': %s",<br>++ (int)str->slen, str->ptr, s));<br>++ } else {<br>++ PJ_LOG(1, (THIS_FILE, "Can't parse input string: %s", s));<br>++ }<br>++ PJ_THROW(PJSIP_EINVAL_ERR_EXCEPTION);<br>++}<br>++<br>++static void strtoi_validate(const pj_str_t *str, int min_val,<br>++ int max_val, int *value)<br>++{ <br>++ long retval;<br>++ pj_status_t status;<br>++<br>++ if (!str || !value) {<br>++ on_str_parse_error(str, PJ_EINVAL);<br>++ }<br>++ status = pj_strtol2(str, &retval);<br>++ if (status != PJ_EINVAL) {<br>++ if (min_val > retval) {<br>++ *value = min_val;<br>++ status = PJ_ETOOSMALL;<br>++ } else if (retval > max_val) {<br>++ *value = max_val;<br>++ status = PJ_ETOOBIG;<br>++ } else<br>++ *value = (int)retval;<br>++ }<br>++<br>++ if (status != PJ_SUCCESS)<br>++ on_str_parse_error(str, status);<br>++}<br>++<br>+ /* Get parser constants. */<br>+ PJ_DEF(const pjsip_parser_const_t*) pjsip_parser_const(void)<br>+ {<br>+@@ -285,6 +338,14 @@ static pj_status_t init_parser()<br>+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);<br>+ <br>+ /*<br>++ * Invalid value exception.<br>++ */<br>++ pj_assert (PJSIP_EINVAL_ERR_EXCEPTION == -2);<br>++ status = pj_exception_id_alloc("PJSIP invalid value error", <br>++ &PJSIP_EINVAL_ERR_EXCEPTION);<br>++ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);<br>++<br>++ /*<br>+ * Init character input spec (cis)<br>+ */<br>+ <br>+@@ -502,6 +563,9 @@ void deinit_sip_parser(void)<br>+ /* Deregister exception ID */<br>+ pj_exception_id_free(PJSIP_SYN_ERR_EXCEPTION);<br>+ PJSIP_SYN_ERR_EXCEPTION = -1;<br>++<br>++ pj_exception_id_free(PJSIP_EINVAL_ERR_EXCEPTION);<br>++ PJSIP_EINVAL_ERR_EXCEPTION = -2;<br>+ }<br>+ pj_leave_critical_section();<br>+ }<br>+@@ -766,7 +830,7 @@ PJ_DEF(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size,<br>+ }<br>+ <br>+ /* Determine if a message has been received. */<br>+-PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, <br>++PJ_DEF(pj_status_t) pjsip_find_msg( const char *buf, pj_size_t size, <br>+ pj_bool_t is_datagram, pj_size_t *msg_size)<br>+ {<br>+ #if PJ_HAS_TCP<br>+@@ -776,6 +840,7 @@ PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size,<br>+ const char *line;<br>+ int content_length = -1;<br>+ pj_str_t cur_msg;<br>++ pj_status_t status = PJ_SUCCESS;<br>+ const pj_str_t end_hdr = { "\n\r\n", 3};<br>+ <br>+ *msg_size = size;<br>+@@ -836,9 +901,16 @@ PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size,<br>+ pj_scan_get_newline(&scanner);<br>+ <br>+ /* Found a valid Content-Length header. */<br>+- content_length = pj_strtoul(&str_clen);<br>++ strtoi_validate(&str_clen, PJSIP_MIN_CONTENT_LENGTH,<br>++ PJSIP_MAX_CONTENT_LENGTH, &content_length);<br>+ }<br>+ PJ_CATCH_ANY {<br>++ int eid = PJ_GET_EXCEPTION();<br>++ if (eid == PJSIP_SYN_ERR_EXCEPTION) {<br>++ status = PJSIP_EMISSINGHDR;<br>++ } else if (eid == PJSIP_EINVAL_ERR_EXCEPTION) {<br>++ status = PJSIP_EINVALIDHDR;<br>++ }<br>+ content_length = -1;<br>+ }<br>+ PJ_END<br>+@@ -858,7 +930,7 @@ PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size,<br>+ <br>+ /* Found Content-Length? */<br>+ if (content_length == -1) {<br>+- return PJSIP_EMISSINGHDR;<br>++ return status;<br>+ }<br>+ <br>+ /* Enough packet received? */<br>+@@ -938,10 +1010,14 @@ static pj_bool_t is_next_sip_version(pj_scanner *scanner)<br>+ static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx,<br>+ pjsip_parser_err_report *err_list)<br>+ {<br>+- pj_bool_t parsing_headers;<br>+- pjsip_msg *msg = NULL;<br>++ /* These variables require "volatile" so their values get<br>++ * preserved when re-entering the PJ_TRY block after an error.<br>++ */<br>++ volatile pj_bool_t parsing_headers;<br>++ pjsip_msg *volatile msg = NULL;<br>++ pjsip_ctype_hdr *volatile ctype_hdr = NULL;<br>++<br>+ pj_str_t hname;<br>+- pjsip_ctype_hdr *ctype_hdr = NULL;<br>+ pj_scanner *scanner = ctx->scanner;<br>+ pj_pool_t *pool = ctx->pool;<br>+ PJ_USE_EXCEPTION;<br>+@@ -1023,7 +1099,6 @@ parse_headers:<br>+ hdr->name = hdr->sname = hname;<br>+ }<br>+ <br>+- <br>+ /* Single parse of header line can produce multiple headers.<br>+ * For example, if one Contact: header contains Contact list<br>+ * separated by comma, then these Contacts will be split into<br>+@@ -1267,7 +1342,7 @@ static void int_parse_uri_host_port( pj_scanner *scanner,<br>+ pj_str_t port;<br>+ pj_scan_get_char(scanner);<br>+ pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &port);<br>+- *p_port = pj_strtoul(&port);<br>++ strtoi_validate(&port, PJSIP_MIN_PORT, PJSIP_MAX_PORT, p_port);<br>+ } else {<br>+ *p_port = 0;<br>+ }<br>+@@ -1458,8 +1533,8 @@ static void* int_parse_sip_url( pj_scanner *scanner,<br>+ url->transport_param = pvalue;<br>+ <br>+ } else if (!parser_stricmp(pname, pconst.pjsip_TTL_STR) && pvalue.slen) {<br>+- url->ttl_param = pj_strtoul(&pvalue);<br>+-<br>++ strtoi_validate(&pvalue, PJSIP_MIN_TTL, PJSIP_MAX_TTL,<br>++ &url->ttl_param);<br>+ } else if (!parser_stricmp(pname, pconst.pjsip_MADDR_STR) && pvalue.slen) {<br>+ url->maddr_param = pvalue;<br>+ <br>+@@ -1595,7 +1670,8 @@ static void int_parse_status_line( pj_scanner *scanner,<br>+ <br>+ parse_sip_version(scanner);<br>+ pj_scan_get( scanner, &pconst.pjsip_DIGIT_SPEC, &token);<br>+- status_line->code = pj_strtoul(&token);<br>++ strtoi_validate(&token, PJSIP_MIN_STATUS_CODE, PJSIP_MAX_STATUS_CODE,<br>++ &status_line->code);<br>+ if (*scanner->curptr != '\r' && *scanner->curptr != '\n')<br>+ pj_scan_get( scanner, &pconst.pjsip_NOT_NEWLINE, &status_line->reason);<br>+ else<br>+@@ -1780,20 +1856,34 @@ static void int_parse_contact_param( pjsip_contact_hdr *hdr,<br>+ if (!parser_stricmp(pname, pconst.pjsip_Q_STR) && pvalue.slen) {<br>+ char *dot_pos = (char*) pj_memchr(pvalue.ptr, '.', pvalue.slen);<br>+ if (!dot_pos) {<br>+- hdr->q1000 = pj_strtoul(&pvalue) * 1000;<br>++ strtoi_validate(&pvalue, PJSIP_MIN_Q1000, PJSIP_MAX_Q1000,<br>++ &hdr->q1000);<br>++ hdr->q1000 *= 1000;<br>+ } else {<br>+ pj_str_t tmp = pvalue;<br>++ unsigned long qval_frac;<br>+ <br>+ tmp.slen = dot_pos - pvalue.ptr;<br>+- hdr->q1000 = pj_strtoul(&tmp) * 1000;<br>++ strtoi_validate(&tmp, PJSIP_MIN_Q1000, PJSIP_MAX_Q1000,<br>++ &hdr->q1000);<br>++ hdr->q1000 *= 1000;<br>+ <br>+ pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1);<br>+ pvalue.ptr = dot_pos + 1;<br>+- hdr->q1000 += pj_strtoul_mindigit(&pvalue, 3);<br>++ if (pvalue.slen > 3) {<br>++ pvalue.slen = 3;<br>++ }<br>++ qval_frac = pj_strtoul_mindigit(&pvalue, 3);<br>++ if ((unsigned)hdr->q1000 > (PJ_MAXINT32 - qval_frac)) {<br>++ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);<br>++ }<br>++ hdr->q1000 += qval_frac;<br>+ } <br>+- } else if (!parser_stricmp(pname, pconst.pjsip_EXPIRES_STR) && pvalue.slen) {<br>+- hdr->expires = pj_strtoul(&pvalue);<br>+-<br>++ } else if (!parser_stricmp(pname, pconst.pjsip_EXPIRES_STR) && <br>++ pvalue.slen) <br>++ {<br>++ strtoi_validate(&pvalue, PJSIP_MIN_EXPIRES, PJSIP_MAX_EXPIRES,<br>++ &hdr->expires);<br>+ } else {<br>+ pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param);<br>+ p->name = pname;<br>+@@ -1890,19 +1980,22 @@ static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx )<br>+ static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx )<br>+ {<br>+ pj_str_t cseq, method;<br>+- pjsip_cseq_hdr *hdr;<br>++ pjsip_cseq_hdr *hdr = NULL;<br>++ int cseq_val = 0;<br>+ <br>+- hdr = pjsip_cseq_hdr_create(ctx->pool);<br>+ pj_scan_get( ctx->scanner, &pconst.pjsip_DIGIT_SPEC, &cseq);<br>+- hdr->cseq = pj_strtoul(&cseq);<br>++ strtoi_validate(&cseq, PJSIP_MIN_CSEQ, PJSIP_MAX_CSEQ, &cseq_val);<br>+ <br>+- pj_scan_get( ctx->scanner, &pconst.pjsip_TOKEN_SPEC, &method);<br>+- pjsip_method_init_np(&hdr->method, &method);<br>++ hdr = pjsip_cseq_hdr_create(ctx->pool);<br>++ hdr->cseq = cseq_val;<br>+ <br>++ pj_scan_get( ctx->scanner, &pconst.pjsip_TOKEN_SPEC, &method);<br>+ parse_hdr_end( ctx->scanner );<br>+ <br>+- if (ctx->rdata)<br>++ pjsip_method_init_np(&hdr->method, &method);<br>++ if (ctx->rdata) {<br>+ ctx->rdata->msg_info.cseq = hdr;<br>++ }<br>+ <br>+ return (pjsip_hdr*)hdr;<br>+ }<br>+@@ -1984,7 +2077,8 @@ static pjsip_hdr* parse_hdr_retry_after(pjsip_parse_ctx *ctx)<br>+ hdr = pjsip_retry_after_hdr_create(ctx->pool, 0);<br>+ <br>+ pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &tmp);<br>+- hdr->ivalue = pj_strtoul(&tmp);<br>++ strtoi_validate(&tmp, PJSIP_MIN_RETRY_AFTER, PJSIP_MAX_RETRY_AFTER,<br>++ &hdr->ivalue);<br>+ <br>+ while (!pj_scan_is_eof(scanner) && *scanner->curptr!='\r' &&<br>+ *scanner->curptr!='\n')<br>+@@ -2073,7 +2167,8 @@ static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner,<br>+ hdr->branch_param = pvalue;<br>+ <br>+ } else if (!parser_stricmp(pname, pconst.pjsip_TTL_STR) && pvalue.slen) {<br>+- hdr->ttl_param = pj_strtoul(&pvalue);<br>++ strtoi_validate(&pvalue, PJSIP_MIN_TTL, PJSIP_MAX_TTL,<br>++ &hdr->ttl_param);<br>+ <br>+ } else if (!parser_stricmp(pname, pconst.pjsip_MADDR_STR) && pvalue.slen) {<br>+ hdr->maddr_param = pvalue;<br>+@@ -2082,9 +2177,10 @@ static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner,<br>+ hdr->recvd_param = pvalue;<br>+ <br>+ } else if (!parser_stricmp(pname, pconst.pjsip_RPORT_STR)) {<br>+- if (pvalue.slen)<br>+- hdr->rport_param = pj_strtoul(&pvalue);<br>+- else<br>++ if (pvalue.slen) {<br>++ strtoi_validate(&pvalue, PJSIP_MIN_PORT, PJSIP_MAX_PORT,<br>++ &hdr->rport_param);<br>++ } else<br>+ hdr->rport_param = 0;<br>+ } else {<br>+ pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param);<br>+@@ -2213,7 +2309,8 @@ static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx )<br>+ pj_str_t digit;<br>+ pj_scan_get_char(scanner);<br>+ pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &digit);<br>+- hdr->sent_by.port = pj_strtoul(&digit);<br>++ strtoi_validate(&digit, PJSIP_MIN_PORT, PJSIP_MAX_PORT,<br>++ &hdr->sent_by.port);<br>+ }<br>+ <br>+ int_parse_via_param(hdr, scanner, ctx->pool);<br>+@@ -2298,9 +2395,10 @@ PJ_DEF(pj_status_t) pjsip_parse_headers( pj_pool_t *pool, char *input,<br>+ unsigned options)<br>+ {<br>+ enum { STOP_ON_ERROR = 1 };<br>++ pj_str_t hname;<br>+ pj_scanner scanner;<br>+ pjsip_parse_ctx ctx;<br>+- pj_str_t hname;<br>++<br>+ PJ_USE_EXCEPTION;<br>+ <br>+ pj_scan_init(&scanner, input, size, PJ_SCAN_AUTOSKIP_WS_HEADER,<br>+@@ -2323,7 +2421,7 @@ retry_parse:<br>+ */<br>+ hname.slen = 0;<br>+ <br>+- /* Get hname. */<br>++ /* Get hname. */ <br>+ pj_scan_get( &scanner, &pconst.pjsip_TOKEN_SPEC, &hname);<br>+ if (pj_scan_get_char( &scanner ) != ':') {<br>+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);<br>+diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c<br>+index f3be93beb..7ac3d1b76 100644<br>+--- a/pjsip/src/pjsip/sip_transaction.c<br>++++ b/pjsip/src/pjsip/sip_transaction.c<br>+@@ -289,11 +289,11 @@ static pj_status_t create_tsx_key_2543( pj_pool_t *pool,<br>+ <br>+ /* Calculate length required. */<br>+ len_required = method->name.slen + /* Method */<br>+- 9 + /* CSeq number */<br>++ 11 + /* CSeq number */<br>+ rdata->msg_info.from->tag.slen + /* From tag. */<br>+ rdata->msg_info.cid->id.slen + /* Call-ID */<br>+ host->slen + /* Via host. */<br>+- 9 + /* Via port. */<br>++ 11 + /* Via port. */<br>+ 16; /* Separator+Allowance. */<br>+ key = p = (char*) pj_pool_alloc(pool, len_required);<br>+ <br>+diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c<br>+index 0c338e8fd..b24cbb411 100644<br>+--- a/pjsip/src/pjsip/sip_transport.c<br>++++ b/pjsip/src/pjsip/sip_transport.c<br>+@@ -1848,7 +1848,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,<br>+ /* Check for parsing syntax error */<br>+ if (msg==NULL || !pj_list_empty(&rdata->msg_info.parse_err)) {<br>+ pjsip_parser_err_report *err;<br>+- char buf[128];<br>++ char buf[256];<br>+ pj_str_t tmp;<br>+ <br>+ /* Gather syntax error information */<br>+@@ -1862,7 +1862,10 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,<br>+ pj_exception_id_name(err->except_code),<br>+ (int)err->hname.slen, err->hname.ptr,<br>+ err->line, err->col);<br>+- if (len > 0 && len < (int) (sizeof(buf)-tmp.slen)) {<br>++ if (len >= (int)sizeof(buf)-tmp.slen) {<br>++ len = (int)sizeof(buf)-tmp.slen;<br>++ }<br>++ if (len > 0) {<br>+ tmp.slen += len;<br>+ }<br>+ err = err->next;<br>+-- <br>+2.13.6<br>+<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/7120">change 7120</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/7120"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 14 </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I48de2e4ccf196990906304e8d7061f4ffdd772ff </div>
<div style="display:none"> Gerrit-Change-Number: 7120 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>