static ssize_t qsossl_send(struct connectdata * conn, int sockindex, const void * mem, size_t len, CURLcode * curlcode) { /* SSL_Write() is said to return 'int' while write() and send() returns 'size_t' */ int rc; rc = SSL_Write(conn->ssl[sockindex].handle, (void *) mem, (int) len); if(rc < 0) { switch(rc) { case SSL_ERROR_BAD_STATE: /* The operation did not complete; the same SSL I/O function should be called again later. This is basically an EWOULDBLOCK equivalent. */ *curlcode = CURLE_AGAIN; return -1; case SSL_ERROR_IO: switch (errno) { case EWOULDBLOCK: case EINTR: *curlcode = CURLE_AGAIN; return -1; } failf(conn->data, "SSL_Write() I/O error: %s", strerror(errno)); *curlcode = CURLE_SEND_ERROR; return -1; } /* An SSL error. */ failf(conn->data, "SSL_Write() returned error %s", SSL_Strerror(rc, NULL)); *curlcode = CURLE_SEND_ERROR; return -1; } return (ssize_t) rc; /* number of bytes */ }
/* Read/write from/to client at the specified socket. * * ctx The SSL/TLS connection data. * threadData The SSL connection data for a thread. * sslConn The SSL connection data object. * returns EXIT_FAILURE on failure and EXIT_SUCCESS otherwise. */ static int SSLConn_ReadWrite(SSLConn_CTX* ctx, ThreadData* threadData, SSLConn* sslConn) { int ret; int len; /* Perform TLS handshake if in accept state. */ switch (sslConn->state) { case ACCEPT: ret = SSL_Accept(sslConn->ssl, &ctx->acceptTime, &ctx->resumeTime); if (ret == 0) { printf("ERROR: Accept failed\n"); SSLConn_Close(ctx, threadData, sslConn); return EXIT_FAILURE; } if (ret == 1) sslConn->state = READ; break; case READ: { char buffer[NUM_READ_BYTES]; len = ctx->bufferLen; if (ctx->maxBytes > 0) { len = min(len, ctx->maxBytes - ctx->totalReadBytes); } if (len == 0) break; /* Read application data. */ ret = SSL_Read(sslConn->ssl, buffer, len, &ctx->totalReadBytes, &ctx->readTime); if (ret == 0) { SSLConn_Close(ctx, threadData, sslConn); return EXIT_FAILURE; } } if (ret != 1) break; sslConn->state = WRITE; case WRITE: len = ctx->replyLen; if (ctx->maxBytes > 0) { len = min(len, ctx->maxBytes - ctx->totalWriteBytes); } if (len == 0) break; /* Write application data. */ ret = SSL_Write(sslConn->ssl, reply, len, &ctx->totalWriteBytes, &ctx->writeTime); if (ret == 0) { printf("ERROR: Write failed\n"); SSLConn_Close(ctx, threadData, sslConn); return EXIT_FAILURE; } if (ret == 1) sslConn->state = READ; break; case CLOSED: break; } return EXIT_SUCCESS; }
HRESULT Library_spot_net_security_native_Microsoft_SPOT_Net_Security_SslNative::ReadWriteHelper( CLR_RT_StackFrame& stack, bool isWrite ) { NATIVE_PROFILE_CLR_NETWORK(); TINYCLR_HEADER(); CLR_RT_HeapBlock* socket = stack.Arg0().Dereference(); CLR_RT_HeapBlock_Array* arrData = stack.Arg1().DereferenceArray(); CLR_INT32 offset = stack.Arg2().NumericByRef().s4; CLR_INT32 count = stack.Arg3().NumericByRef().s4; CLR_INT32 timeout_ms = stack.Arg4().NumericByRef().s4; CLR_UINT8* buffer; CLR_RT_HeapBlock hbTimeout; CLR_INT32 totReadWrite; bool fRes = true; CLR_INT64 *timeout; int result = 0; CLR_INT32 handle; if(count == 0) { stack.SetResult_I4( 0 ); TINYCLR_SET_AND_LEAVE(S_OK); } FAULT_ON_NULL(socket); handle = socket[ Library_spot_net_native_Microsoft_SPOT_Net_SocketNative::FIELD__m_Handle ].NumericByRef().s4; /* Because we could have been a rescheduled call due to a prior call that would have blocked, we need to see * if our handle has been shutdown before continuing. */ if (handle == Library_spot_net_native_Microsoft_SPOT_Net_SocketNative::DISPOSED_HANDLE) { ThrowError( stack, CLR_E_OBJECT_DISPOSED ); TINYCLR_SET_AND_LEAVE(CLR_E_PROCESS_EXCEPTION); } FAULT_ON_NULL(arrData); hbTimeout.SetInteger( timeout_ms ); TINYCLR_CHECK_HRESULT(stack.SetupTimeout( hbTimeout, timeout )); // // Push "totReadWrite" onto the eval stack. // if(stack.m_customState == 1) { stack.PushValueI4( 0 ); stack.m_customState = 2; } totReadWrite = stack.m_evalStack[ 1 ].NumericByRef().s4; buffer = arrData->GetElement( offset + totReadWrite ); count -= totReadWrite; if((offset + count + totReadWrite) > (int)arrData->m_numOfElements) TINYCLR_SET_AND_LEAVE(CLR_E_INDEX_OUT_OF_RANGE); while(count > 0) { // first make sure we have data to read or ability to write while(fRes) { if(!isWrite) { // check SSL_DataAvailable() in case SSL has already read and buffered socket data result = SSL_DataAvailable(handle); if((result > 0) || ((result < 0) && (SOCK_getlasterror() != SOCK_EWOULDBLOCK))) { break; } } result = Library_spot_net_native_Microsoft_SPOT_Net_SocketNative::Helper__SelectSocket( handle, isWrite ? 1 : 0 ); if((result > 0) || ((result < 0) && (SOCK_getlasterror() != SOCK_EWOULDBLOCK))) { break; } // non-blocking - allow other threads to run while we wait for socket activity TINYCLR_CHECK_HRESULT(g_CLR_RT_ExecutionEngine.WaitEvents( stack.m_owningThread, *timeout, CLR_RT_ExecutionEngine::c_Event_Socket, fRes )); // timeout expired if(!fRes) { result = SOCK_SOCKET_ERROR; ThrowError(stack, SOCK_ETIMEDOUT); TINYCLR_SET_AND_LEAVE( CLR_E_PROCESS_EXCEPTION ); } } // socket is in the excepted state, so let's bail out if(SOCK_SOCKET_ERROR == result) { break; } if(isWrite) { result = SSL_Write( handle, (const char*)buffer, count ); } else { result = SSL_Read( handle, (char*)buffer, count ); if(result == SSL_RESULT__WOULD_BLOCK) { continue; } } // ThrowOnError expects anything other than 0 to be a failure - so return 0 if we don't have an error if(result <= 0) { break; } buffer += result; totReadWrite += result; count -= result; // read is non-blocking if we have any data if(!isWrite && (totReadWrite > 0)) { break; } stack.m_evalStack[ 1 ].NumericByRef().s4 = totReadWrite; } stack.PopValue(); // totReadWrite stack.PopValue(); // Timeout if(result < 0) { TINYCLR_CHECK_HRESULT(ThrowOnError( stack, result )); } stack.SetResult_I4( totReadWrite ); TINYCLR_NOCLEANUP(); }
/* Read/write from/to server at the specified socket. * * ctx The SSL/TLS connection data. * sslConn The SSL connection. * returns EXIT_FAILURE on failure and EXIT_SUCCESS otherwise. */ static int SSLConn_ReadWrite(SSLConn_CTX* ctx, SSLConn* sslConn) { int ret = 0; int len; switch (sslConn->state) { case INIT: case CLOSE: break; /* Perform TLS handshake. */ case CONNECT: ret = SSL_Connect(sslConn->ssl, ctx->resume, sslConn->session, &ctx->connTime, &ctx->resumeTime); if (ret == 0) { sslConn->state = CLOSE; return EXIT_FAILURE; } if (ret == 1) { sslConn->state = WRITE; } break; case WRITE: len = ctx->replyLen; if (ctx->maxBytes > 0) { len = min(len, ctx->maxBytes - ctx->totalWriteBytes); } /* Don't write if we are done. */ if (len == 0) break; /* Write application data. */ ret = SSL_Write(sslConn->ssl, ctx->reply, len, &ctx->totalWriteBytes, &ctx->writeTime); if (ret == 0) { sslConn->state = CLOSE; return EXIT_FAILURE; } if (ret == 1) sslConn->state = READ_WAIT; break; case READ_WAIT: ret = TCP_Select(sslConn->sockfd, 0); if (ret != 1) break; sslConn->state = READ; case READ: len = ctx->bufferLen; if (ctx->maxBytes > 0) { len = min(len, ctx->maxBytes - ctx->totalReadBytes); } /* Don't read if we are done. */ if (len == 0) break; /* Read application data. */ ret = SSL_Read(sslConn->ssl, ctx->buffer, len, &ctx->totalReadBytes, &ctx->readTime); if (ret == 0) { sslConn->state = CLOSE; return EXIT_FAILURE; } if (ret == 1) { if (ctx->maxConnections > 0) sslConn->state = CLOSE; else sslConn->state = WRITE; } break; } sslConn->err = ret; return EXIT_SUCCESS; }