SSL_write 发生了什么

SSL_write 发生了什么

以前在设计协议栈的时候,非常随意,因为包括下层的TCP到上层的应用处理,都是自己设计的,怎么样不麻烦这么约定。然而,如果将自己的协议栈放在通用平台,那么问题就显得比较微妙了,不是因为协议栈本身自己的实现的难度,而是使用协议栈的方式多样化导致接口设计需要变得非常复杂。

一个简单的SSL_write流程,其实核心内容很简单:
1:分段(如果必要的话)
2:每段算摘要(GCM模式不需要这步)
3:每段进行加密
4:填充5自己的record头。
5:调用glicsend/write发送。

看似很简单,跟发送Finished一模一样,但是仔细想想就会让人头痛。比如:

应用层调用SSL_write(s, plaintext, 100)

正常情况就是加密等操作完成后,调用send/write发送成功。但是当Socket的send buffer不够的时候,send/write未能发送全部数据,这就麻烦大了。

一个详细的例子:

1:应用层调用SSL_write(s, plaintext, 100)
2:OpenSSL里面,先对plaintext进行加密,假设加密结果还是116字节
3:调用BIO_write,即调用send/write,但是send/write返回了4,即实际上只发送了4字节,剩余112字节的密文还未发送。

问题来了,那么返回给应用层的返回值是什么?即SSL_write的返回值是什么?

1:返回 4?显然不正确,因为发送了密文数据4字节,不代表明文数据4字节,返回给应用层的值无法评估。退一步讲,假设密文数据和明文数据一致,那么应用层会在第二次接着调用SSL_write(s, plaintext+4, 96),然而,这96字节的plaintext实际上在第一次调用时,被加密过了,当第二次再被加密,加密状态机必然存在问题。

2:上面1中,既然会造成多加密一部分数据的情况,那么直接返回100是否正确,即返回多少依据被加密数据多少决定而非send/write的返回值决定?返回100,应用层确实不会多发送数据,但是由于返回了100字节,应用层会认为自己发送的数据全部被发送完成了,导致不会监听自己的fd的可写事件,那么被OpenSSL缓存的数据,也没有人主动触发进行发送了。

解决方法

当发送阻塞的情况下,OpenSSL首先会缓存未发送成功的112字节的密文数据;其次,记录此次调用加密接口待加密的明文数据大小100字节;最后返回-1,具体错误码就是EAGAIN

上面这些缓存的数据怎么用?当然是在后续操作中使用。

当应用层第二次会接着调用SSL_write(s, plaintext, 100),OpenSSL会首先会尝试发送缓存的112字节密文数据,不成功或者部分成功直接return EAGAIN,若成功,则接着将入参plaintext偏移100字节(第一次调用时记录的值),同样入参长度也会减去100,剩余数据接着第一次放时描述的逻辑处理。

换句话说,当你SSL_write返回-1/EAGAIN时,不代表你数据没被处理;其次这种情况下你的入参buffer只可以被追加数据。

猜你喜欢

转载自blog.csdn.net/mrpre/article/details/81059856