@pbreed,
Let me know if I miss anything in the below:
So in your case 2 and 3...
Case 2 should return a WOLFSSL_SUCCESS because all the data was encrypted and handed off to the TCP stack. Once it's handed off to the TCP stack wolfSSL no longer has any control of it or knowledge of if only a partial send was achieved. wolfSSL is wholly disconnected from the transport medium, this is why we are able to provide custom I/O callbacks that work over any mode of transport and we are not limited to TCP only.
Case 3 is the only case above that should return a WANT_WRITE because only a partial encrypt was achieved and not all of the data was handed off to the TCP stack.
Can I change the behavior in wolfSSL_write by limiting the writes to some maximum that guarantees it fits in one encrypted block?
Yes absolutely. If you need the write callback to behave differently you could either modify the default one that expects TCP or you could write your own "send" function that says "if the sz passed in is larger than 16k (the limit of a TLS packet) only try to encrypt and send 16k at most, return the amount encrypted and written successfully. (which will be turned into a WANT_WRITE when it bubbles out).
Here is some basic documentation for the return codes and behavior that are expected from the I/O callbacks:
int myReceive(WOLFSSL *ssl, char *buf, int sz, void *ctx)
{
// ssl = the current SSL object, cast to void if unused
// buf = the buffer to receive the message, always used
// sz = the size in bytes to receive, always used
// ctx = a custom user context, can be anything, a structure, char buf, variable, cast to the correct type and use as needed, cast to void if unused.
// RULE1: Only return the amount received.
// RULE2: In the case of a failed receive return one of the following errors as appropriate, returning 0 will
// trigger an automatic re-receive attempt without returning control to the calling application.
// WOLFSSL_CBIO_ERR_GENERAL = -1, /* general unexpected err */
// WOLFSSL_CBIO_ERR_WANT_READ = -2, /* need to call read again */
// WOLFSSL_CBIO_ERR_WANT_WRITE = -2, /* need to call write again */
// WOLFSSL_CBIO_ERR_CONN_RST = -3, /* connection reset */
// WOLFSSL_CBIO_ERR_ISR = -4, /* interrupt */
// WOLFSSL_CBIO_ERR_CONN_CLOSE = -5, /* connection closed or epipe */
// WOLFSSL_CBIO_ERR_TIMEOUT = -6 /* socket timeout */
// RULE3: In the case of a partial receive, only return the amount read, call wolfSSL_read again
// with the exact same parameters (including sz), the state machine will internally keep
// track of received vs remainder and will handle the remainder appropriately.
}
int mySend(WOLFSSL *ssl, char *buf, int sz, void *ctx)
{
// ssl = the current SSL object, cast to void if unused
// buf = the message to send, always used
// sz = the size in bytes to send, always used
// ctx = a custom user context, can be anything, a structure, char buf, variable, cast to the correct type and use as needed, cast to void if unused.
// RULE1: Only return the amount sent.
// RULE2: In the case of a failed send return one of the following errors as appropriate, returning 0 will
// trigger an automatic re-send attempt without returning control to the calling application.
// WOLFSSL_CBIO_ERR_GENERAL = -1, /* general unexpected err */
// WOLFSSL_CBIO_ERR_WANT_READ = -2, /* need to call read again */
// WOLFSSL_CBIO_ERR_WANT_WRITE = -2, /* need to call write again */
// WOLFSSL_CBIO_ERR_CONN_RST = -3, /* connection reset */
// WOLFSSL_CBIO_ERR_ISR = -4, /* interrupt */
// WOLFSSL_CBIO_ERR_CONN_CLOSE = -5, /* connection closed or epipe */
// WOLFSSL_CBIO_ERR_TIMEOUT = -6 /* socket timeout */
// RULE3: In the case of a partial send, only return the amount written, call wolfSSL_write again
// with the exact same parameters (including sz), the state machine will internally keep
// track of send vs remainder and will handle the remainder appropriately.
}
// Register your callbacks in place of the defaults:
wolfSSL_CTX_SetIORecv(ctx, mySend);
wolfSSL_CTX_SetIOSend(ctx, myReceive);
Let me know if this helps.
Warm Regards,
K