学习学习

《openssl 编程》之 ssl实现

上一篇 / 下一篇  2007-11-15 10:41:16 / 个人分类:信息安全

第三十一章  SSL实现

31.1概述

SSL协议最先由netscape公司提出,包括sslv2sslv3两个版本。当前形成标准的为了tls协议(rfc2246规范)DTLSrfc4347用于支持UDP协议)。sslv3tls协议大致一样,只是有一些细微的差别。实际应用中,用的最多的为sslv3

akQ i Fz0

SSL协议能够保证通信双方的信道安全。它能提供数据加密、身份认证以及消息完整性保护,另外SSL协议还支持数据压缩。

0F6c V1~H Z1MR0

SSL协议通过客户端和服务端握手来协商各种算法和密钥。

E hY8r%t-@0

31.2 openssl实现

SSL协议源码位于ssl目录下。它实现了sslv2sslv3TLS以及DTLSDatagram TLS,基于UDPTLS实现)。ssl实现中,对于每个协议,都有客户端实现(XXX_clnt.c)、服务端实现(XXX_srvr.c)、加密实现(XXX_enc.c)、记录协议实现(XXX_pkt.c)METHOD方法(XXX_meth.c)、客户端服务端都用到的握手方法实现(XXX_both.c),以及对外提供的函数实现(XXX_lib.c),比较有规律。

W+U@q@1W/GV/^+~0

31.3 建立SSL测试环境

为了对SSL协议有大致的了解,我们可以通过openssl命令来建立一个SSL测试环境。LUPA开源社区xx*T+Oh&Em

1) 建立自己的CALUPA开源社区8T|L6tv/oo,CJ3xi

 openssl安装目录的misc目录下(或者在apps目录下),运行脚本:./CA.sh -newcaWindows环境下运行:perl ca.pl –newca),出现提示符时,直接回车。  运行完毕后会生成一个demonCA的目录,里面包含了ca证书及其私钥。LUPA开源社区 nOKHe:O*@F

2)  生成客户端和服务端证书申请:LUPA开源社区5pb z d#TX)C-Q

 openssl req -newkey rsa:1024 -out req1.pem -keyout sslclientkey.pem

H"D,Y Ts)S0

openssl req -newkey rsa:1024 -out req2.pem -keyout sslserverkey.pemLUPA开源社区9x&EsT.WE!M

3)     签发客户端和服务端证书LUPA开源社区mUnXt'p

openssl ca -in req1.pem -out sslclientcert.pem

q nh G/H.q J0

openssl ca -in req2.pem -out sslservercert.pem

Zj2PY2H-Cw ]0

4)   运行ssl服务端和客户端:LUPA开源社区 aRZ7DiF-[

openssl s_server -cert sslservercert.pem -key sslserverkey.pem -CAfile demoCA/cacert.pem -ssl3LUPA开源社区!Y a'|@i _v*O'|;d

openssl s_client -ssl3 -CAfile demoCA/cacert.pemLUPA开源社区c,A|1E\u

运行客户端程序后,如果正确,会打印类似如下内容:

7|8tQ$Vb0

SSL-Session:LUPA开源社区9OGJ9XI4]fx

   Protocol  : SSLv3

8nHS:b#t2Ja6l0

Cipher    : DHE-RSA-AES256-SHA

;o;P5G,}NT%}*D5L+{6ep8v0

    Session-ID: A729F5845CBFFBA68B27F701A6BD9D411627FA5BDC780264131EE966D1DFD6F5

` Ko`6A^'?0

    Session-ID-ctx:

jQ&]8v nFy0

    Master-Key: B00EEBD68165197BF033605F348A91676E872EB48487990D8BC77022578EECC0A9789CD1F929E6A9EA259F9F9F3F9DFALUPA开源社区.^!h QK:L4v&yN J

    Key-Arg   : NoneLUPA开源社区3Z!z ~p(lP

    Start Time: 1164077175

?*Y!cwp |j&? x%u0

    Timeout   : 7200 (sec)

O)tR+X1v3M u ~_ G!E0

    Verify return code: 0 (ok)

