Skip to content
Snippets Groups Projects
Commit 045401d9 authored by Vittorio Giovara's avatar Vittorio Giovara Committed by Tristan Matthews
Browse files

contrib: add pjproject rules and patches

Change-Id: Ib89527686e2ca90d1d96e042604c59b031ed37a8
Refs: #46617
parent 22cc7b04
Branches
Tags
No related merge requests found
ead780282a1b3df8ca326992d30b83236c3fdb9dbafb134b33d3f59484ce7253ca8014f2d7d8b98968a696b75e8a29a545b3aa34a66acdf55a21942ca9b30370 pjproject-2.2.1.tar.bz2
From 5ddeabda001689893f43e60a96436904f0597457 Mon Sep 17 00:00:00 2001
From: Vittorio Giovara <vittorio.giovara@savoirfairelinux.com>
Date: Mon, 9 Jun 2014 18:17:42 -0400
Subject: [PATCH] update aconfigure
---
aconfigure | 207 +++++++++++++++++++++++++++++++------------------------------
1 file changed, 106 insertions(+), 101 deletions(-)
diff --git a/aconfigure b/aconfigure
index d4fc521..03f727f 100755
--- a/aconfigure
+++ b/aconfigure
@@ -1,11 +1,9 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.68 for pjproject 2.x.
+# Generated by GNU Autoconf 2.69 for pjproject 2.x.
#
#
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
-# Foundation, Inc.
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
#
#
# This configure script is free software; the Free Software Foundation
@@ -134,6 +132,31 @@ export LANGUAGE
# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
if test "x$CONFIG_SHELL" = x; then
as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
emulate sh
@@ -167,7 +190,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
else
exitcode=1; echo positional parameters were not saved.
fi
-test x\$exitcode = x0 || exit 1"
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
@@ -212,21 +236,25 @@ IFS=$as_save_IFS
if test "x$CONFIG_SHELL" != x; then :
- # We cannot yet assume a decent shell, so we have to provide a
- # neutralization value for shells without unset; and this also
- # works around shells that cannot unset nonexistent variables.
- # Preserve -v and -x to the replacement shell.
- BASH_ENV=/dev/null
- ENV=/dev/null
- (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
- export CONFIG_SHELL
- case $- in # ((((
- *v*x* | *x*v* ) as_opts=-vx ;;
- *v* ) as_opts=-v ;;
- *x* ) as_opts=-x ;;
- * ) as_opts= ;;
- esac
- exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
fi
if test x$as_have_required = xno; then :
@@ -328,6 +356,14 @@ $as_echo X"$as_dir" |
} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
@@ -449,6 +485,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
chmod +x "$as_me.lineno" ||
{ $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
# Don't try to exec as it changes $[0], causing all sort of problems
# (the dirname of $[0] is not the place where we might find the
# original and so on. Autoconf is especially sensitive to this).
@@ -483,16 +523,16 @@ if (echo >conf$$.file) 2>/dev/null; then
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
- # In both cases, we have to default to `cp -p'.
+ # In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
@@ -504,28 +544,8 @@ else
as_mkdir_p=false
fi
-if test -x / >/dev/null 2>&1; then
- as_test_x='test -x'
-else
- if ls -dL / >/dev/null 2>&1; then
- as_ls_L_option=L
- else
- as_ls_L_option=
- fi
- as_test_x='
- eval sh -c '\''
- if test -d "$1"; then
- test -d "$1/.";
- else
- case $1 in #(
- -*)set "./$1";;
- esac;
- case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
- ???[sx]*):;;*)false;;esac;fi
- '\'' sh
- '
-fi
-as_executable_p=$as_test_x
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
@@ -1252,8 +1272,6 @@ target=$target_alias
if test "x$host_alias" != x; then
if test "x$build_alias" = x; then
cross_compiling=maybe
- $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.
- If a cross compiler is detected then cross compile mode will be used" >&2
elif test "x$build_alias" != "x$host_alias"; then
cross_compiling=yes
fi
@@ -1567,9 +1585,9 @@ test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
pjproject configure 2.x
-generated by GNU Autoconf 2.68
+generated by GNU Autoconf 2.69
-Copyright (C) 2010 Free Software Foundation, Inc.
+Copyright (C) 2012 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
_ACEOF
@@ -1683,7 +1701,7 @@ $as_echo "$ac_try_echo"; } >&5
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
- $as_test_x conftest$ac_exeext
+ test -x conftest$ac_exeext
}; then :
ac_retval=0
else
@@ -1970,7 +1988,7 @@ This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by pjproject $as_me 2.x, which was
-generated by GNU Autoconf 2.68. Invocation command line was
+generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2495,7 +2513,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2535,7 +2553,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2588,7 +2606,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}cc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2629,7 +2647,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
ac_prog_rejected=yes
continue
@@ -2687,7 +2705,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2731,7 +2749,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3177,8 +3195,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdarg.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+struct stat;
/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
struct buf { int x; };
FILE * (*rcsopen) (struct buf *, struct stat *, int);
@@ -3291,7 +3308,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3335,7 +3352,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CXX="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3544,7 +3561,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3584,7 +3601,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_RANLIB="ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3638,7 +3655,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3682,7 +3699,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_AR="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -4464,7 +4481,7 @@ do
for ac_prog in grep ggrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+ as_fn_executable_p "$ac_path_GREP" || continue
# Check for GNU ac_path_GREP and select it if it is found.
# Check for GNU $ac_path_GREP
case `"$ac_path_GREP" --version 2>&1` in
@@ -4530,7 +4547,7 @@ do
for ac_prog in egrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+ as_fn_executable_p "$ac_path_EGREP" || continue
# Check for GNU ac_path_EGREP and select it if it is found.
# Check for GNU $ac_path_EGREP
case `"$ac_path_EGREP" --version 2>&1` in
@@ -6489,7 +6506,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_SDL_CONFIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6535,7 +6552,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_SDL_CONFIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6640,7 +6657,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_PKG_CONFIG="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -8449,16 +8466,16 @@ if (echo >conf$$.file) 2>/dev/null; then
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
- # In both cases, we have to default to `cp -p'.
+ # In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
@@ -8518,28 +8535,16 @@ else
as_mkdir_p=false
fi
-if test -x / >/dev/null 2>&1; then
- as_test_x='test -x'
-else
- if ls -dL / >/dev/null 2>&1; then
- as_ls_L_option=L
- else
- as_ls_L_option=
- fi
- as_test_x='
- eval sh -c '\''
- if test -d "$1"; then
- test -d "$1/.";
- else
- case $1 in #(
- -*)set "./$1";;
- esac;
- case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
- ???[sx]*):;;*)false;;esac;fi
- '\'' sh
- '
-fi
-as_executable_p=$as_test_x
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
@@ -8561,7 +8566,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# values after options handling.
ac_log="
This file was extended by pjproject $as_me 2.x, which was
-generated by GNU Autoconf 2.68. Invocation command line was
+generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
@@ -8623,10 +8628,10 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
pjproject config.status 2.x
-configured by $0, generated by GNU Autoconf 2.68,
+configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
-Copyright (C) 2010 Free Software Foundation, Inc.
+Copyright (C) 2012 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."
@@ -8714,7 +8719,7 @@ fi
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
if \$ac_cs_recheck; then
- set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
shift
\$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
CONFIG_SHELL='$SHELL'
--
1.8.3.2
diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
index 10f86fd..4ace1bc 100644
--- a/pjlib/include/pj/config.h
+++ b/pjlib/include/pj/config.h
@@ -245,7 +245,13 @@
# define PJ_M_NAME "armv4"
# define PJ_HAS_PENTIUM 0
# if !PJ_IS_LITTLE_ENDIAN && !PJ_IS_BIG_ENDIAN
-# error Endianness must be declared for this processor
+# if defined(__GNUC__)
+# include <endian.h>
+# define PJ_IS_LITTLE_ENDIAN __BYTE_ORDER__ == __LITTLE_ENDIAN__
+# define PJ_IS_BIG_ENDIAN __BYTE_ORDER__ == __BIG_ENDIAN__
+# else
+# error Endianness must be declared for this processor
+# endif
# endif
#elif defined (PJ_M_POWERPC) || defined(__powerpc) || defined(__powerpc__) || \
From 73ffecc7f0224e64b0694ddc740f7795757a9fcf Mon Sep 17 00:00:00 2001
From: Vittorio Giovara <vittorio.giovara@savoirfairelinux.com>
Date: Mon, 9 Jun 2014 14:20:55 -0400
Subject: [PATCH] ssl_sock: add gnutls backend
This backend is mutually exclusive with the OpenSSL one, but completely
compatible, and conformant to the PJSIP API. Also avoids any license issues
when linking statically.
The configure script is updated to select either OpenSSL or GnuTLS
with --enable-ssl[='...'] and a new symbol (PJ_HAS_TLS_SOCK) is introduced
to identify which backend is in use.
Written by Vittorio Giovara <vittorio.giovara@savoirfairelinux.com> and
Philippe Proulx <philippe.proulx@savoirfairelinux.com> on behalf of
Savoir-Faire Linux.
---
aconfigure | 183 ++-
aconfigure.ac | 100 +-
pjlib/build/Makefile | 2 +-
pjlib/include/pj/compat/os_auto.h.in | 3 +
pjlib/include/pj/config.h | 4 +-
pjlib/src/pj/ssl_sock_common.c | 5 +
pjlib/src/pj/ssl_sock_gtls.c | 2781 ++++++++++++++++++++++++++++++++++
pjlib/src/pj/ssl_sock_ossl.c | 6 +-
8 files changed, 3022 insertions(+), 62 deletions(-)
create mode 100644 pjlib/src/pj/ssl_sock_gtls.c
diff --git a/aconfigure b/aconfigure
index 8fd8c64..d4fc521 100755
--- a/aconfigure
+++ b/aconfigure
@@ -617,6 +617,8 @@ ac_no_opencore_amrnb
libcrypto_present
libssl_present
openssl_h_present
+libgnutls_present
+gnutls_h_present
ac_no_ssl
ac_v4l2_ldflags
ac_v4l2_cflags
@@ -1439,8 +1441,8 @@ Optional Features:
package and samples location using IPPROOT and
IPPSAMPLES env var or with --with-ipp and
--with-ipp-samples options
- --disable-ssl Exclude SSL support the build (default: autodetect)
-
+ --enable-ssl=backend Select 'gnutls' or 'openssl' (default) to provide
+ SSL support (autodetect)
--disable-opencore-amr Exclude OpenCORE AMR support from the build
(default: autodetect)
@@ -7363,33 +7365,159 @@ fi
# Check whether --enable-ssl was given.
if test "${enable_ssl+set}" = set; then :
- enableval=$enable_ssl;
- if test "$enable_ssl" = "no"; then
- ac_no_ssl=1
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if SSL support is disabled... yes" >&5
+ enableval=$enable_ssl; if test "x$enableval" = "xgnutls"; then
+ ssl_backend="gnutls"
+ else
+ ssl_backend="openssl"
+ fi
+
+fi
+
+
+if test "x$enable_ssl" = "xno"; then
+ ac_no_ssl=1
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if SSL support is disabled... yes" >&5
$as_echo "Checking if SSL support is disabled... yes" >&6; }
- fi
+else
+ if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then
+ CFLAGS="$CFLAGS -I$with_ssl/include"
+ LDFLAGS="$LDFLAGS -L$with_ssl/lib"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using SSL prefix... $with_ssl" >&5
+$as_echo "Using SSL prefix... $with_ssl" >&6; }
+ fi
+ if test "x$ssl_backend" = "xgnutls"; then
+ for ac_prog in pkg-config "python pkgconfig.py"
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_PKG_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$PKG_CONFIG"; then
+ ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_PKG_CONFIG="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+PKG_CONFIG=$ac_cv_prog_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ test -n "$PKG_CONFIG" && break
+done
+test -n "$PKG_CONFIG" || PKG_CONFIG="none"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for GnuTLS installations.." >&5
+$as_echo "checking for GnuTLS installations.." >&6; }
+
+
+ ac_fn_c_check_header_mongrel "$LINENO" "gnutls/gnutls.h" "ac_cv_header_gnutls_gnutls_h" "$ac_includes_default"
+if test "x$ac_cv_header_gnutls_gnutls_h" = xyes; then :
+ gnutls_h_present=1
+fi
+
+
+
+ if test "$PKG_CONFIG" != "none"; then
+ if $PKG_CONFIG --exists gnutls; then
+ LIBS="$LIBS `$PKG_CONFIG --libs gnutls`"
+ libgnutls_present=1
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gnutls_certificate_set_x509_system_trust in -lgnutls" >&5
+$as_echo_n "checking for gnutls_certificate_set_x509_system_trust in -lgnutls... " >&6; }
+if ${ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgnutls $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gnutls_certificate_set_x509_system_trust ();
+int
+main ()
+{
+return gnutls_certificate_set_x509_system_trust ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust=yes
else
+ ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" >&5
+$as_echo "$ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" >&6; }
+if test "x$ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" = xyes; then :
+ libgnutls_present=1 &&
+ LIBS="$LIBS -lgnutls"
+fi
+
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: *** Warning: neither pkg-config nor python is available, disabling gnutls. ***" >&5
+$as_echo "*** Warning: neither pkg-config nor python is available, disabling gnutls. ***" >&6; }
+ fi
+
+ if test "x$gnutls_h_present" = "x1" -a "x$libgnutls_present" = "x1"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: GnuTLS library found, SSL support enabled" >&5
+$as_echo "GnuTLS library found, SSL support enabled" >&6; }
+ # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
+ #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
+ $as_echo "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for OpenSSL installations.." >&5
+ $as_echo "#define PJ_HAS_TLS_SOCK 1" >>confdefs.h
+
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** No GnuTLS libraries found, disabling SSL support **" >&5
+$as_echo "** No GnuTLS libraries found, disabling SSL support **" >&6; }
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for OpenSSL installations.." >&5
$as_echo "checking for OpenSSL installations.." >&6; }
- if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then
- CFLAGS="$CFLAGS -I$with_ssl/include"
- LDFLAGS="$LDFLAGS -L$with_ssl/lib"
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using SSL prefix... $with_ssl" >&5
-$as_echo "Using SSL prefix... $with_ssl" >&6; }
- fi
- ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default"
+ ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default"
if test "x$ac_cv_header_openssl_ssl_h" = xyes; then :
openssl_h_present=1
fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ERR_load_BIO_strings in -lcrypto" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ERR_load_BIO_strings in -lcrypto" >&5
$as_echo_n "checking for ERR_load_BIO_strings in -lcrypto... " >&6; }
if ${ac_cv_lib_crypto_ERR_load_BIO_strings+:} false; then :
$as_echo_n "(cached) " >&6
@@ -7429,7 +7557,7 @@ if test "x$ac_cv_lib_crypto_ERR_load_BIO_strings" = xyes; then :
libcrypto_present=1 && LIBS="$LIBS -lcrypto"
fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_library_init in -lssl" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_library_init in -lssl" >&5
$as_echo_n "checking for SSL_library_init in -lssl... " >&6; }
if ${ac_cv_lib_ssl_SSL_library_init+:} false; then :
$as_echo_n "(cached) " >&6
@@ -7469,22 +7597,23 @@ if test "x$ac_cv_lib_ssl_SSL_library_init" = xyes; then :
libssl_present=1 && LIBS="$LIBS -lssl"
fi
- if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: OpenSSL library found, SSL support enabled" >&5
+ if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: OpenSSL library found, SSL support enabled" >&5
$as_echo "OpenSSL library found, SSL support enabled" >&6; }
- # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
- #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
- $as_echo "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h
+ # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
+ #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
+ $as_echo "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h
- else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** OpenSSL libraries not found, disabling SSL support **" >&5
-$as_echo "** OpenSSL libraries not found, disabling SSL support **" >&6; }
- fi
+ $as_echo "#define PJ_HAS_TLS_SOCK 0" >>confdefs.h
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** No OpenSSL libraries found, disabling SSL support **" >&5
+$as_echo "** No OpenSSL libraries found, disabling SSL support **" >&6; }
+ fi
+ fi
fi
-
# Check whether --with-opencore-amrnb was given.
if test "${with_opencore_amrnb+set}" = set; then :
withval=$with_opencore_amrnb; as_fn_error $? "This option is obsolete and replaced by --with-opencore-amr=DIR" "$LINENO" 5
diff --git a/aconfigure.ac b/aconfigure.ac
index cd71a7a..465285e 100644
--- a/aconfigure.ac
+++ b/aconfigure.ac
@@ -1346,38 +1346,76 @@ fi
dnl # Include SSL support
AC_SUBST(ac_no_ssl)
-AC_ARG_ENABLE(ssl,
- AC_HELP_STRING([--disable-ssl],
- [Exclude SSL support the build (default: autodetect)])
- ,
- [
- if test "$enable_ssl" = "no"; then
- [ac_no_ssl=1]
- AC_MSG_RESULT([Checking if SSL support is disabled... yes])
- fi
- ],
- [
- AC_MSG_RESULT([checking for OpenSSL installations..])
- if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then
- CFLAGS="$CFLAGS -I$with_ssl/include"
- LDFLAGS="$LDFLAGS -L$with_ssl/lib"
- AC_MSG_RESULT([Using SSL prefix... $with_ssl])
+AC_ARG_ENABLE([ssl],
+ AC_HELP_STRING([--enable-ssl[=backend]],
+ [Select 'gnutls' or 'openssl' (default) to provide SSL support (autodetect)]),
+ [ if test "x$enableval" = "xgnutls"; then
+ [ssl_backend="gnutls"]
+ else
+ [ssl_backend="openssl"]
fi
- AC_SUBST(openssl_h_present)
- AC_SUBST(libssl_present)
- AC_SUBST(libcrypto_present)
- AC_CHECK_HEADER(openssl/ssl.h,[openssl_h_present=1])
- AC_CHECK_LIB(crypto,ERR_load_BIO_strings,[libcrypto_present=1 && LIBS="$LIBS -lcrypto"])
- AC_CHECK_LIB(ssl,SSL_library_init,[libssl_present=1 && LIBS="$LIBS -lssl"])
- if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then
- AC_MSG_RESULT([OpenSSL library found, SSL support enabled])
- # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
- #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
- AC_DEFINE(PJ_HAS_SSL_SOCK, 1)
- else
- AC_MSG_RESULT([** OpenSSL libraries not found, disabling SSL support **])
- fi
- ])
+ ])
+
+if test "x$enable_ssl" = "xno"; then
+ [ac_no_ssl=1]
+ AC_MSG_RESULT([Checking if SSL support is disabled... yes])
+else
+ if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then
+ CFLAGS="$CFLAGS -I$with_ssl/include"
+ LDFLAGS="$LDFLAGS -L$with_ssl/lib"
+ AC_MSG_RESULT([Using SSL prefix... $with_ssl])
+ fi
+ if test "x$ssl_backend" = "xgnutls"; then
+ AC_CHECK_PROGS(PKG_CONFIG,
+ pkg-config "python pkgconfig.py",
+ none)
+ AC_MSG_RESULT([checking for GnuTLS installations..])
+ AC_SUBST(gnutls_h_present)
+ AC_SUBST(libgnutls_present)
+ AC_CHECK_HEADER(gnutls/gnutls.h, [gnutls_h_present=1])
+
+ if test "$PKG_CONFIG" != "none"; then
+ if $PKG_CONFIG --exists gnutls; then
+ LIBS="$LIBS `$PKG_CONFIG --libs gnutls`"
+ libgnutls_present=1
+ else
+ AC_CHECK_LIB(gnutls,
+ gnutls_certificate_set_x509_system_trust,
+ [libgnutls_present=1 &&
+ LIBS="$LIBS -lgnutls"])
+ fi
+ else
+ AC_MSG_RESULT([*** Warning: neither pkg-config nor python is available, disabling gnutls. ***])
+ fi
+
+ if test "x$gnutls_h_present" = "x1" -a "x$libgnutls_present" = "x1"; then
+ AC_MSG_RESULT([GnuTLS library found, SSL support enabled])
+ # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
+ #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
+ AC_DEFINE(PJ_HAS_SSL_SOCK, 1)
+ AC_DEFINE(PJ_HAS_TLS_SOCK, 1)
+ else
+ AC_MSG_RESULT([** No GnuTLS libraries found, disabling SSL support **])
+ fi
+ else
+ AC_MSG_RESULT([checking for OpenSSL installations..])
+ AC_SUBST(openssl_h_present)
+ AC_SUBST(libssl_present)
+ AC_SUBST(libcrypto_present)
+ AC_CHECK_HEADER(openssl/ssl.h, [openssl_h_present=1])
+ AC_CHECK_LIB(crypto,ERR_load_BIO_strings, [libcrypto_present=1 && LIBS="$LIBS -lcrypto"])
+ AC_CHECK_LIB(ssl,SSL_library_init, [libssl_present=1 && LIBS="$LIBS -lssl"])
+ if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then
+ AC_MSG_RESULT([OpenSSL library found, SSL support enabled])
+ # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
+ #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
+ AC_DEFINE(PJ_HAS_SSL_SOCK, 1)
+ AC_DEFINE(PJ_HAS_TLS_SOCK, 0)
+ else
+ AC_MSG_RESULT([** No OpenSSL libraries found, disabling SSL support **])
+ fi
+ fi
+fi
dnl # Obsolete option --with-opencore-amrnb
AC_ARG_WITH(opencore-amrnb,
diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile
index 1e64950..e650a31 100644
--- a/pjlib/build/Makefile
+++ b/pjlib/build/Makefile
@@ -35,7 +35,7 @@ export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
guid.o hash.o ip_helper_generic.o list.o lock.o log.o os_time_common.o \
os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \
rbtree.o sock_common.o sock_qos_common.o sock_qos_bsd.o \
- ssl_sock_common.o ssl_sock_ossl.o ssl_sock_dump.o \
+ ssl_sock_common.o ssl_sock_ossl.o ssl_sock_gtls.o ssl_sock_dump.o \
string.o timer.o types.o
export PJLIB_CFLAGS += $(_CFLAGS)
export PJLIB_CXXFLAGS += $(_CXXFLAGS)
diff --git a/pjlib/include/pj/compat/os_auto.h.in b/pjlib/include/pj/compat/os_auto.h.in
index 77980d3..6d6a625 100644
--- a/pjlib/include/pj/compat/os_auto.h.in
+++ b/pjlib/include/pj/compat/os_auto.h.in
@@ -206,6 +206,9 @@
#ifndef PJ_HAS_SSL_SOCK
#undef PJ_HAS_SSL_SOCK
#endif
+#ifndef PJ_HAS_TLS_SOCK
+#undef PJ_HAS_TLS_SOCK
+#endif
#endif /* __PJ_COMPAT_OS_AUTO_H__ */
diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
index bfb4b69..b5fdb4a 100644
--- a/pjlib/include/pj/config.h
+++ b/pjlib/include/pj/config.h
@@ -848,13 +848,15 @@
/**
* Enable secure socket. For most platforms, this is implemented using
- * OpenSSL, so this will require OpenSSL to be installed. For Symbian
+ * OpenSSL, so this will require OpenSSL or GnuTLS to be installed. For Symbian
* platform, this is implemented natively using CSecureSocket.
*
* Default: 0 (for now)
*/
#ifndef PJ_HAS_SSL_SOCK
# define PJ_HAS_SSL_SOCK 0
+ // When set to 1 secure sockets will use the GnuTLS backend
+# define PJ_HAS_TLS_SOCK 0
#endif
diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c
index 67a8d63..602a1af 100644
--- a/pjlib/src/pj/ssl_sock_common.c
+++ b/pjlib/src/pj/ssl_sock_common.c
@@ -34,7 +34,12 @@ PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param)
param->async_cnt = 1;
param->concurrency = -1;
param->whole_data = PJ_TRUE;
+#if defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 1
+ // GnuTLS is allowed to send bigger chunks
+ param->send_buffer_size = 65536;
+#else
param->send_buffer_size = 8192;
+#endif
#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0
param->read_buffer_size = 1500;
#endif
diff --git a/pjlib/src/pj/ssl_sock_gtls.c b/pjlib/src/pj/ssl_sock_gtls.c
new file mode 100644
index 0000000..75b3723
--- /dev/null
+++ b/pjlib/src/pj/ssl_sock_gtls.c
@@ -0,0 +1,2781 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2014 Savoir-Faire Linux. (http://www.savoirfairelinux.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pj/ssl_sock.h>
+#include <pj/activesock.h>
+#include <pj/compat/socket.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/list.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/timer.h>
+#include <pj/file_io.h>
+
+
+/* Only build when PJ_HAS_SSL_SOCK and PJ_HAS_TLS_SOCK are enabled */
+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \
+ defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK != 0
+
+#define THIS_FILE "ssl_sock_gtls.c"
+
+/* Workaround for ticket #985 */
+#define DELAYED_CLOSE_TIMEOUT 200
+
+/* Maximum ciphers */
+#define MAX_CIPHERS 100
+
+/* Standard trust locations */
+#define TRUST_STORE_FILE1 "/etc/ssl/certs/ca-certificates.crt"
+#define TRUST_STORE_FILE2 "/etc/ssl/certs/ca-bundle.crt"
+
+/* Debugging output level for GnuTLS only */
+#define GNUTLS_LOG_LEVEL 0
+
+/* GnuTLS includes */
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+
+#ifdef _MSC_VER
+# pragma comment( lib, "gnutls")
+#endif
+
+
+/* TLS state enumeration. */
+enum tls_connection_state {
+ TLS_STATE_NULL,
+ TLS_STATE_HANDSHAKING,
+ TLS_STATE_ESTABLISHED
+};
+
+/* Internal timer types. */
+enum timer_id {
+ TIMER_NONE,
+ TIMER_HANDSHAKE_TIMEOUT,
+ TIMER_CLOSE
+};
+
+/* Structure of SSL socket read buffer. */
+typedef struct read_data_t {
+ void *data;
+ pj_size_t len;
+} read_data_t;
+
+/*
+ * Get the offset of pointer to read-buffer of SSL socket from read-buffer
+ * of active socket. Note that both SSL socket and active socket employ
+ * different but correlated read-buffers (as much as async_cnt for each),
+ * and to make it easier/faster to find corresponding SSL socket's read-buffer
+ * from known active socket's read-buffer, the pointer of corresponding
+ * SSL socket's read-buffer is stored right after the end of active socket's
+ * read-buffer.
+ */
+#define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \
+ (read_data_t**) \
+ ((pj_int8_t *)(asock_rbuf) + \
+ ssock->param.read_buffer_size)
+
+/* Structure of SSL socket write data. */
+typedef struct write_data_t {
+ PJ_DECL_LIST_MEMBER(struct write_data_t);
+ pj_ioqueue_op_key_t key;
+ pj_size_t record_len;
+ pj_ioqueue_op_key_t *app_key;
+ pj_size_t plain_data_len;
+ pj_size_t data_len;
+ unsigned flags;
+ union {
+ char content[1];
+ const char *ptr;
+ } data;
+} write_data_t;
+
+
+/* Structure of SSL socket write buffer (circular buffer). */
+typedef struct send_buf_t {
+ char *buf;
+ pj_size_t max_len;
+ char *start;
+ pj_size_t len;
+} send_buf_t;
+
+
+/* Circular buffer object */
+typedef struct circ_buf_t {
+ pj_size_t cap; /* maximum number of elements (must be power of 2) */
+ pj_size_t readp; /* index of oldest element */
+ pj_size_t writep; /* index at which to write new element */
+ pj_size_t size; /* number of elements */
+ pj_uint8_t *buf; /* data buffer */
+ pj_pool_t *pool; /* where new allocations will take place */
+} circ_buf_t;
+
+
+/* Secure socket structure definition. */
+struct pj_ssl_sock_t {
+ pj_pool_t *pool;
+ pj_ssl_sock_t *parent;
+ pj_ssl_sock_param param;
+ pj_ssl_cert_t *cert;
+
+ pj_ssl_cert_info local_cert_info;
+ pj_ssl_cert_info remote_cert_info;
+
+ pj_bool_t is_server;
+ enum tls_connection_state connection_state;
+ pj_ioqueue_op_key_t handshake_op_key;
+ pj_timer_entry timer;
+ pj_status_t verify_status;
+
+ int last_err;
+
+ pj_sock_t sock;
+ pj_activesock_t *asock;
+
+ pj_sockaddr local_addr;
+ pj_sockaddr rem_addr;
+ int addr_len;
+
+ pj_bool_t read_started;
+ pj_size_t read_size;
+ pj_uint32_t read_flags;
+ void **asock_rbuf;
+ read_data_t *ssock_rbuf;
+
+ write_data_t write_pending; /* list of pending writes */
+ write_data_t write_pending_empty; /* cache for write_pending */
+ pj_bool_t flushing_write_pend; /* flag of flushing is ongoing */
+ send_buf_t send_buf;
+ write_data_t send_pending; /* list of pending write to network */
+
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t xcred;
+
+ circ_buf_t circ_buf_input;
+ pj_lock_t *circ_buf_input_mutex;
+
+ circ_buf_t circ_buf_output;
+ pj_lock_t *circ_buf_output_mutex;
+
+ int tls_init_count; /* library initialization counter */
+};
+
+
+/* Certificate/credential structure definition. */
+struct pj_ssl_cert_t {
+ pj_str_t CA_file;
+ pj_str_t cert_file;
+ pj_str_t privkey_file;
+ pj_str_t privkey_pass;
+};
+
+/* GnuTLS available ciphers */
+static unsigned tls_available_ciphers;
+
+/* Array of id/names for available ciphers */
+static struct tls_ciphers_t {
+ pj_ssl_cipher id;
+ const char *name;
+} tls_ciphers[MAX_CIPHERS];
+
+/* Last error reported somehow */
+static int tls_last_error;
+
+
+/*
+ *******************************************************************
+ * Circular buffer functions.
+ *******************************************************************
+ */
+
+static pj_status_t circ_init(pj_pool_factory *factory,
+ circ_buf_t *cb, pj_size_t cap)
+{
+ cb->cap = cap;
+ cb->readp = 0;
+ cb->writep = 0;
+ cb->size = 0;
+
+ /* Initial pool holding the buffer elements */
+ cb->pool = pj_pool_create(factory, "tls-circ%p", cap, cap, NULL);
+ if (!cb->pool)
+ return PJ_ENOMEM;
+
+ /* Allocate circular buffer */
+ cb->buf = pj_pool_alloc(cb->pool, cap);
+ if (!cb->buf) {
+ pj_pool_release(cb->pool);
+ return PJ_ENOMEM;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static void circ_deinit(circ_buf_t *cb)
+{
+ if (cb->pool) {
+ pj_pool_release(cb->pool);
+ cb->pool = NULL;
+ }
+}
+
+static pj_bool_t circ_empty(const circ_buf_t *cb)
+{
+ return cb->size == 0;
+}
+
+static pj_size_t circ_size(const circ_buf_t *cb)
+{
+ return cb->size;
+}
+
+static pj_size_t circ_avail(const circ_buf_t *cb)
+{
+ return cb->cap - cb->size;
+}
+
+static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len)
+{
+ pj_size_t size_after = cb->cap - cb->readp;
+ pj_size_t tbc = PJ_MIN(size_after, len);
+ pj_size_t rem = len - tbc;
+
+ pj_memcpy(dst, cb->buf + cb->readp, tbc);
+ pj_memcpy(dst + tbc, cb->buf, rem);
+
+ cb->readp += len;
+ cb->readp &= (cb->cap - 1);
+
+ cb->size -= len;
+}
+
+static pj_status_t circ_write(circ_buf_t *cb,
+ const pj_uint8_t *src, pj_size_t len)
+{
+ /* Overflow condition: resize */
+ if (len > circ_avail(cb)) {
+ /* Minimum required capacity */
+ pj_size_t min_cap = len + cb->size;
+
+ /* Next 32-bit power of two */
+ min_cap--;
+ min_cap |= min_cap >> 1;
+ min_cap |= min_cap >> 2;
+ min_cap |= min_cap >> 4;
+ min_cap |= min_cap >> 8;
+ min_cap |= min_cap >> 16;
+ min_cap++;
+
+ /* Create a new pool to hold a bigger buffer, using the same factory */
+ pj_pool_t *pool = pj_pool_create(cb->pool->factory, "tls-circ%p",
+ min_cap, min_cap, NULL);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ /* Allocate our new buffer */
+ pj_uint8_t *buf = pj_pool_alloc(pool, min_cap);
+ if (!buf) {
+ pj_pool_release(pool);
+ return PJ_ENOMEM;
+ }
+
+ /* Save old size, which we shall restore after the next read */
+ pj_size_t old_size = cb->size;
+
+ /* Copy old data into beginning of new buffer */
+ circ_read(cb, buf, cb->size);
+
+ /* Restore old size now */
+ cb->size = old_size;
+
+ /* Release the previous pool */
+ pj_pool_release(cb->pool);
+
+ /* Update circular buffer members */
+ cb->pool = pool;
+ cb->buf = buf;
+ cb->readp = 0;
+ cb->writep = cb->size;
+ cb->cap = min_cap;
+ }
+
+ pj_size_t size_after = cb->cap - cb->writep;
+ pj_size_t tbc = PJ_MIN(size_after, len);
+ pj_size_t rem = len - tbc;
+
+ pj_memcpy(cb->buf + cb->writep, src, tbc);
+ pj_memcpy(cb->buf, src + tbc, rem);
+
+ cb->writep += len;
+ cb->writep &= (cb->cap - 1);
+
+ cb->size += len;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ *******************************************************************
+ * Static/internal functions.
+ *******************************************************************
+ */
+
+/* Convert from GnuTLS error to pj_status_t. */
+static pj_status_t tls_status_from_err(pj_ssl_sock_t *ssock, int err)
+{
+ pj_status_t status;
+
+ switch (err) {
+ case GNUTLS_E_SUCCESS:
+ status = PJ_SUCCESS;
+ break;
+ case GNUTLS_E_MEMORY_ERROR:
+ status = PJ_ENOMEM;
+ break;
+ case GNUTLS_E_LARGE_PACKET:
+ status = PJ_ETOOBIG;
+ break;
+ case GNUTLS_E_NO_CERTIFICATE_FOUND:
+ status = PJ_ENOTFOUND;
+ break;
+ case GNUTLS_E_SESSION_EOF:
+ status = PJ_EEOF;
+ break;
+ case GNUTLS_E_HANDSHAKE_TOO_LARGE:
+ status = PJ_ETOOBIG;
+ break;
+ case GNUTLS_E_EXPIRED:
+ status = PJ_EGONE;
+ break;
+ case GNUTLS_E_TIMEDOUT:
+ status = PJ_ETIMEDOUT;
+ break;
+ case GNUTLS_E_PREMATURE_TERMINATION:
+ status = PJ_ECANCELLED;
+ break;
+ case GNUTLS_E_INTERNAL_ERROR:
+ case GNUTLS_E_UNIMPLEMENTED_FEATURE:
+ status = PJ_EBUG;
+ break;
+ case GNUTLS_E_AGAIN:
+ case GNUTLS_E_INTERRUPTED:
+ case GNUTLS_E_REHANDSHAKE:
+ status = PJ_EPENDING;
+ break;
+ case GNUTLS_E_TOO_MANY_EMPTY_PACKETS:
+ case GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS:
+ case GNUTLS_E_RECORD_LIMIT_REACHED:
+ status = PJ_ETOOMANY;
+ break;
+ case GNUTLS_E_UNSUPPORTED_VERSION_PACKET:
+ case GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM:
+ case GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE:
+ case GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE:
+ case GNUTLS_E_X509_UNSUPPORTED_EXTENSION:
+ case GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION:
+ status = PJ_ENOTSUP;
+ break;
+ case GNUTLS_E_INVALID_SESSION:
+ case GNUTLS_E_INVALID_REQUEST:
+ case GNUTLS_E_INVALID_PASSWORD:
+ case GNUTLS_E_ILLEGAL_PARAMETER:
+ case GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION:
+ case GNUTLS_E_UNEXPECTED_PACKET:
+ case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
+ case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET:
+ case GNUTLS_E_UNWANTED_ALGORITHM:
+ case GNUTLS_E_USER_ERROR:
+ status = PJ_EINVAL;
+ break;
+ default:
+ status = PJ_EUNKNOWN;
+ break;
+ }
+
+ /* Not thread safe */
+ tls_last_error = err;
+ if (ssock)
+ ssock->last_err = err;
+ return status;
+}
+
+
+/* Get error string from GnuTLS using tls_last_error */
+static pj_str_t tls_strerror(pj_status_t status,
+ char *buf, pj_size_t bufsize)
+{
+ pj_str_t errstr;
+ const char *tmp = gnutls_strerror(tls_last_error);
+
+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
+ if (tmp) {
+ pj_ansi_strncpy(buf, tmp, bufsize);
+ errstr = pj_str(buf);
+ return errstr;
+ }
+#endif /* PJ_HAS_ERROR_STRING */
+
+ errstr.ptr = buf;
+ errstr.slen = pj_ansi_snprintf(buf, bufsize, "GnuTLS error %d: %s",
+ tls_last_error, tmp);
+ if (errstr.slen < 1 || errstr.slen >= (int) bufsize)
+ errstr.slen = bufsize - 1;
+
+ return errstr;
+}
+
+
+/* GnuTLS way of reporting internal operations. */
+static void tls_print_logs(int level, const char* msg)
+{
+ PJ_LOG(3, (THIS_FILE, "GnuTLS [%d]: %s", level, msg));
+}
+
+
+/* Initialize GnuTLS. */
+static pj_status_t tls_init(void)
+{
+ /* Register error subsystem */
+ pj_status_t status = pj_register_strerror(PJ_ERRNO_START_USER +
+ PJ_ERRNO_SPACE_SIZE * 6,
+ PJ_ERRNO_SPACE_SIZE,
+ &tls_strerror);
+ pj_assert(status == PJ_SUCCESS);
+
+ /* Init GnuTLS library */
+ int ret = gnutls_global_init();
+ if (ret < 0)
+ return tls_status_from_err(NULL, ret);
+
+ gnutls_global_set_log_level(GNUTLS_LOG_LEVEL);
+ gnutls_global_set_log_function(tls_print_logs);
+
+ /* Init available ciphers */
+ if (!tls_available_ciphers) {
+ unsigned int i;
+
+ for (i = 0; ; i++) {
+ unsigned char id[2];
+ const char *suite = gnutls_cipher_suite_info(i, (unsigned char *)id,
+ NULL, NULL, NULL, NULL);
+ tls_ciphers[i].id = 0;
+ /* usually the array size is bigger than the number of available
+ * ciphers anyway, so by checking here we can exit the loop as soon
+ * as either all ciphers have been added or the array is full */
+ if (suite && i < PJ_ARRAY_SIZE(tls_ciphers)) {
+ tls_ciphers[i].id = (pj_ssl_cipher)
+ (pj_uint32_t) ((id[0] << 8) | id[1]);
+ tls_ciphers[i].name = suite;
+ } else
+ break;
+ }
+
+ tls_available_ciphers = i;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* Shutdown GnuTLS */
+static void tls_deinit(void)
+{
+ gnutls_global_deinit();
+}
+
+
+/* Callback invoked every time a certificate has to be validated. */
+static int tls_cert_verify_cb(gnutls_session_t session)
+{
+ pj_ssl_sock_t *ssock;
+ unsigned int status;
+ int ret;
+
+ /* Get SSL socket instance */
+ ssock = (pj_ssl_sock_t *)gnutls_session_get_ptr(session);
+ pj_assert(ssock);
+
+ /* Support only x509 format */
+ ret = gnutls_certificate_type_get(session) != GNUTLS_CRT_X509;
+ if (ret < 0) {
+ ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT;
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ /* Store verification status */
+ ret = gnutls_certificate_verify_peers2(session, &status);
+ if (ret < 0) {
+ ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+ if (status & GNUTLS_CERT_INVALID) {
+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+ ssock->verify_status |= PJ_SSL_CERT_EISSUER_NOT_FOUND;
+ else if (status & GNUTLS_CERT_EXPIRED ||
+ status & GNUTLS_CERT_NOT_ACTIVATED)
+ ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD;
+ else if (status & GNUTLS_CERT_SIGNER_NOT_CA ||
+ status & GNUTLS_CERT_INSECURE_ALGORITHM)
+ ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED;
+ else if (status & GNUTLS_CERT_UNEXPECTED_OWNER ||
+ status & GNUTLS_CERT_MISMATCH)
+ ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH;
+ else if (status & GNUTLS_CERT_REVOKED)
+ ssock->verify_status |= PJ_SSL_CERT_EREVOKED;
+ else
+ ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
+
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ /* When verification is not requested just return ok here, however
+ * applications can still get the verification status. */
+ if (ssock->param.verify_peer) {
+ gnutls_x509_crt_t cert;
+ unsigned int cert_list_size;
+ const gnutls_datum_t *cert_list;
+ int ret;
+
+ ret = gnutls_x509_crt_init(&cert);
+ if (ret < 0)
+ goto out;
+
+ cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
+ if (cert_list == NULL) {
+ ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
+ goto out;
+ }
+
+ /* TODO: verify whole chain perhaps? */
+ ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ ret = gnutls_x509_crt_import(cert, &cert_list[0],
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT;
+ goto out;
+ }
+ ret = gnutls_x509_crt_check_hostname(cert, ssock->param.server_name.ptr);
+ if (ret < 0)
+ goto out;
+
+ gnutls_x509_crt_deinit(cert);
+
+ /* notify GnuTLS to continue handshake normally */
+ return GNUTLS_E_SUCCESS;
+
+out:
+ tls_last_error = ret;
+ ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+
+/* gnutls_handshake() and gnutls_record_send() will call this function to
+ * send/write (encrypted) data */
+static ssize_t tls_data_push(gnutls_transport_ptr_t ptr,
+ const void *data, size_t len)
+{
+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr;
+
+ pj_lock_acquire(ssock->circ_buf_output_mutex);
+ if (circ_write(&ssock->circ_buf_output, data, len) != PJ_SUCCESS) {
+ pj_lock_release(ssock->circ_buf_output_mutex);
+
+ errno = ENOMEM;
+ return -1;
+ }
+
+ pj_lock_release(ssock->circ_buf_output_mutex);
+
+ return len;
+}
+
+
+/* gnutls_handshake() and gnutls_record_recv() will call this function to
+ * receive/read (encrypted) data */
+static ssize_t tls_data_pull(gnutls_transport_ptr_t ptr,
+ void *data, pj_size_t len)
+{
+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr;
+
+ pj_lock_acquire(ssock->circ_buf_input_mutex);
+
+ if (circ_empty(&ssock->circ_buf_input)) {
+ pj_lock_release(ssock->circ_buf_input_mutex);
+
+ /* Data buffers not yet filled */
+ errno = EAGAIN;
+ return -1;
+ }
+
+ pj_size_t circ_buf_size = circ_size(&ssock->circ_buf_input);
+ pj_size_t read_size = PJ_MIN(circ_buf_size, len);
+
+ circ_read(&ssock->circ_buf_input, data, read_size);
+
+ pj_lock_release(ssock->circ_buf_input_mutex);
+
+ return read_size;
+}
+
+
+/* Append a string to the priority string, only once. */
+static pj_status_t tls_str_append_once(pj_str_t *dst, pj_str_t *src)
+{
+ if (pj_strstr(dst, src) == NULL) {
+ /* Check buffer size */
+ if (dst->slen + src->slen + 3 > 1024)
+ return PJ_ETOOMANY;
+
+ pj_strcat2(dst, ":+");
+ pj_strcat(dst, src);
+ }
+ return PJ_SUCCESS;
+}
+
+
+/* Generate priority string with user preference order. */
+static pj_status_t tls_priorities_set(pj_ssl_sock_t *ssock)
+{
+ char buf[1024];
+ pj_str_t cipher_list;
+ pj_str_t compression = pj_str("COMP-NULL");
+ pj_str_t server = pj_str(":%SERVER_PRECEDENCE");
+ int i, j, ret;
+ const char *priority;
+ const char *err;
+
+ pj_strset(&cipher_list, buf, 0);
+
+ /* For each level, enable only the requested protocol */
+ switch (ssock->param.proto) {
+ case PJ_SSL_SOCK_PROTO_DEFAULT:
+ case PJ_SSL_SOCK_PROTO_TLS1:
+ // set lowest compatibility mode, ask for TLS client hello
+ if (ssock->param.ciphers_num == 0)
+ priority = "NORMAL:-VERS-SSL3.0:-VERS-TLS1.1:-VERS-TLS1.2:%LATEST_RECORD_VERSION";
+ else
+ priority = "NONE:+VERS-TLS1.0:%LATEST_RECORD_VERSION";
+ break;
+ case PJ_SSL_SOCK_PROTO_SSL3:
+ if (ssock->param.ciphers_num == 0)
+ priority = "NORMAL:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2";
+ else
+ priority = "NONE:+VERS-SSL3.0";
+ break;
+ case PJ_SSL_SOCK_PROTO_SSL23:
+ /* GnuTLS does not support any SSLv2 suite, let's just enable SSLv3
+ * with maximum compatibility */
+ if (ssock->param.ciphers_num == 0)
+ priority = "NORMAL:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:%COMPAT";
+ else
+ priority = "NONE:+VERS-SSL3.0:%COMPAT";
+ break;
+ default:
+ return PJ_ENOTSUP;
+ }
+
+ pj_strcat2(&cipher_list, priority);
+ for (i = 0; i < ssock->param.ciphers_num; i++) {
+ for (j = 0; ; j++) {
+ pj_ssl_cipher c;
+ const char *suite;
+ unsigned char id[2];
+ gnutls_protocol_t proto;
+ gnutls_kx_algorithm_t kx;
+ gnutls_mac_algorithm_t mac;
+ gnutls_cipher_algorithm_t algo;
+
+ suite = gnutls_cipher_suite_info(j, (unsigned char *)id,
+ &kx, &algo, &mac, &proto);
+ if (!suite)
+ break;
+
+ c = (pj_ssl_cipher) (pj_uint32_t) ((id[0] << 8) | id[1]);
+ if (ssock->param.ciphers[i] == c) {
+ char temp[256];
+ pj_str_t cipher_entry;
+
+ /* Protocol version */
+ pj_strset(&cipher_entry, temp, 0);
+ pj_strcat2(&cipher_entry, "VERS-");
+ pj_strcat2(&cipher_entry, gnutls_protocol_get_name(proto));
+ ret = tls_str_append_once(&cipher_list, &cipher_entry);
+ if (ret != PJ_SUCCESS)
+ return ret;
+
+ /* Cipher */
+ pj_strset(&cipher_entry, temp, 0);
+ pj_strcat2(&cipher_entry, gnutls_cipher_get_name(algo));
+ ret = tls_str_append_once(&cipher_list, &cipher_entry);
+ if (ret != PJ_SUCCESS)
+ return ret;
+
+ /* Mac */
+ pj_strset(&cipher_entry, temp, 0);
+ pj_strcat2(&cipher_entry, gnutls_mac_get_name(mac));
+ ret = tls_str_append_once(&cipher_list, &cipher_entry);
+ if (ret != PJ_SUCCESS)
+ return ret;
+
+ /* Key exchange */
+ pj_strset(&cipher_entry, temp, 0);
+ pj_strcat2(&cipher_entry, gnutls_kx_get_name(kx));
+ ret = tls_str_append_once(&cipher_list, &cipher_entry);
+ if (ret != PJ_SUCCESS)
+ return ret;
+
+ /* Compression is always disabled */
+ /* Signature is level-default */
+ break;
+ }
+ }
+ }
+
+ /* Disable compression, it's a TLS-only extension after all */
+ tls_str_append_once(&cipher_list, &compression);
+
+ /* Server will be the one deciding which crypto to use */
+ if (ssock->is_server) {
+ if (cipher_list.slen + server.slen + 1 > sizeof(buf))
+ return PJ_ETOOMANY;
+ else
+ pj_strcat(&cipher_list, &server);
+ }
+
+ /* End the string and print it */
+ cipher_list.ptr[cipher_list.slen] = '\0';
+ PJ_LOG(5, (ssock->pool->obj_name, "Priority string: %s", cipher_list.ptr));
+
+ /* Set our priority string */
+ ret = gnutls_priority_set_direct(ssock->session,
+ cipher_list.ptr, &err);
+ if (ret < 0) {
+ tls_last_error = GNUTLS_E_INVALID_REQUEST;
+ return PJ_EINVAL;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* Load root CA file or load the installed ones. */
+static pj_status_t tls_trust_set(pj_ssl_sock_t *ssock)
+{
+ int ntrusts = 0;
+ int err;
+
+ err = gnutls_certificate_set_x509_system_trust(ssock->xcred);
+ if (err > 0)
+ ntrusts += err;
+ err = gnutls_certificate_set_x509_trust_file(ssock->xcred,
+ TRUST_STORE_FILE1,
+ GNUTLS_X509_FMT_PEM);
+ if (err > 0)
+ ntrusts += err;
+
+ err = gnutls_certificate_set_x509_trust_file(ssock->xcred,
+ TRUST_STORE_FILE2,
+ GNUTLS_X509_FMT_PEM);
+ if (err > 0)
+ ntrusts += err;
+
+ if (ntrusts > 0)
+ return PJ_SUCCESS;
+ else if (!ntrusts)
+ return PJ_ENOTFOUND;
+ else
+ return PJ_EINVAL;
+}
+
+
+/* Create and initialize new GnuTLS context and instance */
+static pj_status_t tls_open(pj_ssl_sock_t *ssock)
+{
+ pj_ssl_cert_t *cert;
+ pj_status_t status;
+ int ret;
+
+ pj_assert(ssock);
+
+ cert = ssock->cert;
+
+ /* Even if reopening is harmless, having one instance only simplifies
+ * deallocating it later on */
+ if (!ssock->tls_init_count) {
+ ssock->tls_init_count++;
+ ret = tls_init();
+ if (ret < 0)
+ return ret;
+ } else
+ return PJ_SUCCESS;
+
+ /* Start this socket session */
+ ret = gnutls_init(&ssock->session, ssock->is_server ? GNUTLS_SERVER
+ : GNUTLS_CLIENT);
+ if (ret < 0)
+ goto out;
+
+ /* Set the ssock object to be retrieved by transport (send/recv) and by
+ * user data from this session */
+ gnutls_transport_set_ptr(ssock->session,
+ (gnutls_transport_ptr_t) (uintptr_t) ssock);
+ gnutls_session_set_ptr(ssock->session,
+ (gnutls_transport_ptr_t) (uintptr_t) ssock);
+
+ /* Initialize input circular buffer */
+ status = circ_init(ssock->pool->factory, &ssock->circ_buf_input, 512);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Initialize output circular buffer */
+ status = circ_init(ssock->pool->factory, &ssock->circ_buf_output, 512);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Set the callback that allows GnuTLS to PUSH and PULL data
+ * TO and FROM the transport layer */
+ gnutls_transport_set_push_function(ssock->session, tls_data_push);
+ gnutls_transport_set_pull_function(ssock->session, tls_data_pull);
+
+ /* Determine which cipher suite to support */
+ status = tls_priorities_set(ssock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Allocate credentials for handshaking and transmission */
+ ret = gnutls_certificate_allocate_credentials(&ssock->xcred);
+ if (ret < 0)
+ goto out;
+ gnutls_certificate_set_verify_function(ssock->xcred, tls_cert_verify_cb);
+
+ /* Load system trust file(s) */
+ status = tls_trust_set(ssock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Load user-provided CA, certificate and key if available */
+ if (cert) {
+ /* Load CA if one is specified. */
+ if (cert->CA_file.slen) {
+ ret = gnutls_certificate_set_x509_trust_file(ssock->xcred,
+ cert->CA_file.ptr,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ ret = gnutls_certificate_set_x509_trust_file(ssock->xcred,
+ cert->CA_file.ptr,
+ GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ goto out;
+ }
+
+ /* Load certificate, key and pass if one is specified */
+ if (cert->cert_file.slen) {
+ const char *prikey_file = cert->privkey_file.slen
+ ? cert->privkey_file.ptr
+ : NULL;
+ const char *prikey_pass = (cert->privkey_file.slen &&
+ cert->privkey_pass.slen)
+ ? cert->privkey_pass.ptr
+ : NULL;
+ ret = gnutls_certificate_set_x509_key_file2(ssock->xcred,
+ cert->cert_file.ptr,
+ prikey_file,
+ GNUTLS_X509_FMT_PEM,
+ prikey_pass,
+ 0);
+ if (ret != GNUTLS_E_SUCCESS)
+ ret = gnutls_certificate_set_x509_key_file2(ssock->xcred,
+ cert->cert_file.ptr,
+ prikey_file,
+ GNUTLS_X509_FMT_DER,
+ prikey_pass,
+ 0);
+ if (ret < 0)
+ goto out;
+ }
+ }
+
+ /* Require client certificate if asked */
+ if (ssock->is_server && ssock->param.require_client_cert)
+ gnutls_certificate_server_set_request(ssock->session,
+ GNUTLS_CERT_REQUIRE);
+
+ /* Finally set credentials for this session */
+ ret = gnutls_credentials_set(ssock->session,
+ GNUTLS_CRD_CERTIFICATE, ssock->xcred);
+ if (ret < 0)
+ goto out;
+
+ ret = GNUTLS_E_SUCCESS;
+out:
+ return tls_status_from_err(ssock, ret);
+}
+
+
+/* Destroy GnuTLS credentials and session. */
+static void tls_close(pj_ssl_sock_t *ssock)
+{
+ if (ssock->session) {
+ gnutls_bye(ssock->session, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(ssock->session);
+ ssock->session = NULL;
+ }
+
+ if (ssock->xcred) {
+ gnutls_certificate_free_credentials(ssock->xcred);
+ ssock->xcred = NULL;
+ }
+
+ /* Free GnuTLS library */
+ if (ssock->tls_init_count) {
+ ssock->tls_init_count--;
+ tls_deinit();
+ }
+
+ /* Destroy circular buffers */
+ circ_deinit(&ssock->circ_buf_input);
+ circ_deinit(&ssock->circ_buf_output);
+}
+
+
+/* Reset socket state. */
+static void tls_sock_reset(pj_ssl_sock_t *ssock)
+{
+ ssock->connection_state = TLS_STATE_NULL;
+
+ tls_close(ssock);
+
+ if (ssock->asock) {
+ pj_activesock_close(ssock->asock);
+ ssock->asock = NULL;
+ ssock->sock = PJ_INVALID_SOCKET;
+ }
+ if (ssock->sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(ssock->sock);
+ ssock->sock = PJ_INVALID_SOCKET;
+ }
+
+ ssock->last_err = tls_last_error = GNUTLS_E_SUCCESS;
+}
+
+
+/* Get Common Name field string from a general name string */
+static void tls_cert_get_cn(const pj_str_t *gen_name, pj_str_t *cn)
+{
+ pj_str_t CN_sign = {"CN=", 3};
+ char *p, *q;
+
+ pj_bzero(cn, sizeof(cn));
+
+ p = pj_strstr(gen_name, &CN_sign);
+ if (!p)
+ return;
+
+ p += 3; /* shift pointer to value part */
+ pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr));
+ q = pj_strchr(cn, ',');
+ if (q)
+ cn->slen = q - p;
+}
+
+
+/* Get certificate info; in case the certificate info is already populated,
+ * this function will check if the contents need updating by inspecting the
+ * issuer and the serial number. */
+static void tls_cert_get_info(pj_pool_t *pool, pj_ssl_cert_info *ci,
+ gnutls_x509_crt_t cert)
+{
+ pj_bool_t update_needed;
+ char buf[512] = { 0 };
+ size_t bufsize = sizeof(buf);
+ pj_uint8_t serial_no[64] = { 0 }; /* should be >= sizeof(ci->serial_no) */
+ size_t serialsize = sizeof(serial_no);
+ size_t len = sizeof(buf);
+ int i, ret, seq = 0;
+ pj_ssl_cert_name_type type;
+
+ pj_assert(pool && ci && cert);
+
+ /* Get issuer */
+ gnutls_x509_crt_get_issuer_dn(cert, buf, &bufsize);
+
+ /* Get serial no */
+ gnutls_x509_crt_get_serial(cert, serial_no, &serialsize);
+
+ /* Check if the contents need to be updated */
+ update_needed = pj_strcmp2(&ci->issuer.info, buf) ||
+ pj_memcmp(ci->serial_no, serial_no, serialsize);
+ if (!update_needed)
+ return;
+
+ /* Update cert info */
+
+ pj_bzero(ci, sizeof(pj_ssl_cert_info));
+
+ /* Version */
+ ci->version = gnutls_x509_crt_get_version(cert);
+
+ /* Issuer */
+ pj_strdup2(pool, &ci->issuer.info, buf);
+ tls_cert_get_cn(&ci->issuer.info, &ci->issuer.cn);
+
+ /* Serial number */
+ pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no));
+
+ /* Subject */
+ bufsize = sizeof(buf);
+ gnutls_x509_crt_get_dn(cert, buf, &bufsize);
+ pj_strdup2(pool, &ci->subject.info, buf);
+ tls_cert_get_cn(&ci->subject.info, &ci->subject.cn);
+
+ /* Validity */
+ ci->validity.end.sec = gnutls_x509_crt_get_expiration_time(cert);
+ ci->validity.start.sec = gnutls_x509_crt_get_activation_time(cert);
+ ci->validity.gmt = 0;
+
+ /* Subject Alternative Name extension */
+ if (ci->version >= 3) {
+ char out[256] = { 0 };
+ /* Get the number of all alternate names so that we can allocate
+ * the correct number of bytes in subj_alt_name */
+ while (gnutls_x509_crt_get_subject_alt_name(cert, seq, out, &len,
+ NULL) != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ seq++;
+
+ ci->subj_alt_name.entry = pj_pool_calloc(pool, seq,
+ sizeof(*ci->subj_alt_name.entry));
+ if (!ci->subj_alt_name.entry) {
+ tls_last_error = GNUTLS_E_MEMORY_ERROR;
+ return;
+ }
+
+ /* Now populate the alternative names */
+ for (i = 0; i < seq; i++) {
+ len = sizeof(out) - 1;
+ ret = gnutls_x509_crt_get_subject_alt_name(cert, i, out, &len, NULL);
+ switch (ret) {
+ case GNUTLS_SAN_IPADDRESS:
+ type = PJ_SSL_CERT_NAME_IP;
+ pj_inet_ntop2(len == sizeof(pj_in6_addr) ? pj_AF_INET6()
+ : pj_AF_INET(),
+ out, buf, sizeof(buf));
+ break;
+ case GNUTLS_SAN_URI:
+ type = PJ_SSL_CERT_NAME_URI;
+ break;
+ case GNUTLS_SAN_RFC822NAME:
+ type = PJ_SSL_CERT_NAME_RFC822;
+ break;
+ case GNUTLS_SAN_DNSNAME:
+ type = PJ_SSL_CERT_NAME_DNS;
+ break;
+ default:
+ type = PJ_SSL_CERT_NAME_UNKNOWN;
+ break;
+ }
+
+ if (len && type != PJ_SSL_CERT_NAME_UNKNOWN) {
+ ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type;
+ pj_strdup2(pool,
+ &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
+ type == PJ_SSL_CERT_NAME_IP ? buf : out);
+ ci->subj_alt_name.cnt++;
+ }
+ }
+ /* TODO: if no DNS alt. names were found, we could check against
+ * the commonName as per RFC3280. */
+ }
+}
+
+
+/* Update local & remote certificates info. This function should be
+ * called after handshake or renegotiation successfully completed. */
+static void tls_cert_update(pj_ssl_sock_t *ssock)
+{
+ gnutls_x509_crt_t cert = NULL;
+ const gnutls_datum_t *us;
+ const gnutls_datum_t *certs;
+ unsigned int certslen = 0;
+ int ret = GNUTLS_CERT_INVALID;
+
+ pj_assert(ssock->connection_state == TLS_STATE_ESTABLISHED);
+
+ /* Get active local certificate */
+ us = gnutls_certificate_get_ours(ssock->session);
+ if (!us)
+ goto us_out;
+
+ ret = gnutls_x509_crt_init(&cert);
+ if (ret < 0)
+ goto us_out;
+ ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ goto us_out;
+
+ tls_cert_get_info(ssock->pool, &ssock->local_cert_info, cert);
+
+us_out:
+ tls_last_error = ret;
+ if (cert)
+ gnutls_x509_crt_deinit(cert);
+ else
+ pj_bzero(&ssock->local_cert_info, sizeof(pj_ssl_cert_info));
+
+ cert = NULL;
+
+ /* Get active remote certificate */
+ certs = gnutls_certificate_get_peers(ssock->session, &certslen);
+ if (certs == NULL || certslen == 0)
+ goto peer_out;
+
+ ret = gnutls_x509_crt_init(&cert);
+ if (ret < 0)
+ goto peer_out;
+
+ ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ goto peer_out;
+
+ tls_cert_get_info(ssock->pool, &ssock->remote_cert_info, cert);
+
+peer_out:
+ tls_last_error = ret;
+ if (cert)
+ gnutls_x509_crt_deinit(cert);
+ else
+ pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info));
+}
+
+
+/* When handshake completed:
+ * - notify application
+ * - if handshake failed, reset SSL state
+ * - return PJ_FALSE when SSL socket instance is destroyed by application. */
+static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock,
+ pj_status_t status)
+{
+ pj_bool_t ret = PJ_TRUE;
+
+ /* Cancel handshake timer */
+ if (ssock->timer.id == TIMER_HANDSHAKE_TIMEOUT) {
+ pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
+ ssock->timer.id = TIMER_NONE;
+ }
+
+ /* Update certificates info on successful handshake */
+ if (status == PJ_SUCCESS)
+ tls_cert_update(ssock);
+
+ /* Accepting */
+ if (ssock->is_server) {
+ if (status != PJ_SUCCESS) {
+ /* Handshake failed in accepting, destroy our self silently. */
+
+ char errmsg[PJ_ERR_MSG_SIZE];
+ char buf[PJ_INET6_ADDRSTRLEN + 10];
+
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ PJ_LOG(3, (ssock->pool->obj_name,
+ "Handshake failed in accepting %s: %s",
+ pj_sockaddr_print(&ssock->rem_addr, buf, sizeof(buf), 3),
+ errmsg));
+
+ /* Workaround for ticket #985 */
+#if (defined(PJ_WIN32) && PJ_WIN32 != 0) || (defined(PJ_WIN64) && PJ_WIN64 != 0)
+ if (ssock->param.timer_heap) {
+ pj_time_val interval = {0, DELAYED_CLOSE_TIMEOUT};
+
+ tls_sock_reset(ssock);
+
+ ssock->timer.id = TIMER_CLOSE;
+ pj_time_val_normalize(&interval);
+ if (pj_timer_heap_schedule(ssock->param.timer_heap,
+ &ssock->timer, &interval) != 0)
+ {
+ ssock->timer.id = TIMER_NONE;
+ pj_ssl_sock_close(ssock);
+ }
+ } else
+#endif /* PJ_WIN32 */
+ {
+ pj_ssl_sock_close(ssock);
+ }
+
+ return PJ_FALSE;
+ }
+ /* Notify application the newly accepted SSL socket */
+ if (ssock->param.cb.on_accept_complete)
+ ret = (*ssock->param.cb.on_accept_complete)
+ (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr,
+ pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr));
+
+ } else { /* Connecting */
+ /* On failure, reset SSL socket state first, as app may try to
+ * reconnect in the callback. */
+ if (status != PJ_SUCCESS) {
+ /* Server disconnected us, possibly due to negotiation failure */
+ tls_sock_reset(ssock);
+ }
+ if (ssock->param.cb.on_connect_complete) {
+
+ ret = (*ssock->param.cb.on_connect_complete)(ssock, status);
+ }
+ }
+
+ return ret;
+}
+
+static write_data_t *alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len)
+{
+ send_buf_t *send_buf = &ssock->send_buf;
+ pj_size_t avail_len, skipped_len = 0;
+ char *reg1, *reg2;
+ pj_size_t reg1_len, reg2_len;
+ write_data_t *p;
+
+ /* Check buffer availability */
+ avail_len = send_buf->max_len - send_buf->len;
+ if (avail_len < len)
+ return NULL;
+
+ /* If buffer empty, reset start pointer and return it */
+ if (send_buf->len == 0) {
+ send_buf->start = send_buf->buf;
+ send_buf->len = len;
+ p = (write_data_t*)send_buf->start;
+ goto init_send_data;
+ }
+
+ /* Free space may be wrapped/splitted into two regions, so let's
+ * analyze them if any region can hold the write data. */
+ reg1 = send_buf->start + send_buf->len;
+ if (reg1 >= send_buf->buf + send_buf->max_len)
+ reg1 -= send_buf->max_len;
+ reg1_len = send_buf->max_len - send_buf->len;
+ if (reg1 + reg1_len > send_buf->buf + send_buf->max_len) {
+ reg1_len = send_buf->buf + send_buf->max_len - reg1;
+ reg2 = send_buf->buf;
+ reg2_len = send_buf->start - send_buf->buf;
+ } else {
+ reg2 = NULL;
+ reg2_len = 0;
+ }
+
+ /* More buffer availability check, note that the write data must be in
+ * a contigue buffer. */
+ avail_len = PJ_MAX(reg1_len, reg2_len);
+ if (avail_len < len)
+ return NULL;
+
+ /* Get the data slot */
+ if (reg1_len >= len) {
+ p = (write_data_t*)reg1;
+ } else {
+ p = (write_data_t*)reg2;
+ skipped_len = reg1_len;
+ }
+
+ /* Update buffer length */
+ send_buf->len += len + skipped_len;
+
+init_send_data:
+ /* Init the new send data */
+ pj_bzero(p, sizeof(*p));
+ pj_list_init(p);
+ pj_list_push_back(&ssock->send_pending, p);
+
+ return p;
+}
+
+static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata)
+{
+ send_buf_t *buf = &ssock->send_buf;
+ write_data_t *spl = &ssock->send_pending;
+
+ pj_assert(!pj_list_empty(&ssock->send_pending));
+
+ /* Free slot from the buffer */
+ if (spl->next == wdata && spl->prev == wdata) {
+ /* This is the only data, reset the buffer */
+ buf->start = buf->buf;
+ buf->len = 0;
+ } else if (spl->next == wdata) {
+ /* This is the first data, shift start pointer of the buffer and
+ * adjust the buffer length.
+ */
+ buf->start = (char*)wdata->next;
+ if (wdata->next > wdata) {
+ buf->len -= ((char*)wdata->next - buf->start);
+ } else {
+ /* Overlapped */
+ pj_size_t right_len, left_len;
+ right_len = buf->buf + buf->max_len - (char*)wdata;
+ left_len = (char*)wdata->next - buf->buf;
+ buf->len -= (right_len + left_len);
+ }
+ } else if (spl->prev == wdata) {
+ /* This is the last data, just adjust the buffer length */
+ if (wdata->prev < wdata) {
+ pj_size_t jump_len;
+ jump_len = (char*)wdata -
+ ((char*)wdata->prev + wdata->prev->record_len);
+ buf->len -= (wdata->record_len + jump_len);
+ } else {
+ /* Overlapped */
+ pj_size_t right_len, left_len;
+ right_len = buf->buf + buf->max_len -
+ ((char*)wdata->prev + wdata->prev->record_len);
+ left_len = (char*)wdata + wdata->record_len - buf->buf;
+ buf->len -= (right_len + left_len);
+ }
+ }
+ /* For data in the middle buffer, just do nothing on the buffer. The slot
+ * will be freed later when freeing the first/last data. */
+
+ /* Remove the data from send pending list */
+ pj_list_erase(wdata);
+}
+
+#if 0
+/* Just for testing send buffer alloc/free */
+#include <pj/rand.h>
+pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool)
+{
+ enum { MAX_CHUNK_NUM = 20 };
+ unsigned chunk_size, chunk_cnt, i;
+ write_data_t *wdata[MAX_CHUNK_NUM] = {0};
+ pj_time_val now;
+ pj_ssl_sock_t *ssock = NULL;
+ pj_ssl_sock_param param;
+ pj_status_t status;
+
+ pj_gettimeofday(&now);
+ pj_srand((unsigned)now.sec);
+
+ pj_ssl_sock_param_default(&param);
+ status = pj_ssl_sock_create(pool, &param, &ssock);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ if (ssock->send_buf.max_len == 0) {
+ ssock->send_buf.buf = (char *)
+ pj_pool_alloc(ssock->pool,
+ ssock->param.send_buffer_size);
+ ssock->send_buf.max_len = ssock->param.send_buffer_size;
+ ssock->send_buf.start = ssock->send_buf.buf;
+ ssock->send_buf.len = 0;
+ }
+
+ chunk_size = ssock->param.send_buffer_size / MAX_CHUNK_NUM / 2;
+ chunk_cnt = 0;
+ for (i = 0; i < MAX_CHUNK_NUM; i++) {
+ wdata[i] = alloc_send_data(ssock, pj_rand() % chunk_size + 321);
+ if (wdata[i])
+ chunk_cnt++;
+ else
+ break;
+ }
+
+ while (chunk_cnt) {
+ i = pj_rand() % MAX_CHUNK_NUM;
+ if (wdata[i]) {
+ free_send_data(ssock, wdata[i]);
+ wdata[i] = NULL;
+ chunk_cnt--;
+ }
+ }
+
+ if (ssock->send_buf.len != 0)
+ status = PJ_EBUG;
+
+ pj_ssl_sock_close(ssock);
+ return status;
+}
+#endif
+
+/* Flush write circular buffer to network socket. */
+static pj_status_t flush_circ_buf_output(pj_ssl_sock_t *ssock,
+ pj_ioqueue_op_key_t *send_key,
+ pj_size_t orig_len, unsigned flags)
+{
+ pj_ssize_t len;
+ write_data_t *wdata;
+ pj_size_t needed_len;
+ pj_status_t status;
+
+ pj_lock_acquire(ssock->circ_buf_output_mutex);
+
+ /* Check if there is data in the circular buffer, flush it if any */
+ if (circ_empty(&ssock->circ_buf_output)) {
+ pj_lock_release(ssock->circ_buf_output_mutex);
+
+ return PJ_SUCCESS;
+ }
+
+ len = circ_size(&ssock->circ_buf_output);
+
+ /* Calculate buffer size needed, and align it to 8 */
+ needed_len = len + sizeof(write_data_t);
+ needed_len = ((needed_len + 7) >> 3) << 3;
+
+ /* Allocate buffer for send data */
+ wdata = alloc_send_data(ssock, needed_len);
+ if (wdata == NULL) {
+ pj_lock_release(ssock->circ_buf_output_mutex);
+ return PJ_ENOMEM;
+ }
+
+ /* Copy the data and set its properties into the send data */
+ pj_ioqueue_op_key_init(&wdata->key, sizeof(pj_ioqueue_op_key_t));
+ wdata->key.user_data = wdata;
+ wdata->app_key = send_key;
+ wdata->record_len = needed_len;
+ wdata->data_len = len;
+ wdata->plain_data_len = orig_len;
+ wdata->flags = flags;
+ circ_read(&ssock->circ_buf_output, (pj_uint8_t *)&wdata->data, len);
+
+ /* Ticket #1573: Don't hold mutex while calling PJLIB socket send(). */
+ pj_lock_release(ssock->circ_buf_output_mutex);
+
+ /* Send it */
+ if (ssock->param.sock_type == pj_SOCK_STREAM()) {
+ status = pj_activesock_send(ssock->asock, &wdata->key,
+ wdata->data.content, &len,
+ flags);
+ } else {
+ status = pj_activesock_sendto(ssock->asock, &wdata->key,
+ wdata->data.content, &len,
+ flags,
+ (pj_sockaddr_t*)&ssock->rem_addr,
+ ssock->addr_len);
+ }
+
+ if (status != PJ_EPENDING) {
+ /* When the sending is not pending, remove the wdata from send
+ * pending list. */
+ pj_lock_acquire(ssock->circ_buf_output_mutex);
+ free_send_data(ssock, wdata);
+ pj_lock_release(ssock->circ_buf_output_mutex);
+ }
+
+ return status;
+}
+
+static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te)
+{
+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)te->user_data;
+ int timer_id = te->id;
+
+ te->id = TIMER_NONE;
+
+ PJ_UNUSED_ARG(th);
+
+ switch (timer_id) {
+ case TIMER_HANDSHAKE_TIMEOUT:
+ PJ_LOG(1, (ssock->pool->obj_name, "TLS timeout after %d.%ds",
+ ssock->param.timeout.sec, ssock->param.timeout.msec));
+
+ on_handshake_complete(ssock, PJ_ETIMEDOUT);
+ break;
+ case TIMER_CLOSE:
+ pj_ssl_sock_close(ssock);
+ break;
+ default:
+ pj_assert(!"Unknown timer");
+ break;
+ }
+}
+
+
+/* Try to perform an asynchronous handshake */
+static pj_status_t tls_try_handshake(pj_ssl_sock_t *ssock)
+{
+ int ret;
+ pj_status_t status;
+
+ /* Perform SSL handshake */
+ ret = gnutls_handshake(ssock->session);
+
+ status = flush_circ_buf_output(ssock, &ssock->handshake_op_key, 0, 0);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (ret == GNUTLS_E_SUCCESS) {
+ /* System are GO */
+ ssock->connection_state = TLS_STATE_ESTABLISHED;
+ status = PJ_SUCCESS;
+ } else if (!gnutls_error_is_fatal(ret)) {
+ /* Non fatal error, retry later (busy or again) */
+ status = PJ_EPENDING;
+ } else {
+ /* Fatal error invalidates session, no fallback */
+ status = PJ_EINVAL;
+ }
+
+ tls_last_error = ret;
+
+ return status;
+}
+
+
+/*
+ *******************************************************************
+ * Active socket callbacks.
+ *******************************************************************
+ */
+
+/* PJ_TRUE asks the socket to read more data, PJ_FALSE takes it off the queue */
+static pj_bool_t asock_on_data_read(pj_activesock_t *asock, void *data,
+ pj_size_t size, pj_status_t status,
+ pj_size_t *remainder)
+{
+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)
+ pj_activesock_get_user_data(asock);
+
+ pj_size_t app_remainder = 0;
+
+ if (data && size > 0) {
+ /* Push data into input circular buffer (for GnuTLS) */
+ pj_lock_acquire(ssock->circ_buf_input_mutex);
+ circ_write(&ssock->circ_buf_input, data, size);
+ pj_lock_release(ssock->circ_buf_input_mutex);
+ }
+
+ /* Check if SSL handshake hasn't finished yet */
+ if (ssock->connection_state == TLS_STATE_HANDSHAKING) {
+ pj_bool_t ret = PJ_TRUE;
+
+ if (status == PJ_SUCCESS)
+ status = tls_try_handshake(ssock);
+
+ /* Not pending is either success or failed */
+ if (status != PJ_EPENDING)
+ ret = on_handshake_complete(ssock, status);
+
+ return ret;
+ }
+
+ /* See if there is any decrypted data for the application */
+ if (ssock->read_started) {
+ do {
+ /* Get read data structure at the end of the data */
+ read_data_t *app_read_data = *(OFFSET_OF_READ_DATA_PTR(ssock, data));
+ int app_data_size = (int)(ssock->read_size - app_read_data->len);
+
+ /* Decrypt received data using GnuTLS (will read our input
+ * circular buffer) */
+ int decrypted_size = gnutls_record_recv(ssock->session,
+ app_read_data->data +
+ app_read_data->len,
+ app_data_size);
+
+ if (decrypted_size > 0 || status != PJ_SUCCESS) {
+ if (ssock->param.cb.on_data_read) {
+ pj_bool_t ret;
+ app_remainder = 0;
+
+ if (decrypted_size > 0)
+ app_read_data->len += decrypted_size;
+
+ ret = (*ssock->param.cb.on_data_read)(ssock,
+ app_read_data->data,
+ app_read_data->len,
+ status,
+ &app_remainder);
+
+ if (!ret) {
+ /* We've been destroyed */
+ return PJ_FALSE;
+ }
+
+ /* Application may have left some data to be consumed
+ * later as remainder */
+ app_read_data->len = app_remainder;
+ }
+
+ /* Active socket signalled connection closed/error, this has
+ * been signalled to the application along with any remaining
+ * buffer. So, let's just reset SSL socket now. */
+ if (status != PJ_SUCCESS) {
+ tls_sock_reset(ssock);
+ return PJ_FALSE;
+ }
+ } else if (decrypted_size == 0) {
+ /* Nothing more to read */
+
+ return PJ_TRUE;
+ } else if (decrypted_size == GNUTLS_E_AGAIN ||
+ decrypted_size == GNUTLS_E_INTERRUPTED) {
+ return PJ_TRUE;
+ } else if (decrypted_size == GNUTLS_E_REHANDSHAKE) {
+ /* Seems like we are renegotiating */
+ pj_status_t try_handshake_status = tls_try_handshake(ssock);
+
+ /* Not pending is either success or failed */
+ if (try_handshake_status != PJ_EPENDING) {
+ if (!on_handshake_complete(ssock, try_handshake_status)) {
+ return PJ_FALSE;
+ }
+ }
+
+ if (try_handshake_status != PJ_SUCCESS &&
+ try_handshake_status != PJ_EPENDING) {
+ return PJ_FALSE;
+ }
+ } else if (!gnutls_error_is_fatal(decrypted_size)) {
+ /* non-fatal error, let's just continue */
+ } else {
+ return PJ_FALSE;
+ }
+ } while (PJ_TRUE);
+ }
+
+ return PJ_TRUE;
+}
+
+
+/* Callback every time new data is available from the active socket */
+static pj_bool_t asock_on_data_sent(pj_activesock_t *asock,
+ pj_ioqueue_op_key_t *send_key,
+ pj_ssize_t sent)
+{
+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)pj_activesock_get_user_data(asock);
+
+ PJ_UNUSED_ARG(send_key);
+ PJ_UNUSED_ARG(sent);
+
+ if (ssock->connection_state == TLS_STATE_HANDSHAKING) {
+ /* Initial handshaking */
+ pj_status_t status = tls_try_handshake(ssock);
+
+ /* Not pending is either success or failed */
+ if (status != PJ_EPENDING)
+ return on_handshake_complete(ssock, status);
+
+ } else if (send_key != &ssock->handshake_op_key) {
+ /* Some data has been sent, notify application */
+ write_data_t *wdata = (write_data_t*)send_key->user_data;
+ if (ssock->param.cb.on_data_sent) {
+ pj_bool_t ret;
+ pj_ssize_t sent_len;
+
+ sent_len = sent > 0 ? wdata->plain_data_len : sent;
+
+ ret = (*ssock->param.cb.on_data_sent)(ssock, wdata->app_key,
+ sent_len);
+ if (!ret) {
+ /* We've been destroyed */
+ return PJ_FALSE;
+ }
+ }
+
+ /* Update write buffer state */
+ pj_lock_acquire(ssock->circ_buf_output_mutex);
+ free_send_data(ssock, wdata);
+ pj_lock_release(ssock->circ_buf_output_mutex);
+ } else {
+ /* SSL re-negotiation is on-progress, just do nothing */
+ /* FIXME: check if this is valid for GnuTLS too */
+ }
+
+ return PJ_TRUE;
+}
+
+
+/* Callback every time a new connection has been accepted (server) */
+static pj_bool_t asock_on_accept_complete(pj_activesock_t *asock,
+ pj_sock_t newsock,
+ const pj_sockaddr_t *src_addr,
+ int src_addr_len)
+{
+ pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t *)
+ pj_activesock_get_user_data(asock);
+
+ pj_ssl_sock_t *ssock;
+ pj_activesock_cb asock_cb;
+ pj_activesock_cfg asock_cfg;
+ unsigned int i;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(src_addr_len);
+
+ /* Create new SSL socket instance */
+ status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->param,
+ &ssock);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Update new SSL socket attributes */
+ ssock->sock = newsock;
+ ssock->parent = ssock_parent;
+ ssock->is_server = PJ_TRUE;
+ if (ssock_parent->cert) {
+ status = pj_ssl_sock_set_certificate(ssock, ssock->pool,
+ ssock_parent->cert);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+ }
+
+ /* Apply QoS, if specified */
+ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
+ &ssock->param.qos_params, 1,
+ ssock->pool->obj_name, NULL);
+ if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
+ goto on_return;
+
+ /* Update local address */
+ ssock->addr_len = src_addr_len;
+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
+ &ssock->addr_len);
+ if (status != PJ_SUCCESS) {
+ /* This fails on few envs, e.g: win IOCP, just tolerate this and
+ * use parent local address instead.
+ */
+ pj_sockaddr_cp(&ssock->local_addr, &ssock_parent->local_addr);
+ }
+
+ /* Set remote address */
+ pj_sockaddr_cp(&ssock->rem_addr, src_addr);
+
+ /* Create SSL context */
+ status = tls_open(ssock);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Prepare read buffer */
+ ssock->asock_rbuf = (void **)pj_pool_calloc(ssock->pool,
+ ssock->param.async_cnt,
+ sizeof(void*));
+ if (!ssock->asock_rbuf)
+ return PJ_ENOMEM;
+
+ for (i = 0; i < ssock->param.async_cnt; ++i) {
+ ssock->asock_rbuf[i] = (void *)pj_pool_alloc(
+ ssock->pool,
+ ssock->param.read_buffer_size +
+ sizeof(read_data_t*));
+ if (!ssock->asock_rbuf[i])
+ return PJ_ENOMEM;
+ }
+
+ /* Create active socket */
+ pj_activesock_cfg_default(&asock_cfg);
+ asock_cfg.async_cnt = ssock->param.async_cnt;
+ asock_cfg.concurrency = ssock->param.concurrency;
+ asock_cfg.whole_data = PJ_TRUE;
+
+ pj_bzero(&asock_cb, sizeof(asock_cb));
+ asock_cb.on_data_read = asock_on_data_read;
+ asock_cb.on_data_sent = asock_on_data_sent;
+
+ status = pj_activesock_create(ssock->pool,
+ ssock->sock,
+ ssock->param.sock_type,
+ &asock_cfg,
+ ssock->param.ioqueue,
+ &asock_cb,
+ ssock,
+ &ssock->asock);
+
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Start reading */
+ status = pj_activesock_start_read2(ssock->asock, ssock->pool,
+ (unsigned)ssock->param.read_buffer_size,
+ ssock->asock_rbuf,
+ PJ_IOQUEUE_ALWAYS_ASYNC);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Prepare write/send state */
+ pj_assert(ssock->send_buf.max_len == 0);
+ ssock->send_buf.buf = (char *)pj_pool_alloc(ssock->pool,
+ ssock->param.send_buffer_size);
+ if (!ssock->send_buf.buf)
+ return PJ_ENOMEM;
+
+ ssock->send_buf.max_len = ssock->param.send_buffer_size;
+ ssock->send_buf.start = ssock->send_buf.buf;
+ ssock->send_buf.len = 0;
+
+ /* Start handshake timer */
+ if (ssock->param.timer_heap &&
+ (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0)) {
+ pj_assert(ssock->timer.id == TIMER_NONE);
+ ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
+ status = pj_timer_heap_schedule(ssock->param.timer_heap,
+ &ssock->timer,
+ &ssock->param.timeout);
+ if (status != PJ_SUCCESS)
+ ssock->timer.id = TIMER_NONE;
+ }
+
+ /* Start SSL handshake */
+ ssock->connection_state = TLS_STATE_HANDSHAKING;
+
+ status = tls_try_handshake(ssock);
+
+on_return:
+ if (ssock && status != PJ_EPENDING)
+ on_handshake_complete(ssock, status);
+
+ /* Must return PJ_TRUE whatever happened, as active socket must
+ * continue listening.
+ */
+ return PJ_TRUE;
+}
+
+
+/* Callback every time a new connection has been completed (client) */
+static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock,
+ pj_status_t status)
+{
+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
+ pj_activesock_get_user_data(asock);
+
+ unsigned int i;
+ int ret;
+
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Update local address */
+ ssock->addr_len = sizeof(pj_sockaddr);
+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
+ &ssock->addr_len);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Create SSL context */
+ status = tls_open(ssock);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Prepare read buffer */
+ ssock->asock_rbuf = (void **)pj_pool_calloc(ssock->pool,
+ ssock->param.async_cnt,
+ sizeof(void *));
+ if (!ssock->asock_rbuf)
+ return PJ_ENOMEM;
+
+ for (i = 0; i < ssock->param.async_cnt; ++i) {
+ ssock->asock_rbuf[i] = (void *)pj_pool_alloc(
+ ssock->pool,
+ ssock->param.read_buffer_size +
+ sizeof(read_data_t *));
+ if (!ssock->asock_rbuf[i])
+ return PJ_ENOMEM;
+ }
+
+ /* Start read */
+ status = pj_activesock_start_read2(ssock->asock, ssock->pool,
+ (unsigned) ssock->param.read_buffer_size,
+ ssock->asock_rbuf,
+ PJ_IOQUEUE_ALWAYS_ASYNC);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Prepare write/send state */
+ pj_assert(ssock->send_buf.max_len == 0);
+ ssock->send_buf.buf = (char *)pj_pool_alloc(ssock->pool,
+ ssock->param.send_buffer_size);
+ if (!ssock->send_buf.buf)
+ return PJ_ENOMEM;
+
+ ssock->send_buf.max_len = ssock->param.send_buffer_size;
+ ssock->send_buf.start = ssock->send_buf.buf;
+ ssock->send_buf.len = 0;
+
+ /* Set server name to connect */
+ if (ssock->param.server_name.slen) {
+ /* Server name is null terminated already */
+ ret = gnutls_server_name_set(ssock->session, GNUTLS_NAME_DNS,
+ ssock->param.server_name.ptr,
+ ssock->param.server_name.slen);
+ if (ret < 0) {
+ PJ_LOG(3, (ssock->pool->obj_name,
+ "gnutls_server_name_set() failed: %s",
+ gnutls_strerror(ret)));
+ }
+ }
+
+ /* Start handshake */
+ ssock->connection_state = TLS_STATE_HANDSHAKING;
+
+ status = tls_try_handshake(ssock);
+ if (status != PJ_EPENDING)
+ goto on_return;
+
+ return PJ_TRUE;
+
+on_return:
+ return on_handshake_complete(ssock, status);
+}
+
+static void tls_ciphers_fill(void)
+{
+ if (!tls_available_ciphers) {
+ tls_init();
+ tls_deinit();
+ }
+}
+
+/*
+ *******************************************************************
+ * API
+ *******************************************************************
+ */
+
+/* Load credentials from files. */
+PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool,
+ const pj_str_t *CA_file,
+ const pj_str_t *cert_file,
+ const pj_str_t *privkey_file,
+ const pj_str_t *privkey_pass,
+ pj_ssl_cert_t **p_cert)
+{
+ pj_ssl_cert_t *cert;
+
+ PJ_ASSERT_RETURN(pool && CA_file && cert_file && privkey_file, PJ_EINVAL);
+
+ cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
+ pj_strdup_with_null(pool, &cert->CA_file, CA_file);
+ pj_strdup_with_null(pool, &cert->cert_file, cert_file);
+ pj_strdup_with_null(pool, &cert->privkey_file, privkey_file);
+ pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass);
+
+ *p_cert = cert;
+
+ return PJ_SUCCESS;
+}
+
+
+/* Store credentials. */
+PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate(pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ const pj_ssl_cert_t *cert)
+{
+ pj_ssl_cert_t *cert_;
+
+ PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL);
+
+ cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
+ pj_memcpy(cert_, cert, sizeof(cert));
+ pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file);
+ pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file);
+ pj_strdup_with_null(pool, &cert_->privkey_file, &cert->privkey_file);
+ pj_strdup_with_null(pool, &cert_->privkey_pass, &cert->privkey_pass);
+
+ ssock->cert = cert_;
+
+ return PJ_SUCCESS;
+}
+
+
+/* Get available ciphers. */
+PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[],
+ unsigned *cipher_num)
+{
+ unsigned int i;
+
+ PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL);
+
+ tls_ciphers_fill();
+
+ if (!tls_available_ciphers) {
+ *cipher_num = 0;
+ return PJ_ENOTFOUND;
+ }
+
+ *cipher_num = PJ_MIN(*cipher_num, tls_available_ciphers);
+
+ for (i = 0; i < *cipher_num; ++i)
+ ciphers[i] = tls_ciphers[i].id;
+
+ return PJ_SUCCESS;
+}
+
+
+/* Get cipher name string. */
+PJ_DEF(const char *)pj_ssl_cipher_name(pj_ssl_cipher cipher)
+{
+ unsigned int i;
+
+ tls_ciphers_fill();
+
+ for (i = 0; i < tls_available_ciphers; ++i) {
+ if (cipher == tls_ciphers[i].id)
+ return tls_ciphers[i].name;
+ }
+
+ return NULL;
+}
+
+
+/* Get cipher identifier. */
+PJ_DEF(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name)
+{
+ unsigned int i;
+
+ tls_ciphers_fill();
+
+ for (i = 0; i < tls_available_ciphers; ++i) {
+ if (!pj_ansi_stricmp(tls_ciphers[i].name, cipher_name))
+ return tls_ciphers[i].id;
+ }
+
+ return PJ_TLS_UNKNOWN_CIPHER;
+}
+
+
+/* Check if the specified cipher is supported by the TLS backend. */
+PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher)
+{
+ unsigned int i;
+
+ tls_ciphers_fill();
+
+ for (i = 0; i < tls_available_ciphers; ++i) {
+ if (cipher == tls_ciphers[i].id)
+ return PJ_TRUE;
+ }
+
+ return PJ_FALSE;
+}
+
+/* Create SSL socket instance. */
+PJ_DEF(pj_status_t) pj_ssl_sock_create(pj_pool_t *pool,
+ const pj_ssl_sock_param *param,
+ pj_ssl_sock_t **p_ssock)
+{
+ pj_ssl_sock_t *ssock;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL);
+ PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP);
+
+ pool = pj_pool_create(pool->factory, "tls%p", 512, 512, NULL);
+
+ /* Create secure socket */
+ ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t);
+ ssock->pool = pool;
+ ssock->sock = PJ_INVALID_SOCKET;
+ ssock->connection_state = TLS_STATE_NULL;
+ pj_list_init(&ssock->write_pending);
+ pj_list_init(&ssock->write_pending_empty);
+ pj_list_init(&ssock->send_pending);
+ pj_timer_entry_init(&ssock->timer, 0, ssock, &on_timer);
+ pj_ioqueue_op_key_init(&ssock->handshake_op_key,
+ sizeof(pj_ioqueue_op_key_t));
+
+ /* Create secure socket mutex */
+ status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
+ &ssock->circ_buf_output_mutex);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create input circular buffer mutex */
+ status = pj_lock_create_simple_mutex(pool, pool->obj_name,
+ &ssock->circ_buf_input_mutex);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create output circular buffer mutex */
+ status = pj_lock_create_simple_mutex(pool, pool->obj_name,
+ &ssock->circ_buf_output_mutex);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Init secure socket param */
+ ssock->param = *param;
+ ssock->param.read_buffer_size = ((ssock->param.read_buffer_size + 7) >> 3) << 3;
+
+ if (param->ciphers_num > 0) {
+ unsigned int i;
+ ssock->param.ciphers = (pj_ssl_cipher *)
+ pj_pool_calloc(pool, param->ciphers_num,
+ sizeof(pj_ssl_cipher));
+ if (!ssock->param.ciphers)
+ return PJ_ENOMEM;
+
+ for (i = 0; i < param->ciphers_num; ++i)
+ ssock->param.ciphers[i] = param->ciphers[i];
+ }
+
+ /* Server name must be null-terminated */
+ pj_strdup_with_null(pool, &ssock->param.server_name, &param->server_name);
+
+ /* Finally */
+ *p_ssock = ssock;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Close the secure socket. This will unregister the socket from the
+ * ioqueue and ultimately close the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock)
+{
+ pj_pool_t *pool;
+
+ PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+
+ if (!ssock->pool)
+ return PJ_SUCCESS;
+
+ if (ssock->timer.id != TIMER_NONE) {
+ pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
+ ssock->timer.id = TIMER_NONE;
+ }
+
+ tls_sock_reset(ssock);
+
+ pj_lock_destroy(ssock->circ_buf_output_mutex);
+ pj_lock_destroy(ssock->circ_buf_input_mutex);
+
+ pool = ssock->pool;
+ ssock->pool = NULL;
+ if (pool)
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+
+/* Associate arbitrary data with the secure socket. */
+PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock,
+ void *user_data)
+{
+ PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+
+ ssock->param.user_data = user_data;
+ return PJ_SUCCESS;
+}
+
+
+/* Retrieve the user data previously associated with this secure socket. */
+PJ_DEF(void *)pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
+{
+ PJ_ASSERT_RETURN(ssock, NULL);
+
+ return ssock->param.user_data;
+}
+
+
+/* Retrieve the local address and port used by specified SSL socket. */
+PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
+ pj_ssl_sock_info *info)
+{
+ pj_bzero(info, sizeof(*info));
+
+ /* Established flag */
+ info->established = (ssock->connection_state == TLS_STATE_ESTABLISHED);
+
+ /* Protocol */
+ info->proto = ssock->param.proto;
+
+ /* Local address */
+ pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
+
+ if (info->established) {
+ int i;
+ gnutls_cipher_algorithm_t lookup;
+ gnutls_cipher_algorithm_t cipher;
+
+ /* Current cipher */
+ cipher = gnutls_cipher_get(ssock->session);
+ for (i = 0; ; i++) {
+ unsigned char id[2];
+ const char *suite = gnutls_cipher_suite_info(i, (unsigned char *)id,
+ NULL, &lookup, NULL,
+ NULL);
+ if (suite) {
+ if (lookup == cipher) {
+ info->cipher = (pj_uint32_t) ((id[0] << 8) | id[1]);
+ break;
+ }
+ } else
+ break;
+ }
+
+ /* Remote address */
+ pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr);
+
+ /* Certificates info */
+ info->local_cert_info = &ssock->local_cert_info;
+ info->remote_cert_info = &ssock->remote_cert_info;
+
+ /* Verification status */
+ info->verify_status = ssock->verify_status;
+ }
+
+ /* Last known GnuTLS error code */
+ info->last_native_err = ssock->last_err;
+
+ return PJ_SUCCESS;
+}
+
+
+/* Starts read operation on this secure socket. */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read(pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ unsigned buff_size,
+ pj_uint32_t flags)
+{
+ void **readbuf;
+ unsigned int i;
+
+ PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL);
+ PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED,
+ PJ_EINVALIDOP);
+
+ readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt,
+ sizeof(void *));
+ if (!readbuf)
+ return PJ_ENOMEM;
+
+ for (i = 0; i < ssock->param.async_cnt; ++i) {
+ readbuf[i] = pj_pool_alloc(pool, buff_size);
+ if (!readbuf[i])
+ return PJ_ENOMEM;
+ }
+
+ return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags);
+}
+
+
+/*
+ * Same as #pj_ssl_sock_start_read(), except that the application
+ * supplies the buffers for the read operation so that the acive socket
+ * does not have to allocate the buffers.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ unsigned buff_size,
+ void *readbuf[],
+ pj_uint32_t flags)
+{
+ unsigned int i;
+
+ PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL);
+ PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED,
+ PJ_EINVALIDOP);
+
+ /* Create SSL socket read buffer */
+ ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool,
+ ssock->param.async_cnt,
+ sizeof(read_data_t));
+ if (!ssock->ssock_rbuf)
+ return PJ_ENOMEM;
+
+ /* Store SSL socket read buffer pointer in the activesock read buffer */
+ for (i = 0; i < ssock->param.async_cnt; ++i) {
+ read_data_t **p_ssock_rbuf =
+ OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]);
+
+ ssock->ssock_rbuf[i].data = readbuf[i];
+ ssock->ssock_rbuf[i].len = 0;
+
+ *p_ssock_rbuf = &ssock->ssock_rbuf[i];
+ }
+
+ ssock->read_size = buff_size;
+ ssock->read_started = PJ_TRUE;
+ ssock->read_flags = flags;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Same as pj_ssl_sock_start_read(), except that this function is used
+ * only for datagram sockets, and it will trigger \a on_data_recvfrom()
+ * callback instead.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ unsigned buff_size,
+ pj_uint32_t flags)
+{
+ PJ_UNUSED_ARG(ssock);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(buff_size);
+ PJ_UNUSED_ARG(flags);
+
+ return PJ_ENOTSUP;
+}
+
+
+/*
+ * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom()
+ * operation takes the buffer from the argument rather than creating
+ * new ones.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ unsigned buff_size,
+ void *readbuf[],
+ pj_uint32_t flags)
+{
+ PJ_UNUSED_ARG(ssock);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(buff_size);
+ PJ_UNUSED_ARG(readbuf);
+ PJ_UNUSED_ARG(flags);
+
+ return PJ_ENOTSUP;
+}
+
+
+/*
+ * Write the plain data to GnuTLS, it will be encrypted by gnutls_record_send()
+ * and sent via tls_data_push. Note that re-negotitation may be on progress, so
+ * sending data should be delayed until re-negotiation is completed.
+ */
+static pj_status_t tls_write(pj_ssl_sock_t *ssock,
+ pj_ioqueue_op_key_t *send_key,
+ const void *data, pj_ssize_t size, unsigned flags)
+{
+ pj_status_t status;
+ int nwritten;
+ pj_ssize_t total_written = 0;
+
+ /* Ask GnuTLS to encrypt our plaintext now. GnuTLS will use the push
+ * callback to actually write the encrypted bytes into our output circular
+ * buffer. GnuTLS may refuse to "send" everything at once, but since we are
+ * not really sending now, we will just call it again now until it succeeds
+ * (or fails in a fatal way). */
+ while (total_written < size) {
+ /* Try encrypting using GnuTLS */
+ nwritten = gnutls_record_send(ssock->session, data + total_written,
+ size);
+
+ if (nwritten > 0) {
+ /* Good, some data was encrypted and written */
+ total_written += nwritten;
+ } else {
+ /* Normally we would have to retry record_send but our internal
+ * state has not changed, so we have to ask for more data first.
+ * We will just try again later, although this should never happen.
+ */
+ return tls_status_from_err(ssock, nwritten);
+ }
+ }
+
+ /* All encrypted data is written to the output circular buffer;
+ * now send it on the socket (or notify problem). */
+ if (total_written == size)
+ status = flush_circ_buf_output(ssock, send_key, size, flags);
+ else
+ status = PJ_ENOMEM;
+
+ return status;
+}
+
+
+/* Flush delayed data sending in the write pending list. */
+static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock)
+{
+ /* Check for another ongoing flush */
+ if (ssock->flushing_write_pend) {
+ return PJ_EBUSY;
+ }
+
+ pj_lock_acquire(ssock->circ_buf_output_mutex);
+
+ /* Again, check for another ongoing flush */
+ if (ssock->flushing_write_pend) {
+ pj_lock_release(ssock->circ_buf_output_mutex);
+ return PJ_EBUSY;
+ }
+
+ /* Set ongoing flush flag */
+ ssock->flushing_write_pend = PJ_TRUE;
+
+ while (!pj_list_empty(&ssock->write_pending)) {
+ write_data_t *wp;
+ pj_status_t status;
+
+ wp = ssock->write_pending.next;
+
+ /* Ticket #1573: Don't hold mutex while calling socket send. */
+ pj_lock_release(ssock->circ_buf_output_mutex);
+
+ status = tls_write(ssock, &wp->key, wp->data.ptr,
+ wp->plain_data_len, wp->flags);
+ if (status != PJ_SUCCESS) {
+ /* Reset ongoing flush flag first. */
+ ssock->flushing_write_pend = PJ_FALSE;
+ return status;
+ }
+
+ pj_lock_acquire(ssock->circ_buf_output_mutex);
+ pj_list_erase(wp);
+ pj_list_push_back(&ssock->write_pending_empty, wp);
+ }
+
+ /* Reset ongoing flush flag */
+ ssock->flushing_write_pend = PJ_FALSE;
+
+ pj_lock_release(ssock->circ_buf_output_mutex);
+
+ return PJ_SUCCESS;
+}
+
+
+/* Sending is delayed, push back the sending data into pending list. */
+static pj_status_t delay_send(pj_ssl_sock_t *ssock,
+ pj_ioqueue_op_key_t *send_key,
+ const void *data, pj_ssize_t size,
+ unsigned flags)
+{
+ write_data_t *wp;
+
+ pj_lock_acquire(ssock->circ_buf_output_mutex);
+
+ /* Init write pending instance */
+ if (!pj_list_empty(&ssock->write_pending_empty)) {
+ wp = ssock->write_pending_empty.next;
+ pj_list_erase(wp);
+ } else {
+ wp = PJ_POOL_ZALLOC_T(ssock->pool, write_data_t);
+ }
+
+ wp->app_key = send_key;
+ wp->plain_data_len = size;
+ wp->data.ptr = data;
+ wp->flags = flags;
+
+ pj_list_push_back(&ssock->write_pending, wp);
+
+ pj_lock_release(ssock->circ_buf_output_mutex);
+
+ /* Must return PJ_EPENDING */
+ return PJ_EPENDING;
+}
+
+
+/**
+ * Send data using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_send(pj_ssl_sock_t *ssock,
+ pj_ioqueue_op_key_t *send_key,
+ const void *data, pj_ssize_t *size,
+ unsigned flags)
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(ssock && data && size && (*size > 0), PJ_EINVAL);
+ PJ_ASSERT_RETURN(ssock->connection_state==TLS_STATE_ESTABLISHED,
+ PJ_EINVALIDOP);
+
+ /* Flush delayed send first. Sending data might be delayed when
+ * re-negotiation is on-progress. */
+ status = flush_delayed_send(ssock);
+ if (status == PJ_EBUSY) {
+ /* Re-negotiation or flushing is on progress, delay sending */
+ status = delay_send(ssock, send_key, data, *size, flags);
+ goto on_return;
+ } else if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Write data to SSL */
+ status = tls_write(ssock, send_key, data, *size, flags);
+ if (status == PJ_EBUSY) {
+ /* Re-negotiation is on progress, delay sending */
+ status = delay_send(ssock, send_key, data, *size, flags);
+ }
+
+on_return:
+ return status;
+}
+
+
+/**
+ * Send datagram using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock,
+ pj_ioqueue_op_key_t *send_key,
+ const void *data, pj_ssize_t *size,
+ unsigned flags,
+ const pj_sockaddr_t *addr, int addr_len)
+{
+ PJ_UNUSED_ARG(ssock);
+ PJ_UNUSED_ARG(send_key);
+ PJ_UNUSED_ARG(data);
+ PJ_UNUSED_ARG(size);
+ PJ_UNUSED_ARG(flags);
+ PJ_UNUSED_ARG(addr);
+ PJ_UNUSED_ARG(addr_len);
+
+ return PJ_ENOTSUP;
+}
+
+
+/**
+ * Starts asynchronous socket accept() operations on this secure socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ const pj_sockaddr_t *localaddr,
+ int addr_len)
+{
+ pj_activesock_cb asock_cb;
+ pj_activesock_cfg asock_cfg;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL);
+
+ /* Create socket */
+ status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0,
+ &ssock->sock);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Apply SO_REUSEADDR */
+ if (ssock->param.reuse_addr) {
+ int enabled = 1;
+ status = pj_sock_setsockopt(ssock->sock, pj_SOL_SOCKET(),
+ pj_SO_REUSEADDR(),
+ &enabled, sizeof(enabled));
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(4,(ssock->pool->obj_name, status,
+ "Warning: error applying SO_REUSEADDR"));
+ }
+ }
+
+ /* Apply QoS, if specified */
+ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
+ &ssock->param.qos_params, 2,
+ ssock->pool->obj_name, NULL);
+ if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
+ goto on_error;
+
+ /* Bind socket */
+ status = pj_sock_bind(ssock->sock, localaddr, addr_len);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Start listening to the address */
+ status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Create active socket */
+ pj_activesock_cfg_default(&asock_cfg);
+ asock_cfg.async_cnt = ssock->param.async_cnt;
+ asock_cfg.concurrency = ssock->param.concurrency;
+ asock_cfg.whole_data = PJ_TRUE;
+
+ pj_bzero(&asock_cb, sizeof(asock_cb));
+ asock_cb.on_accept_complete = asock_on_accept_complete;
+
+ status = pj_activesock_create(pool,
+ ssock->sock,
+ ssock->param.sock_type,
+ &asock_cfg,
+ ssock->param.ioqueue,
+ &asock_cb,
+ ssock,
+ &ssock->asock);
+
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Start accepting */
+ status = pj_activesock_start_accept(ssock->asock, pool);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Update local address */
+ ssock->addr_len = addr_len;
+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
+ &ssock->addr_len);
+ if (status != PJ_SUCCESS)
+ pj_sockaddr_cp(&ssock->local_addr, localaddr);
+
+ ssock->is_server = PJ_TRUE;
+
+ return PJ_SUCCESS;
+
+on_error:
+ tls_sock_reset(ssock);
+ return status;
+}
+
+
+/**
+ * Starts asynchronous socket connect() operation.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ const pj_sockaddr_t *localaddr,
+ const pj_sockaddr_t *remaddr,
+ int addr_len)
+{
+ pj_activesock_cb asock_cb;
+ pj_activesock_cfg asock_cfg;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
+ PJ_EINVAL);
+
+ /* Create socket */
+ status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0,
+ &ssock->sock);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Apply QoS, if specified */
+ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
+ &ssock->param.qos_params, 2,
+ ssock->pool->obj_name, NULL);
+ if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
+ goto on_error;
+
+ /* Bind socket */
+ status = pj_sock_bind(ssock->sock, localaddr, addr_len);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Create active socket */
+ pj_activesock_cfg_default(&asock_cfg);
+ asock_cfg.async_cnt = ssock->param.async_cnt;
+ asock_cfg.concurrency = ssock->param.concurrency;
+ asock_cfg.whole_data = PJ_TRUE;
+
+ pj_bzero(&asock_cb, sizeof(asock_cb));
+ asock_cb.on_connect_complete = asock_on_connect_complete;
+ asock_cb.on_data_read = asock_on_data_read;
+ asock_cb.on_data_sent = asock_on_data_sent;
+
+ status = pj_activesock_create(pool,
+ ssock->sock,
+ ssock->param.sock_type,
+ &asock_cfg,
+ ssock->param.ioqueue,
+ &asock_cb,
+ ssock,
+ &ssock->asock);
+
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Save remote address */
+ pj_sockaddr_cp(&ssock->rem_addr, remaddr);
+
+ /* Start timer */
+ if (ssock->param.timer_heap &&
+ (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0))
+ {
+ pj_assert(ssock->timer.id == TIMER_NONE);
+ ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
+ status = pj_timer_heap_schedule(ssock->param.timer_heap,
+ &ssock->timer,
+ &ssock->param.timeout);
+ if (status != PJ_SUCCESS)
+ ssock->timer.id = TIMER_NONE;
+ }
+
+ status = pj_activesock_start_connect(ssock->asock, pool, remaddr,
+ addr_len);
+
+ if (status == PJ_SUCCESS)
+ asock_on_connect_complete(ssock->asock, PJ_SUCCESS);
+ else if (status != PJ_EPENDING)
+ goto on_error;
+
+ /* Update local address */
+ ssock->addr_len = addr_len;
+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
+ &ssock->addr_len);
+ /* Note that we may not get an IP address here. This can
+ * happen for example on Windows, where getsockname()
+ * would return 0.0.0.0 if socket has just started the
+ * async connect. In this case, just leave the local
+ * address with 0.0.0.0 for now; it will be updated
+ * once the socket is established.
+ */
+
+ /* Update socket state */
+ ssock->is_server = PJ_FALSE;
+
+ return PJ_EPENDING;
+
+on_error:
+ tls_sock_reset(ssock);
+ return status;
+}
+
+
+PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock)
+{
+ int status;
+
+ /* Nothing established yet */
+ PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED,
+ PJ_EINVALIDOP);
+
+ /* Cannot renegotiate; we're a client */
+ /* FIXME: in fact maybe that's not true */
+ PJ_ASSERT_RETURN(!ssock->is_server, PJ_EINVALIDOP);
+
+ /* First call gnutls_rehandshake() to see if this is even possible */
+ status = gnutls_rehandshake(ssock->session);
+
+ if (status == GNUTLS_E_SUCCESS) {
+ /* Rehandshake is possible, so try a GnuTLS handshake now. The eventual
+ * gnutls_record_recv() calls could return a few specific values during
+ * this state:
+ *
+ * - GNUTLS_E_REHANDSHAKE: rehandshake message processing
+ * - GNUTLS_E_WARNING_ALERT_RECEIVED: client does not wish to
+ * renegotiate
+ */
+ ssock->connection_state = TLS_STATE_HANDSHAKING;
+ status = tls_try_handshake(ssock);
+
+ return status;
+ } else {
+ return tls_status_from_err(ssock, status);
+ }
+}
+
+#endif /* PJ_HAS_SSL_SOCK */
diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
index 7129f21..ed57d56 100644
--- a/pjlib/src/pj/ssl_sock_ossl.c
+++ b/pjlib/src/pj/ssl_sock_ossl.c
@@ -31,8 +31,10 @@
#include <pj/timer.h>
-/* Only build when PJ_HAS_SSL_SOCK is enabled */
-#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0
+/* Only build when PJ_HAS_SSL_SOCK is enabled and when PJ_HAS_TLS_SOCK is
+ * disabled (meaning GnuTLS is off) */
+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \
+ defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 0
#define THIS_FILE "ssl_sock_ossl.c"
--
1.8.3.2
--- a/pjlib/include/pj/config.h
+++ b/pjlib/include/pj/config.h
@@ -549,7 +549,7 @@
* Default: 0 (disabled, for now)
*/
#ifndef PJ_HAS_IPV6
-# define PJ_HAS_IPV6 0
+# define PJ_HAS_IPV6 1
#endif
/**
diff --git a/Makefile b/Makefile
index 33a4e6b..a486eb7 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ include build/host-$(HOST_NAME).mak
include version.mak
LIB_DIRS = pjlib/build pjlib-util/build pjnath/build third_party/build pjmedia/build pjsip/build
-DIRS = $(LIB_DIRS) pjsip-apps/build $(EXTRA_DIRS)
+DIRS = $(LIB_DIRS) $(EXTRA_DIRS)
ifdef MINSIZE
MAKE_FLAGS := MINSIZE=1
diff --git a/pjlib-util/build/Makefile b/pjlib-util/build/Makefile
index cb601cb..862a78a 100644
--- a/pjlib-util/build/Makefile
+++ b/pjlib-util/build/Makefile
@@ -54,7 +54,6 @@ export UTIL_TEST_OBJS += xml.o encryption.o stun.o resolver_test.o test.o \
export UTIL_TEST_CFLAGS += $(_CFLAGS)
export UTIL_TEST_CXXFLAGS += $(_CXXFLAGS)
export UTIL_TEST_LDFLAGS += $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
-export UTIL_TEST_EXE:=pjlib-util-test-$(TARGET_NAME)$(HOST_EXE)
export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT
diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile
index 1e64950..a75fa65 100644
--- a/pjlib/build/Makefile
+++ b/pjlib/build/Makefile
@@ -56,7 +56,6 @@ export TEST_OBJS += activesock.o atomic.o echo_clt.o errno.o exception.o \
export TEST_CFLAGS += $(_CFLAGS)
export TEST_CXXFLAGS += $(_CXXFLAGS)
export TEST_LDFLAGS += $(PJLIB_LDLIB) $(_LDFLAGS)
-export TEST_EXE := pjlib-test-$(TARGET_NAME)$(HOST_EXE)
export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT
diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile
index 8012cb7..2ca283a 100644
--- a/pjmedia/build/Makefile
+++ b/pjmedia/build/Makefile
@@ -165,7 +165,6 @@ export PJMEDIA_TEST_LDFLAGS += $(PJMEDIA_CODEC_LDLIB) \
$(PJLIB_UTIL_LDLIB) \
$(PJNATH_LDLIB) \
$(_LDFLAGS)
-export PJMEDIA_TEST_EXE:=pjmedia-test-$(TARGET_NAME)$(HOST_EXE)
export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT
diff --git a/pjnath/build/Makefile b/pjnath/build/Makefile
index 1bc08b5..109f79b 100644
--- a/pjnath/build/Makefile
+++ b/pjnath/build/Makefile
@@ -54,7 +54,6 @@ export PJNATH_TEST_OBJS += ice_test.o stun.o sess_auth.o server.o concur_test.o
export PJNATH_TEST_CFLAGS += $(_CFLAGS)
export PJNATH_TEST_CXXFLAGS += $(_CXXFLAGS)
export PJNATH_TEST_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
-export PJNATH_TEST_EXE:=pjnath-test-$(TARGET_NAME)$(HOST_EXE)
###############################################################################
@@ -65,7 +64,6 @@ export PJTURN_CLIENT_OBJS += client_main.o
export PJTURN_CLIENT_CFLAGS += $(_CFLAGS)
export PJTURN_CLIENT_CXXFLAGS += $(_CXXFLAGS)
export PJTURN_CLIENT_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
-export PJTURN_CLIENT_EXE:=pjturn-client-$(TARGET_NAME)$(HOST_EXE)
###############################################################################
# Defines for building TURN server application
@@ -76,7 +74,6 @@ export PJTURN_SRV_OBJS += allocation.o auth.o listener_udp.o \
export PJTURN_SRV_CFLAGS += $(_CFLAGS)
export PJTURN_SRV_CXXFLAGS += $(_CXXFLAGS)
export PJTURN_SRV_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
-export PJTURN_SRV_EXE:=pjturn-srv-$(TARGET_NAME)$(HOST_EXE)
diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile
index d2a5c2a..7e2ec60 100644
--- a/pjsip/build/Makefile
+++ b/pjsip/build/Makefile
@@ -165,7 +165,6 @@ export PJSUA2_TEST_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
export PJSUA2_TEST_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS)
export PJSUA2_TEST_CXXFLAGS = $(PJSUA2_LIB_CFLAGS)
export PJSUA2_TEST_LDFLAGS += $(PJ_LDXXFLAGS) $(PJ_LDXXLIBS) $(LDFLAGS)
-export PJSUA2_TEST_EXE := pjsua2-test-$(TARGET_NAME)$(HOST_EXE)
export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT
@@ -195,7 +194,6 @@ export TEST_LDFLAGS += $(PJSIP_LDLIB) \
$(PJLIB_UTIL_LDLIB) \
$(PJNATH_LDLIB) \
$(_LDFLAGS)
-export TEST_EXE := pjsip-test-$(TARGET_NAME)$(HOST_EXE)
export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT
# PJPROJECT
PJPROJECT_VERSION := 2.2.1
PJPROJECT_URL := http://www.pjsip.org/release/$(PJPROJECT_VERSION)/pjproject-$(PJPROJECT_VERSION).tar.bz2
PJPROJECT_OPTIONS := --disable-oss \
--disable-sound \
--disable-video \
--enable-ext-sound \
--disable-speex-aec \
--disable-g711-codec \
--disable-l16-codec \
--disable-gsm-codec \
--disable-g722-codec \
--disable-g7221-codec \
--disable-speex-codec \
--disable-ilbc-codec \
--disable-opencore-amr \
--disable-sdl \
--disable-ffmpeg \
--disable-v4l2 \
--enable-ssl=gnutls
ifdef HAVE_ANDROID
PJPROJECT_OPTIONS += --with-ssl=$(PREFIX)
endif
PKGS += pjproject
# nominally 2.2.0 is enough, but it has to be patched for gnutls
#ifeq ($(call need_pkg,'libpjproject >= 2.2.0'),)
#PKGS_FOUND += pjproject
#endif
DEPS_pjproject += gnutls
ifndef HAVE_MACOSX
DEPS_pjproject += uuid
endif
$(TARBALLS)/pjproject-$(PJPROJECT_VERSION).tar.bz2:
$(call download,$(PJPROJECT_URL))
.sum-pjproject: pjproject-$(PJPROJECT_VERSION).tar.bz2
pjproject: pjproject-$(PJPROJECT_VERSION).tar.bz2 .sum-pjproject
$(UNPACK)
$(APPLY) $(SRC)/pjproject/aconfigureupdate.patch
$(APPLY) $(SRC)/pjproject/endianness.patch
$(APPLY) $(SRC)/pjproject/unknowncipher.patch
$(APPLY) $(SRC)/pjproject/gnutls.patch
$(APPLY) $(SRC)/pjproject/notestsapps.patch
$(APPLY) $(SRC)/pjproject/ipv6.patch
$(UPDATE_AUTOCONFIG)
$(MOVE)
.pjproject: pjproject
cd $< && $(HOSTVARS) ./aconfigure $(HOSTCONF) $(PJPROJECT_OPTIONS)
cd $< && $(MAKE) && $(MAKE) install
touch $@
--- a/pjlib/include/pj/ssl_sock.h 2013-04-26 02:01:43.000000000 -0400
+++ b/pjlib/include/pj/ssl_sock.h 2014-06-16 18:31:58.464991714 -0400
@@ -243,6 +243,9 @@
*/
typedef enum pj_ssl_cipher {
+ /* Unsupported cipher */
+ PJ_TLS_UNKNOWN_CIPHER = -1,
+
/* NULL */
PJ_TLS_NULL_WITH_NULL_NULL = 0x00000000,
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment