12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296 |
- Copyright (c) 2014-2017 Savoir-faire Linux Inc.
- 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>
- Philippe Proulx <philippe.proulx@savoirfairelinux.com> and
- Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- on behalf of Savoir-faire Linux.
- ---
- diff -ru a/aconfigure b/aconfigure
- --- a/aconfigure 2017-01-25 06:23:08.000000000 -0500
- +++ b/aconfigure 2017-06-08 13:51:11.146810527 -0400
- @@ -644,6 +644,8 @@
- libcrypto_present
- libssl_present
- openssl_h_present
- +libgnutls_present
- +gnutls_h_present
- ac_ssl_has_aes_gcm
- ac_no_ssl
- ac_openh264_ldflags
- @@ -1482,8 +1484,8 @@
- 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)
-
- @@ -7787,17 +7789,149 @@
-
- # 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 $host-pkg-config 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 "#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"
- @@ -7971,11 +8105,10 @@
- { $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
- -
- + 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 -ru a/aconfigure.ac b/aconfigure.ac
- --- a/aconfigure.ac 2017-01-25 06:23:08.000000000 -0500
- +++ b/aconfigure.ac 2017-06-08 13:28:17.138135490 -0400
- @@ -1533,18 +1533,59 @@
- dnl # Include SSL support
- AC_SUBST(ac_no_ssl)
- AC_SUBST(ac_ssl_has_aes_gcm,0)
- -AC_ARG_ENABLE(ssl,
- - AS_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_ARG_ENABLE([ssl],
- + AS_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 ])
- +
- +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,
- + $host-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..])
- +
- if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then
- CFLAGS="$CFLAGS -I$with_ssl/include"
- LDFLAGS="$LDFLAGS -L$with_ssl/lib"
- @@ -1578,7 +1619,8 @@
- else
- AC_MSG_RESULT([** OpenSSL libraries not found, disabling SSL support **])
- fi
- - ])
- + fi
- +fi
-
- dnl # Obsolete option --with-opencore-amrnb
- AC_ARG_WITH(opencore-amrnb,
- diff -ru a/pjlib/build/Makefile b/pjlib/build/Makefile
- --- a/pjlib/build/Makefile 2016-10-05 05:52:39.000000000 -0400
- +++ b/pjlib/build/Makefile 2017-06-08 13:30:20.702138591 -0400
- @@ -35,7 +35,7 @@
- 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 \
- - 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 -ru a/pjlib/include/pj/compat/os_auto.h.in b/pjlib/include/pj/compat/os_auto.h.in
- --- a/pjlib/include/pj/compat/os_auto.h.in 2017-01-24 00:36:50.000000000 -0500
- +++ b/pjlib/include/pj/compat/os_auto.h.in 2017-06-08 13:31:04.976064779 -0400
- @@ -219,6 +219,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 -ru a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
- --- a/pjlib/include/pj/config.h 2017-01-25 21:29:59.000000000 -0500
- +++ b/pjlib/include/pj/config.h 2017-06-08 13:34:27.642149351 -0400
- @@ -888,7 +888,7 @@
-
- /**
- * Enable secure socket. For most platforms, this is implemented using
- - * OpenSSL, so this will require OpenSSL to be installed. For Symbian
- + * OpenSSL or GnuTLS, so this will require OpenSSL or GnuTLS to be installed. For Symbian
- * platform, this is implemented natively using CSecureSocket.
- *
- * Default: 0 (for now)
- @@ -896,6 +896,10 @@
- #ifndef PJ_HAS_SSL_SOCK
- # define PJ_HAS_SSL_SOCK 0
- #endif
- +// When set to 1 secure sockets will use the GnuTLS backend than OpenSSL
- +#ifndef PJ_HAS_TLS_SOCK
- +# define PJ_HAS_TLS_SOCK 0
- +#endif
-
-
- /**
- diff -ru a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h
- --- a/pjlib/include/pj/ssl_sock.h 2016-10-27 03:58:01.000000000 -0400
- +++ b/pjlib/include/pj/ssl_sock.h 2017-06-08 13:36:16.448510381 -0400
- @@ -184,6 +184,10 @@
- pj_str_t raw; /**< Raw certificate in PEM format, only
- available for remote certificate. */
-
- + struct {
- + unsigned cnt; /**< # of entry */
- + pj_str_t* cert_raw;
- + } raw_chain;
- } pj_ssl_cert_info;
-
-
- diff -ru a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c
- --- a/pjlib/src/pj/ssl_sock_common.c 2016-10-27 03:58:01.000000000 -0400
- +++ b/pjlib/src/pj/ssl_sock_common.c 2017-06-08 13:37:17.171037628 -0400
- @@ -35,7 +35,12 @@
- 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..37bcaba
- --- /dev/null
- +++ b/pjlib/src/pj/ssl_sock_gtls.c
- @@ -0,0 +1,2877 @@
- +/* $Id$ */
- +/*
- + * Copyright (C) 2014-2016 Savoir-faire Linux. (https://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>
- +
- +#if GNUTLS_VERSION_NUMBER < 0x030306 && !defined(_MSC_VER)
- +#include <dirent.h>
- +#endif
- +
- +#include <errno.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, "libgnutls")
- +#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_sock_param newsock_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 CA_path;
- + 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;
- +}
- +
- +
- +/* 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);
- +
- + /* 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 (ssock->param.verify_peer) {
- + 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. */
- + 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);
- +
- + gnutls_transport_set_errno(ssock->session, 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 */
- + gnutls_transport_set_errno(ssock->session, 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];
- + char priority_buf[256];
- + 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;
- + pj_str_t priority;
- + const char *err;
- +
- + pj_strset(&cipher_list, buf, 0);
- + pj_strset(&priority, priority_buf, 0);
- +
- + /* For each level, enable only the requested protocol */
- + pj_strcat2(&priority, "NORMAL:");
- + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) {
- + pj_strcat2(&priority, "+VERS-TLS1.2:");
- + }
- + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_1) {
- + pj_strcat2(&priority, "+VERS-TLS1.1:");
- + }
- + if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1) {
- + pj_strcat2(&priority, "+VERS-TLS1.0:");
- + }
- + pj_strcat2(&priority, "-VERS-SSL3.0:");
- + pj_strcat2(&priority, "%LATEST_RECORD_VERSION");
- +
- + pj_strcat(&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;
- +}
- +
- +#if GNUTLS_VERSION_NUMBER < 0x030306
- +
- +#ifdef _POSIX_PATH_MAX
- +# define GNUTLS_PATH_MAX _POSIX_PATH_MAX
- +#else
- +# define GNUTLS_PATH_MAX 256
- +#endif
- +
- +static
- +int gnutls_certificate_set_x509_trust_dir(gnutls_certificate_credentials_t cred, const char *dirname, unsigned type)
- +{
- + DIR *dirp;
- + struct dirent *d;
- + int ret;
- + int r = 0;
- + char path[GNUTLS_PATH_MAX];
- +#ifndef _WIN32
- + struct dirent e;
- +#endif
- +
- + dirp = opendir(dirname);
- + if (dirp != NULL) {
- + do {
- +#ifdef _WIN32
- + d = readdir(dirp);
- + if (d != NULL) {
- +#else
- + ret = readdir_r(dirp, &e, &d);
- + if (ret == 0 && d != NULL
- +#ifdef _DIRENT_HAVE_D_TYPE
- + && (d->d_type == DT_REG || d->d_type == DT_LNK || d->d_type == DT_UNKNOWN)
- +#endif
- + ) {
- +#endif
- + snprintf(path, sizeof(path), "%s/%s",
- + dirname, d->d_name);
- +
- + ret = gnutls_certificate_set_x509_trust_file(cred, path, type);
- + if (ret >= 0)
- + r += ret;
- + }
- + }
- + while (d != NULL);
- + closedir(dirp);
- + }
- +
- + return r;
- +}
- +
- +#endif
- +
- +/* 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;
- + }
- + if (cert->CA_path.slen) {
- + ret = gnutls_certificate_set_x509_trust_dir(ssock->xcred,
- + cert->CA_path.ptr,
- + GNUTLS_X509_FMT_PEM);
- + if (ret < 0)
- + ret = gnutls_certificate_set_x509_trust_dir(ssock->xcred,
- + cert->CA_path.ptr,
- + GNUTLS_X509_FMT_DER);
- + if (ret < 0)
- + goto out;
- + }
- +
- + /* Load certificate, key and pass if one is specified */
- + if (cert->cert_file.slen && cert->privkey_file.slen) {
- + const char *prikey_file = cert->privkey_file.ptr;
- + const char *prikey_pass = 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. */
- + }
- +}
- +
- +static void tls_cert_get_chain_raw(pj_pool_t *pool, pj_ssl_cert_info *ci, const gnutls_datum_t *certs, size_t certs_num)
- +{
- + size_t i=0;
- + ci->raw_chain.cert_raw = pj_pool_calloc(pool, certs_num, sizeof(*ci->raw_chain.cert_raw));
- + ci->raw_chain.cnt = certs_num;
- + for (i=0; i < certs_num; ++i) {
- + const pj_str_t crt_raw = {(const char*)certs[i].data, (pj_ssize_t)certs[i].size};
- + pj_strdup(pool, ci->raw_chain.cert_raw+i, &crt_raw);
- + }
- +}
- +
- +/* 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);
- + tls_cert_get_chain_raw(ssock->pool, &ssock->local_cert_info, us, 1);
- +
- +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);
- + tls_cert_get_chain_raw(ssock->pool, &ssock->remote_cert_info, certs, certslen);
- +
- +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(¶m);
- + status = pj_ssl_sock_create(pool, ¶m, &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,
- + ((read_data_t *)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->newsock_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)
- +{
- + return pj_ssl_cert_load_from_files2(pool, CA_file, NULL, cert_file,
- + privkey_file, privkey_pass, p_cert);
- +}
- +
- +/* Load credentials from files. */
- +PJ_DECL(pj_status_t) pj_ssl_cert_load_from_files2(
- + pj_pool_t *pool,
- + const pj_str_t *CA_file,
- + const pj_str_t *CA_path,
- + 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 || CA_path) && cert_file &&
- + privkey_file,
- + PJ_EINVAL);
- +
- + cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
- + if (CA_file) {
- + pj_strdup_with_null(pool, &cert->CA_file, CA_file);
- + }
- + if (CA_path) {
- + pj_strdup_with_null(pool, &cert->CA_path, CA_path);
- + }
- + 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_->CA_path, &cert->CA_path);
- + 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, ¶m->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, ((read_data_t *)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)
- +{
- + return pj_ssl_sock_start_accept2(ssock, pool, localaddr, addr_len,
- + &ssock->param);
- +}
- +
- +/**
- + * Starts asynchronous socket accept() operations on this secure socket.
- + */
- +PJ_DEF(pj_status_t) pj_ssl_sock_start_accept2 (pj_ssl_sock_t *ssock,
- + pj_pool_t *pool,
- + const pj_sockaddr_t *localaddr,
- + int addr_len,
- + const pj_ssl_sock_param *newsock_param)
- +{
- + pj_activesock_cb asock_cb;
- + pj_activesock_cfg asock_cfg;
- + pj_status_t status;
- +
- + PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL);
- +
- + /* Verify new socket parameters */
- + if (newsock_param->grp_lock != ssock->param.grp_lock ||
- + newsock_param->sock_af != ssock->param.sock_af ||
- + newsock_param->sock_type != ssock->param.sock_type)
- + {
- + return 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 */
- + pj_ssl_sock_param_copy(pool, &ssock->newsock_param, newsock_param);
- + 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 -ru a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
- --- a/pjlib/src/pj/ssl_sock_ossl.c 2017-01-24 00:41:05.000000000 -0500
- +++ b/pjlib/src/pj/ssl_sock_ossl.c 2017-06-08 13:42:15.188809557 -0400
- @@ -32,8 +32,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"
-
- diff -ru a/pjmedia/src/pjmedia/transport_srtp.c b/pjmedia/src/pjmedia/transport_srtp.c
- --- a/pjmedia/src/pjmedia/transport_srtp.c 2017-01-10 23:38:29.000000000 -0500
- +++ b/pjmedia/src/pjmedia/transport_srtp.c 2017-06-08 13:43:29.727001721 -0400
- @@ -30,7 +30,8 @@
-
- #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
-
- -#if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0)
- +#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \
- + defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 0
- # include <openssl/rand.h>
-
- /* Suppress compile warning of OpenSSL deprecation (OpenSSL is deprecated
- @@ -1147,7 +1148,8 @@
- key_ok = PJ_TRUE;
-
-
- -#if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0)
- +#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \
- + defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 0
-
- /* Include OpenSSL libraries for MSVC */
- # ifdef _MSC_VER
|