9m#p e&XD3J2w0

此时,输入数据然后回车,服务端会显示出来。

h,M&KNZ0`%};K0

命令的其他选项:LUPA开源社区Me,x.KEp

a)   验证客户端证书LUPA开源社区b cn e I'm2G M!XLG

openssl s_server -cert sslservercert.pem -key sslserverkey.pem -CAfile demoCA/cacert.pem -ssl3 -Verify 1

6i/j-qJ7Wuy0

openssl s_client -ssl3 -CAfile demoCA/cacert.pem -cert sslclientcert.pem -key sslclientkey.pem

bc6Tk9E0

b)   指定加密套件

mN#HeZ(X6{'_'h0

openssl s_server -cert sslservercert.pem -key sslserverkey.pem -CAfile demoCA/cacert.pem -ssl3 -Verify 1

e Ck~Ot/^ B{0

openssl s_client -ssl3 -CAfile demoCA/cacert.pem -cert sslclientcert.pem -key sslclientkey.pem -cipher AES256-SHALUPA开源社区.JPLsf?}/i\Y

其中AES256-SHA可用根据openssl ciphers命令获取,s_server也可用指明加密套件:LUPA开源社区C4BI"MI+C

openssl s_server -cert sslservercert.pem -key sslserverkey.pem -CAfile demoCA/cacert.pem -ssl3 -Verify 1 -cipher AES256-SHA

X8L,if`mG+Pr0

c)   指定私钥加密口令

(hvR;x4a0

openssl s_server -cert sslservercert.pem -key sslserverkey.pem -CAfile demoCA/cacert.pem -ssl3 -Verify 3 -cipher AES256-SHA -pass pass:123456

&a$kZ]yx,?.c6?m0

openssl s_client -ssl3 -CAfile demoCA/cacert.pem -cert sslclientcert.pem -key sslclientkey.pem -pass pass:123456LUPA开源社区E-EI,BS*uI

用参数pass给出私钥保护口令来源:LUPA开源社区q0K:^._1B

-pass file:1.txt   (1.txt的内容为加密口令123456);

N J+}n5h.J0

-pass env:envname (环境变量);LUPA开源社区%a9AB-sd'~%\

-pass fd:fdnameLUPA开源社区:B?/P9f9y9|u2[

-pass stdin

5O6^.}~QAU0

比如:LUPA开源社区3DZ3A7Yc,wQ

openssl s_client -ssl3 -CAfile demoCA/cacert.pem -cert sslclientcert.pem -key sslclientkey.pem -pass stdin

mF5hv.g8f0

然后输入口令123456即可。

V)| `dG pk*d%d0

31.4 数据结构

      ssl的主要数据结构定义在ssl.h中。主要的数据结构有SSL_CTXSSLSSL_SESSIONSSL_CTX数据结构主要用于SSL握手前的环境准备,设置CA文件和目录、设置SSL握手中的证书文件和私钥、设置协议版本以及其他一些SSL握手时的选项。SSL数据结构主要用于SSL握手以及传送应用数据。SSL_SESSION中保存了主密钥、session id、读写加解密钥、读写MAC密钥等信息。SSL_CTX中缓存了所有SSL_SESSION信息,SSL中包含SSL_CTX。一般SSL_CTX的初始化在程序最开始调用,然后再生成SSL数据结构。由于SSL_CTX中缓存了所有的SESSION,新生成的SSL结构又包含SSL_CTX数据,所以通过SSL数据结构能查找以前用过的SESSION id,实现SESSION重用。

v$H4y y,L R5NV0

31.5 加密套件

一个加密套件指明了SSL握手阶段和通信阶段所应该采用的各种算法。这些算法包括:认证算法、密钥交换算法、对称算法和摘要算法等。

SOE.M+l0

在握手初始化的时候,双方都会导入各自所认可的多种加密套件。在握手阶段,由服务端选择其中的一种加密套件。

K8YT$[6BV3d A0

OpenSSLciphers命令可以列出所有的加密套件。openssl的加密套件在s3_lib.cssl3_ciphers数组中定义。比如有:LUPA开源社区FV dn0rmh,K

/* Cipher 05 */

P+mX/g.e0

      {

r+T3~A-t0

      1,LUPA开源社区GQ%q-WB4K

      SSL3_TXT_RSA_RC4_128_SHA,

?J,B&NG[0

      SSL3_CK_RSA_RC4_128_SHA,LUPA开源社区;f(?u+i jPc#| _.uU

      SSL_kRSA|SSL_aRSA|SSL_RC4 |SSL_SHA1|SSL_SSLV3,

;}qR6Q{4iY1~0

      SSL_NOT_EXP|SSL_MEDIUM,LUPA开源社区8d4s7{mka

      0,

"ae8T#kqc0

      128,

q o!W#MS'Z0W1f0

      128,LUPA开源社区-?^"ix~9g7p4|

      SSL_ALL_CIPHERS,LUPA开源社区 [jcH2{:wZ^

      SSL_ALL_STRENGTHS,LUPA开源社区pqSF!w {`+W

      }

R4X(B/NI?(@|+z4g0

其中1表示是合法的加密套件;SSL3_TXT_RSA_RC4_128_SHA为加密套件的名字,SSL3_CK_RSA_RC4_128_SHA为加密套件IDSSL_kRSA|SSL_aRSA|SSL_RC4 |SSL_SHA1|SSL_SSLV3表明了各种算法,其中密钥交换采用RSA算法(SSL_kRSA),认证采用RSA算法(SSL_aRSA),对称加密算法采用RC4算法(SSL_RC4),摘要采用SHA1,采用SSL协议第三版本,SSL_NOT_EXP|SSL_MEDIUM表明算法的强度。LUPA开源社区:{j$S!y@'?

在客户端和服务器端建立安全连接之前,双方都必须指定适合自己的加密套件。加密套件的选择可以通过组合的字符串来控制。LUPA开源社区1i`lg@?"GC

字符串的形式举例:ALL:!ADH:RC4+RSA:+SSLv2:@STRENGTHLUPA开源社区5iSb-n+?[x7Ej6i

Openssl定义了4中选择符号:“+”,“-”,“!”,“@”。其中,“+”表示取交集;“-”表示临时删除一个算法;“!”表示永久删除一个算法;“@“表示了排序方法。

i!rDL[4fg0j0

多个描述之间可以用“:”、“,”、“”、“;”来分开。选择加密套件的时候按照从左到的顺序构成双向链表,存放与内存中。

;[6?R G p0

ALL:!ADH:RC4+RSA:+SSLv2:@STRENGTH表示的意义是:首先选择所有的加密套件(不包含eNULL,即空对称加密算法),然后在得到的双向链表之中去掉身份验证采用DH的加密套件;加入包含RC4算法并将包含RSA的加密套件放在双向链表的尾部;再将支持SSLV2的加密套件放在尾部;最后得到的结果按照安全强度进行排序。

-Y-L6V%C.t0

SSL建立链接之前,客户端和服务器端用openssl函数来设置自己支持的加密套件。主要的函数有:

{1z O {'n;y0

int SSL_set_cipher_list(SSL *s,const char *str)LUPA开源社区$q1c'\2C5E(GgP

int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str)LUPA开源社区.RopB]zik0_ R5~

比如只设置一种加密套件:LUPA开源社区K1X r,D-WM

int   ret=SSL_set_cipher_list(ssl,"RC4-MD5");LUPA开源社区{ `%b`%XKh-j"qm7S

如果服务端只设置了一种加密套件,那么客户端要么接受要么返回错误。加密套件的选择是由服务端做出的。LUPA开源社区~ no0j7P'E;t

31.6 密钥信息

ssl中的密钥相关信息包括:预主密钥、主密钥、读解密密钥及其iv、写加密密钥及其iv、读MAC密钥、写MAC密钥。LUPA开源社区M _ i4Mo lvq/x

1)   预主密钥LUPA开源社区8A0q#ANE6E

预主密钥是主密钥的计算来源。它由客户端生成,采用服务端的公钥加密发送给服务端。LUPA开源社区_$J*gv [i{"fA

sslv3为例,预主密钥的生成在源代码s3_clnt.cssl3_send_client_key_exchange函数中,有源码如下:

a7rx8e NnH4t2K0

tmp_buf[0]=s->client_version>>8;LUPA开源社区:e7Q J5o1J0j-^y e)V

tmp_buf[1]=s->client_version&0xff;LUPA开源社区Xu(j h9m:abu._

if (RAND_bytes(&(tmp_buf[2]),sizeof tmp_buf-2) <= 0)

oi[;a-HU5]e0

goto err;LUPA开源社区4r{L4Y$` z U

s->session->master_key_length=sizeof tmp_buf;

x0zrX@0

……

d\"kUX0

n=RSA_public_encrypt(sizeof tmp_buf,tmp_buf,p,rsa,RSA_PKCS1_PADDING);LUPA开源社区;w NkO%](k\'d

 LUPA开源社区5~iTwg.o

此处,tmp_buf中存放的就是预主密钥。

6H"T#K7uc(v7_.S$~4q0

2)   主密钥

n/l.tT v[f3X#\0

主密钥分别由客户端和服务端根据预主密钥、客户端随机数和服务端随机数来生成,他们的主密钥是相同的。主密钥用于生成各种密钥信息,它存放在SESSION数据结构中。由于协议版本不同,生成方式也不同。sslv3的源代码中,它通过ssl3_generate_master_secret函数生成,tlsv1中它通过tls1_generate_master_secret函数来生成。LUPA开源社区$TXe5Fn H1En3N\

3)   对称密钥和MAC密钥

%Pz'?c/P-eA0

对称密钥(包括IV)和读写MAC密钥通过主密钥、客户端随机数和服务端随机数来生成。sslv3源代码中,它们在ssl3_generate_key_block中生成,在ssl3_change_cipher_state中分配。

g:F1u7b4U7O0

31.7 SESSION

当客户端和服务端在握手中新建了session,服务端生成一个session ID,通过哈希表缓存SESSION信息,并通过server hello消息发送给客户端。此ID是一个随机数,SSL v2版本时长度为16字节,SSLv3TLSv1长度为32字节。此ID与安全无关,但是在服务端必须是唯一的。当需要session重用时,客户端发送包含session idclientHello消息(无sesion重用时,此值为空)给服务端,服务端可用根据此ID来查询缓存。session重用可以免去诸多SSL握手交互,特别是客户端的公钥加密和服务端的私钥解密所带来的性能开销。session的默认超时时间为60*5+4秒,5分钟。LUPA开源社区J1c8^ieH$z b

session相关函数有:

`r]&^Z;['UN0

1)   int SSL_has_matching_session_id(const SSL *ssl, const unsigned char * id,unsigned int id_len)LUPA开源社区o(@*\Fon#^

SSL中查询session ididid_len为输入的要查询的session id,查询哈希表ssl->ctx->sessions,如果匹配,返回1,否则返回0LUPA开源社区%h!o bQO+?hKl;H

      2 int ssl_get_new_session(SSL *s, int session)LUPA开源社区0Q k!} e)b G0r

生成ssl用的session,此函数可用被服务端或客户端调用,当服务端调用时,传入参数session1,生成新的session;当客户端调用时,传入参数session0,只是简单的将session id的长度设为0

1a/@"i2}x)m@"[0

3 int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len)LUPA开源社区eE2p2xx

获取以前用过的session id,用于服务端session重用,本函数由服务端调用,session_id为输入senssion ID首地址,len为其长度,如果返回1,表明要session重用;返回0,表示没有找到;返回-1表示错误。LUPA开源社区#NVUWRg

      4 int SSL_set_session(SSL *s, SSL_SESSION *session)LUPA开源社区u^N2S"\-L n M Up

设置session,本函数用于客户端,用于设置session信息;如果输入参数session为空值,它将置空s->session;如果不为空,它将输入信息作为session信息。

%u\L HC0q;@*K0

      5 void SSL_CTX_flush_sessions(SSL_CTX *s, long t)LUPA开源社区*jr*G?!D

清除超时的SESSION,输入参数t指定一个时间,如果t=0,则清除所有SESSION,一般用time(NULL)取当前时间。此函数调用了哈希表函数lh_doall_arg来处理每一个SESSION数据。

8o:Clfa0

      6 int ssl_clear_bad_session(SSL *s)LUPA开源社区?A:e!aPPze,FR9\

清除无效SESSIONLUPA开源社区 h@}z*wJGs0a'Nw

31.8 多线程支持

编写openssl多线程程序时,需要设置两个回调函数:LUPA开源社区Ce1M \)O

CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);

3?(l A:^5I5`}qc0

CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);

