J'ai un client-serveur très simple avec un socket bloquant qui communique en duplex intégral. J'ai activé SSL/TLS pour l'application. Le modèle est celui d'un producteur-consommateur typique. Le client produit les données, les envoie au serveur et le serveur les traite. Le seul problème est que, de temps en temps, le serveur renvoie des données au client, qui les traite en conséquence. Vous trouverez ci-dessous un pseudo-code très simple de l'application :
1 Client: 2 ------- 3 while (true) 4 { 5 if (poll(pollin, timeout=0) || 0 < SSL_pending(ssl)) 6 { 7 SSL_read(); 8 // Handle WANT_READ or WANT_WRITE appropriately. 9 // If no error, handle the received control message. 10 } 11 // produce data. 12 while (!poll(pollout)) 13 ; // Wait until the pipe is ready for a send(). 14 SSL_write(); 15 // Handle WANT_READ or WANT_WRITE appropriately. 16 if (time to renegotiate) 17 SSL_renegotiate(ssl); 18 } 19 20 Server: 21 ------- 22 while (true) 23 { 24 if (poll(pollin, timeout=1s) || 0 < SSL_pending(ssl)) 25 { 26 SSL_read(); 27 // Handle WANT_READ or WANT_WRITE appropriately. 28 // If no error, consume data. 29 } 30 if (control message needs to be sent) 31 { 32 while (!poll(pollout)) 33 ; // Wait until the pipe is ready for a send(). 34 SSL_write(); 35 // Handle WANT_READ or WANT_WRITE appropriately. 36 } 37 }
Le problème survient lorsque, à des fins de test, je force la renégociation SSL (lignes 16-17). La session démarre gentiment, mais après un certain temps, j'obtiens les erreurs suivantes :
Client:
-------
error:140940F5:SSL routines:SSL3_READ_BYTES:unexpected record
Server:
-------
error:140943F2:SSL routines:SSL3_READ_BYTES:sslv3 alert unexpected message
Il s'avère qu'à peu près au même moment où le client initie une renégociation (ligne 14), le serveur finit par envoyer des données d'application au client (ligne 34). Le client, dans le cadre du processus de renégociation, reçoit ces données d'application et bombarde avec une erreur "unexpected record". De même, lorsque le serveur effectue la réception suivante (ligne 26), il finit par recevoir des données de renégociation alors qu'il attendait des données d'application.
Qu'est-ce que je fais de mal ? Comment dois-je gérer/tester les renégociations SSL avec un canal full-duplex. Notez qu'il n'y a pas de threads impliqués. Il s'agit d'un modèle simple à un seul thread avec des lectures/écritures se produisant à chaque extrémité du socket.
UPDATE : Pour vérifier qu'il n'y a pas de problème avec l'application que j'ai écrite, j'ai même pu reproduire cela assez confortablement avec les implémentations s_client et s_server d'OpenSSL. J'ai démarré un s_server et une fois que le s_client s'est connecté au serveur, j'ai programmé l'envoi d'un paquet de données d'application du serveur au client et un paquet de 'R' (demandes de renégociation) du client au serveur. Finalement, les deux échouent exactement de la même manière que celle décrite ci-dessus.
s_client:
RENEGOTIATING
4840:error:140940F5:SSL routines:SSL3_READ_BYTES:unexpected record:s3_pkt.c:1258:
s_server:
Read BLOCK
ERROR
4838:error:140943F2:SSL routines:SSL3_READ_BYTES:sslv3 alert unexpected message:s3_pkt.c:1108:SSL alert number 10
4838:error:140940E5:SSL routines:SSL3_READ_BYTES:ssl handshake failure:s3_pkt.c:1185:
UPDATE 2 : Ok. Comme suggéré par David, j'ai retravaillé l'application de test pour utiliser des sockets non bloquants et toujours faire SSL_read et SSL_write en premier et faire la sélection en fonction de ce qu'ils retournent et j'obtiens toujours les mêmes erreurs pendant les renégociations (SSL_write finit par obtenir des données d'application de l'autre côté au milieu de la renégociation). La question est la suivante : à tout moment, si SSL_read renvoie WANT_READ, puis-je supposer que c'est parce qu'il n'y a rien dans le tuyau et continuer avec SSL_write puisque j'ai quelque chose à écrire ? Sinon, c'est probablement la raison pour laquelle je me retrouve avec des erreurs. Soit ça, soit je fais mal la renégociation. Remarque : si SSL_read renvoie WANT_WRITE, je fais toujours une sélection et j'appelle à nouveau SSL_read.