bool nabto_fallback_connect_u_event(uint16_t packetLength, nabto_packet_header* hdr) { nabto_connect* con; uint8_t* startOfPayloads; uint8_t* endOfPayloads; uint8_t* noncePayloadStart; uint16_t noncePayloadLength; uint8_t type; uint8_t flags; uint8_t* ptr; bool isReliable; bool hasKeepAlive; con = nabto_find_connection(hdr->nsi_sp); if (con == 0) { NABTO_LOG_TRACE((PRInsi " No connection found.", MAKE_NSI_PRINTABLE(hdr->nsi_cp, hdr->nsi_sp, 0))); return false; } startOfPayloads = nabtoCommunicationBuffer + hdr->hlen; endOfPayloads = nabtoCommunicationBuffer + packetLength; if (!find_payload(startOfPayloads, endOfPayloads, NP_PAYLOAD_TYPE_NONCE, &noncePayloadStart, &noncePayloadLength)) { NABTO_LOG_ERROR((PRI_tcp_fb "No nonce payload in GW_CONN_U packet.", TCP_FB_ARGS(con))); return false; } if (noncePayloadLength < 2) { NABTO_LOG_ERROR((PRI_tcp_fb "The payload received is too small to contain both flags and type.", TCP_FB_ARGS(con))); return false; } ptr = noncePayloadStart + NP_PAYLOAD_HDR_BYTELENGTH; READ_U8(type, ptr); ptr++; READ_U8(flags, ptr); ptr++; isReliable = (flags & NP_GW_CONN_U_FLAG_RELIABLE) != 0; hasKeepAlive = (flags & NP_GW_CONN_U_FLAG_KEEP_ALIVE) != 0; con->tcpFallbackConnectionState = UTFS_READY_FOR_DATA; // If the connection is still resolving upgrade it to a fallback connection. if (isReliable) { con->fbConAttr |= CON_ATTR_NO_RETRANSMIT; NABTO_LOG_DEBUG((PRI_tcp_fb "connection needs no retransmition", TCP_FB_ARGS(con))); } if (!hasKeepAlive) { con->fbConAttr |= CON_ATTR_NO_KEEP_ALIVE; NABTO_LOG_DEBUG((PRI_tcp_fb "connection needs no keep alive", TCP_FB_ARGS(con))); } else { NABTO_LOG_DEBUG((PRI_tcp_fb "connection needs keep alive", TCP_FB_ARGS(con))); } return true; }
void unabto_tcp_fallback_handle_write(nabto_connect* con) { ssize_t status; int dataToSend; unabto_tcp_fallback_connection* fbConn = &fbConns[nabto_connection_index(con)]; UNABTO_ASSERT(fbConn->sendBufferSent <= fbConn->sendBufferLength); dataToSend = fbConn->sendBufferLength - fbConn->sendBufferSent; status = send(fbConn->socket, fbConn->sendBuffer + fbConn->sendBufferSent, dataToSend, MSG_NOSIGNAL); if (status > 0) { fbConn->sendBufferSent += status; } else if (status < 0) { int err = errno; if ((err == EAGAIN) || err == EWOULDBLOCK) { } else { NABTO_LOG_ERROR((PRI_tcp_fb "Send of tcp packet failed", TCP_FB_ARGS(con))); close_tcp_socket(con); return; } } if (fbConn->sendBufferSent > fbConn->sendBufferLength) { NABTO_LOG_FATAL(("fbConn->sendBufferSent(%" PRIsize ") > fbConn->sendBufferLength(%" PRIsize "), that should not be possible", fbConn->sendBufferSent, fbConn->sendBufferLength)); } if (fbConn->sendBufferSent == fbConn->sendBufferLength) { fbConn->sendBufferLength = 0; } }
unabto_tcp_fallback_error unabto_tcp_fallback_write(nabto_connect* con, uint8_t* buffer, size_t bufferLength) { unabto_tcp_fallback_connection* fbConn = &fbConns[nabto_connection_index(con)]; unabto_tcp_fallback_error status = UTFE_OK; if (fbConn->sendBufferLength == 0) { memcpy(fbConn->sendBuffer, buffer, bufferLength); fbConn->sendBufferLength = bufferLength; fbConn->sendBufferSent = 0; unabto_tcp_fallback_handle_write(con); status = UTFE_OK; NABTO_LOG_TRACE((PRI_tcp_fb "Succesful queue of tcp fallback packet.", TCP_FB_ARGS(con))); } else { NABTO_LOG_TRACE((PRI_tcp_fb "Could not enqueue tcp fallback packet.", TCP_FB_ARGS(con))); status = UTFE_QUEUE_FULL; } return status; }
bool unabto_tcp_fallback_event(nabto_connect* con) { if (con->tcpFallbackConnectionState == UTFS_CONNECTED) { uint8_t handshakePacket[1500]; size_t handshakePacketLength; if (!build_handshake_packet(con, handshakePacket, 1500, &handshakePacketLength)) { NABTO_LOG_ERROR((PRI_tcp_fb "Could not create handshake packet.", TCP_FB_ARGS(con))); unabto_tcp_fallback_close(con); return false; } if (unabto_tcp_fallback_write(con, handshakePacket, handshakePacketLength) != UTFE_OK) { NABTO_LOG_ERROR((PRI_tcp_fb "Could not send handshake packet.", TCP_FB_ARGS(con))); return false; } con->tcpFallbackConnectionState = UTFS_HANDSHAKE_SENT; return true; } return false; }
bool unabto_tcp_fallback_handle_write(nabto_connect* con) { ssize_t status; int dataToSend; bool canMaybeSendMoreData = false; unabto_tcp_fallback_connection* fbConn = &fbConns[nabto_connection_index(con)]; UNABTO_ASSERT(fbConn->sendBufferSent <= fbConn->sendBufferLength); dataToSend = fbConn->sendBufferLength - fbConn->sendBufferSent; if (dataToSend == 0) { return false; } NABTO_LOG_TRACE(("data to send %i, sendBufferLength %i, sendBufferSent %i", dataToSend, fbConn->sendBufferLength, fbConn->sendBufferSent)); status = send(fbConn->socket, fbConn->sendBuffer + fbConn->sendBufferSent, dataToSend, MSG_NOSIGNAL); NABTO_LOG_TRACE(("tcp send status: %i", status)); if (status > 0) { fbConn->sendBufferSent += status; canMaybeSendMoreData = true; } else if (status < 0) { int err = errno; if ((err == EAGAIN) || err == EWOULDBLOCK) { canMaybeSendMoreData = false; } else { NABTO_LOG_ERROR((PRI_tcp_fb "Send of tcp packet failed", TCP_FB_ARGS(con))); close_tcp_socket(con); canMaybeSendMoreData = false; return canMaybeSendMoreData; } } if (fbConn->sendBufferSent > fbConn->sendBufferLength) { NABTO_LOG_FATAL(("fbConn->sendBufferSent(%" PRIsize ") > fbConn->sendBufferLength(%" PRIsize "), that should not be possible", fbConn->sendBufferSent, fbConn->sendBufferLength)); } if (fbConn->sendBufferSent == fbConn->sendBufferLength) { fbConn->sendBufferLength = 0; fbConn->sendBufferSent = 0; canMaybeSendMoreData = false; } NABTO_LOG_TRACE(("state after send, sendBufferLength %i, sendBufferSent %i", fbConn->sendBufferLength, fbConn->sendBufferSent)); return canMaybeSendMoreData; }
bool unabto_tcp_fallback_connect(nabto_connect* con) { unabto_tcp_fallback_connection* fbConn = &fbConns[nabto_connection_index(con)]; int status; int flags; fbConn->socket = socket(AF_INET, SOCK_STREAM, 0); if (fbConn->socket < 0) { NABTO_LOG_ERROR((PRI_tcp_fb "Could not create socket for tcp fallback.", TCP_FB_ARGS(con))); return false; } flags = 1; if (setsockopt(fbConn->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flags, sizeof(int)) != 0) { NABTO_LOG_ERROR(("Could not set socket option TCP_NODELAY")); } #ifndef WIN32 flags = fcntl(fbConn->socket, F_GETFL, 0); if (flags < 0) { NABTO_LOG_ERROR((PRI_tcp_fb "fcntl fail", TCP_FB_ARGS(con))); closesocket(fbConn->socket); fbConn->socket = INVALID_SOCKET; con->tcpFallbackConnectionState = UTFS_CLOSED; return false; } if (fcntl(fbConn->socket, F_SETFL, flags | O_NONBLOCK) < 0) { NABTO_LOG_ERROR((PRI_tcp_fb "fcntl fail", TCP_FB_ARGS(con))); closesocket(fbConn->socket); fbConn->socket = INVALID_SOCKET; con->tcpFallbackConnectionState = UTFS_CLOSED; return false; } flags = 1; if(setsockopt(fbConn->socket, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags)) < 0) { NABTO_LOG_ERROR(("could not enable KEEPALIVE")); } #ifndef __MACH__ flags = 9; if(setsockopt(fbConn->socket, SOL_TCP, TCP_KEEPCNT, &flags, sizeof(flags)) < 0) { NABTO_LOG_ERROR(("could not set TCP_KEEPCNT")); } flags = 60; if(setsockopt(fbConn->socket, SOL_TCP, TCP_KEEPIDLE, &flags, sizeof(flags)) < 0) { NABTO_LOG_ERROR(("could not set TCP_KEEPIDLE")); } flags = 60; if(setsockopt(fbConn->socket, SOL_TCP, TCP_KEEPINTVL, &flags, sizeof(flags)) < 0) { NABTO_LOG_ERROR(("could not set TCP KEEPINTVL")); } #else flags = 60; if(setsockopt(fbConn->socket, IPPROTO_TCP, TCP_KEEPALIVE, &flags, sizeof(flags)) < 0) { NABTO_LOG_ERROR(("could not set TCP_KEEPCNT")); } #endif #endif memset(&fbConn->fbHost,0,sizeof(struct sockaddr_in)); fbConn->fbHost.sin_family = AF_INET; fbConn->fbHost.sin_addr.s_addr = htonl(con->fallbackHost.addr); fbConn->fbHost.sin_port = htons(con->fallbackHost.port); NABTO_LOG_INFO((PRI_tcp_fb "Ep. " PRIep, TCP_FB_ARGS(con), MAKE_EP_PRINTABLE(con->fallbackHost))); status = connect(fbConn->socket, (struct sockaddr*)&fbConn->fbHost, sizeof(struct sockaddr_in)); if (status == 0) { con->tcpFallbackConnectionState = UTFS_CONNECTED; } else { int err = errno; // err is two on windows. if (err == EINPROGRESS) { con->tcpFallbackConnectionState = UTFS_CONNECTING; } else { NABTO_LOG_ERROR((PRI_tcp_fb "Could not connect to fallback tcp endpoint. %s", TCP_FB_ARGS(con), strerror(errno))); closesocket(fbConn->socket); fbConn->socket = INVALID_SOCKET; con->tcpFallbackConnectionState = UTFS_CLOSED; return false; } } #ifdef WIN32 flags = 1; if (ioctlsocket(fbConn->socket, FIONBIO, &flags) != 0) { NABTO_LOG_ERROR((PRI_tcp_fb "ioctlsocket fail", TCP_FB_ARGS(con))); closesocket(fbConn->socket); fbConn->socket = INVALID_SOCKET; con->tcpFallbackConnectionState = UTFS_CLOSED; return false; } #endif return true; }
void unabto_tcp_fallback_read_packet(nabto_connect* con) { unabto_tcp_fallback_connection* fbConn = &fbConns[nabto_connection_index(con)]; while(true) { if (fbConn->recvBufferLength < 16) { int status = recv(fbConn->socket, fbConn->recvBuffer + fbConn->recvBufferLength, 16-fbConn->recvBufferLength, 0); int err = errno; if (status < 0) { if ((err == EAGAIN) || err == EWOULDBLOCK) { return; } else { NABTO_LOG_ERROR((PRI_tcp_fb "unabto_tcp_fallback_read_single_packet failed", TCP_FB_ARGS(con))); unabto_tcp_fallback_close(con); return; } } else if (status == 0) { NABTO_LOG_INFO((PRI_tcp_fb "TCP fallback connection closed by peer", TCP_FB_ARGS(con))); unabto_tcp_fallback_close(con); return; } else { fbConn->recvBufferLength+=status; } } if (fbConn->recvBufferLength >= 16) { uint16_t packetLength; int status; int err; READ_U16(packetLength, fbConn->recvBuffer+14); status = recv(fbConn->socket, fbConn->recvBuffer + fbConn->recvBufferLength, packetLength - fbConn->recvBufferLength, 0); err = errno; if (status < 0) { if ((err == EAGAIN) || err == EWOULDBLOCK) { return; } else { NABTO_LOG_ERROR((PRI_tcp_fb "Tcp read failed", TCP_FB_ARGS(con))); unabto_tcp_fallback_close(con); return; } } else if (status == 0) { NABTO_LOG_INFO((PRI_tcp_fb "TCP fallback connection closed by peer", TCP_FB_ARGS(con))); unabto_tcp_fallback_close(con); return; } else { fbConn->recvBufferLength += status; } if (fbConn->recvBufferLength == packetLength) { message_event event; event.type = MT_TCP_FALLBACK; memcpy(nabtoCommunicationBuffer, fbConn->recvBuffer, fbConn->recvBufferLength); NABTO_LOG_TRACE((PRI_tcp_fb "Received fallback packet length %" PRIsize, TCP_FB_ARGS(con), fbConn->recvBufferLength)); nabto_message_event(&event, fbConn->recvBufferLength); NABTO_LOG_TRACE((PRI_tcp_fb "fallback packet done\n==================================================", TCP_FB_ARGS(con))); fbConn->recvBufferLength = 0; } } } }