h@9E;q"R}0

对于多线程程序的写法,读者可以参考crypto/threads/mttest.c,也可以查考下面的例子。LUPA开源社区4["i?*f Rb Ca[ ~

31.9 编程示例

本示例用多线程实现了一个ssl服务端和一个客户端。LUPA开源社区v#m&J4yI4s\lx5e8nM

服务端代码如下:LUPA开源社区.yWo6[K'X*sGt!a$?)N

#include <stdio.h>

+ljk0w U k0S8D0

#include <stdlib.h>LUPA开源社区,NK#@z%~

#include <memory.h>

z,a|PE$_A.b0

#include <errno.h>

_H*\(yH?k A6dRx0

#ifndef   _WIN32LUPA开源社区z3P7UK\

#include <sys/types.h>LUPA开源社区 Q|WY5Te

#include <sys/socket.h>

I yf@ A,a0

#include <netinet/in.h>

b(qng jM k0

#include <arpa/inet.h>LUPA开源社区;g*H._/kYpc

#include <netdb.h>LUPA开源社区Yhghd!dz

#include <unistd.h>

:w[Z4QY0

#else

:A*N9ww4r0

#include <winsock2.h>LUPA开源社区9d)q_%ef'l{

#include <windows.h>LUPA开源社区6A6F.a1C!Bs ^R

#endif

*q8e(gi$c7v,w GS+h7R3syn0

#include "pthread.h"

*nRgzcV}wE0

#include <openssl/rsa.h>

xti7_+\FX3U0

#include <openssl/crypto.h>LUPA开源社区 @8{'G| _%E

#include <openssl/x509.h>LUPA开源社区;XU ^Hqv4b

#include <openssl/pem.h>LUPA开源社区?pw/}Gc

#include <openssl/ssl.h>LUPA开源社区 }-`)G \P8QoI

#include <openssl/err.h>LUPA开源社区5]asU#p

#define CERTF "certs/sslservercert.pem"LUPA开源社区S"Fls {6kE

#define KEYF "certs/sslserverkey.pem"

w e9b_})Hf?0

#define   CAFILE "certs/cacert.pem"LUPA开源社区 K3sWo6C\(i&SM

pthread_mutex_t   mlock=PTHREAD_MUTEX_INITIALIZER;

L'eWfaL0

static pthread_mutex_t *lock_cs;

OO%Wo%k,A*k3x,W;O0

static long *lock_count;

{ s1hY&oe'j%M4{!Q0

#define CHK_NULL(x) if ((x)==NULL) { printf("null\n"); }LUPA开源社区 q)uQ uJ'f

#define CHK_ERR(err,s) if ((err)==-1) { printf(" -1 \n"); }LUPA开源社区8pP P1p&lS7W0T

#define CHK_SSL(err) if ((err)==-1) { printf(" -1 \n");}LUPA开源社区rB Jw FP,Xz

#define   CAFILE "certs/cacert.pem"LUPA开源社区1jLH}/_K:S

 LUPA开源社区%G4N4W9eXl+i

int verify_callback_server(int ok, X509_STORE_CTX *ctx)LUPA开源社区9hAl r(uI3h-H7F7B

{LUPA开源社区 d)S,e%v uu)k

             printf("verify_callback_server \n");

-~,F*S5k+P2\|'t w-M0

       return ok;LUPA开源社区XM&p"u.d1r)K Lm0B`

}

&B;e2cmM7E*Yf0

 

A/O/n'J2krGq0

int   SSL_CTX_use_PrivateKey_file_pass(SSL_CTX *ctx,char *filename,char *pass)

Q$R"_sRc9l0

{LUPA开源社区Gc P_1nZ

      EVP_PKEY    *pkey=NULL;

%sd#|;f-K-p0

      BIO              *key=NULL;

:Nac9d6FQ6X0

      LUPA开源社区 D0]t,a:L#V R

      key=BIO_new(BIO_s_file());LUPA开源社区izb*Vo2YM)p B

      BIO_read_filename(key,filename);

'@ J0U0I/z1nrvAk0

      pkey=PEM_read_bio_PrivateKey(key,NULL,NULL,pass);

.h1\_5|0m0

      if(pkey==NULL)LUPA开源社区?b1P&S2h$Zk

      {

N |GlMg0

             printf("PEM_read_bio_PrivateKey err");

oPn,i"N$k0

             return -1;

,hty D1e;p)U'@'d)]0

      }LUPA开源社区9v/r@%M} c

      if (SSL_CTX_use_PrivateKey(ctx,pkey) <= 0)

(v x6xJjt B"U0

      {LUPA开源社区#U}5d,^ cT7G k5pN

             printf("SSL_CTX_use_PrivateKey err\n");

vtCqWMOsa0

             return -1;

,X(G1^~ L/dr-s0

      }LUPA开源社区:^/Iq3zLn

      BIO_free(key);LUPA开源社区3bAyq(` @d

      return 1;LUPA开源社区S@t2K6s*l

}LUPA开源社区&I9k%g!|vjDWZ

 LUPA开源社区3tS@L;Id)B

static int s_server_verify=SSL_VERIFY_NONE;LUPA开源社区"?TV@ H&sd4jO

void * thread_main(void *arg)

}Ma;~QFk"yK0

{  LUPA开源社区+G)c!tk:I I;P ~ Q

      SOCKET s,AcceptSocket;LUPA开源社区G-\9j AY KE eu

      WORD wVersionRequested;LUPA开源社区xGVUmb

      WSADATA wsaData;

sH'z0}HR;|0

      struct sockaddr_in service;

^6C R7].P+bP.{$kj0

      int   err;LUPA开源社区0f rJBf anF

    size_t            client_len;                                                                                         SSL_CTX            *ctx;LUPA开源社区9F3b-z(M|!r^9[

    SSL       *ssl;

5Rm"M?kI,F(Q Y.t0

    X509            *client_cert;LUPA开源社区+_URV$K

    char       *str;

c$bO_'R)r'@0

    char   buf[1024];LUPA开源社区};u4l2ZbsH

    SSL_METHOD   *meth;

-Rmxn4XY0j)m$o0

       LUPA开源社区 R+Z}7p ~,rwn0H

      ssl=(SSL *)arg;LUPA开源社区6n!jMY1@7| K8y

      s=SSL_get_fd(ssl);LUPA开源社区veV.P$qV*L[

      err = SSL_accept (ssl);LUPA开源社区z.e#J2^5S q

    if(err<0)

PVV2jvQ y)@D0

      {LUPA开源社区B!F&{1a.[^g*MH'k

             printf("ssl accerr\n");

wj,?VHQ8m0

             return ;LUPA开源社区,Cp v8Kjfy

      }LUPA开源社区mPgq"^Cp9y y r

    printf ("SSL connection using %s\n", SSL_get_cipher (ssl));

Cta,I?Y/Ub0

     client_cert = SSL_get_peer_certificate (ssl);

DZf)^ R P#x0

    if (client_cert != NULL)

[$yl+^+N _ [ X{0

    {LUPA开源社区:lB;KN_6s\ F

                printf ("Client certificate:\n");

$pl*ZsZk3bD.eK0

                    str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);LUPA开源社区:l.k k;y K'q+g!t

                CHK_NULL(str);

m(X)I1f Is A0

                printf ("\t subject: %s\n", str);

3vw6Y5FZ;v3F-`X0

                OPENSSL_free (str);

5?l pD`0

                    str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0);

@^/| w*Tn1d GP;U0

                CHK_NULL(str);

5q(P%Fp#y7KQ#N0

                printf ("\t issuer: %s\n", str);

+^a!J.`7H(@oMe!V0

                OPENSSL_free (str);LUPA开源社区X'S.mu!MO8L k

                    X509_free (client_cert);LUPA开源社区hJ)js}F5|o

    }LUPA开源社区hr/n,k:tV%M$q

    else

B ZdW^2~6t?5i0

                printf ("Client does not have certificate.\n");LUPA开源社区4J:weUz^2cD(O

      memset(buf,0,1024);LUPA开源社区e;mz3@ O

      err = SSL_read (ssl, buf, sizeof(buf) - 1);

3U(@5[Psx0

      if(err<0)LUPA开源社区 ol#M5gZN]LL%k|

      {

Q e9kW;c)r]0

             printf("ssl read err\n");LUPA开源社区Vse;fj6H"@ LyL

             closesocket(s);

lv8J*Rk;MK0

             return;

skG V&e"\ W0

      }

`qf SAe.q_ ^d0

      printf("get : %s\n",buf);LUPA开源社区1`J u8j3|$^K9Z#~ }6\!F#v

#if 0

2ZD&JJ5K4LE#y _0

    buf[err] = '\0';

tp4eX,mQ2B'm)f}E3j_/U0

    err = SSL_write (ssl, "I hear you.", strlen("I hear you.")); CHK_SSL(err);LUPA开源社区9?7E_{@

#endif

#e5pe0s-KX4[N@0

    SSL_free (ssl);LUPA开源社区 Z Y)U4wIz6SLC

      closesocket(s);LUPA开源社区F0]3qie c-p:XL `!J

}

A}G&u6CF6W:A!^q0

 

|&pPm H0

pthread_t pthreads_thread_id(void)LUPA开源社区6Lt0w.Jm Li&ju

{LUPA开源社区 ukh9}_G$n:Q

      pthread_t ret;

sb3k2lz#tf$S,O)Mn0

 

L._5RDz{ V0

      ret=pthread_self();LUPA开源社区'r~?5s(c+a AozJW

      return(ret);

J+|8{-] H~\0

}LUPA开源社区I r;K[o:`|D

 

gX0W._ll1c0

void pthreads_locking_callback(int mode, int type, char *file,LUPA开源社区A!X\_`U wf-LT$a

          int line)LUPA开源社区M,d0` cd*d@3q

{

4?XCVKn0

      if (mode & CRYPTO_LOCK)LUPA开源社区 v*C Nk'hDz

             {LUPA开源社区#Dh V9Cg ]n)Q

             pthread_mutex_lock(&(lock_cs[type]));

y;TD L+b%X0

             lock_count[type]++;LUPA开源社区+N)KL*mR*i zO/D Xt

             }LUPA开源社区E:}+j w2PU,K9O

      elseLUPA开源社区0fh;V7a6D"j

             {LUPA开源社区l(CV1ie)A:i!n/c4J

             pthread_mutex_unlock(&(lock_cs[type]));

tOw$|&Fn-[0

             }

N u4f(C#a0

}

.S x:J1O g0

 LUPA开源社区R5Q oa4@ z:e,EDN

int main ()

S'yI'{;j0

{LUPA开源社区8k P IiL

      int                 err;