/*! * @brief Allocates a streaming TCP server channel. * @param remote Pointer to the remote instance. * @param packet Pointer to the request packet. * @returns Indication of success or failure. * @retval ERROR_SUCCESS Opening the server channel completed successfully. */ DWORD request_net_tcp_server_channel_open(Remote * remote, Packet * packet) { DWORD dwResult = ERROR_SUCCESS; TcpServerContext * ctx = NULL; Packet * response = NULL; char * localHost = NULL; StreamChannelOps chops = { 0 }; USHORT localPort = 0; BOOL v4Fallback = FALSE; do { response = packet_create_response(packet); if (!response) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. response == NULL", ERROR_NOT_ENOUGH_MEMORY); } ctx = (TcpServerContext *)malloc(sizeof(TcpServerContext)); if (!ctx) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. ctx == NULL", ERROR_NOT_ENOUGH_MEMORY); } memset(ctx, 0, sizeof(TcpServerContext)); ctx->remote = remote; localPort = (USHORT)(packet_get_tlv_value_uint(packet, TLV_TYPE_LOCAL_PORT) & 0xFFFF); if (!localPort) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. localPort == NULL", ERROR_INVALID_HANDLE); } localHost = packet_get_tlv_value_string(packet, TLV_TYPE_LOCAL_HOST); ctx->fd = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0); if (ctx->fd == INVALID_SOCKET) { v4Fallback = TRUE; } else { int no = 0; if (setsockopt(ctx->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) == SOCKET_ERROR) { // fallback to ipv4 - we're probably running on Windows XP or earlier here, which means that to // support IPv4 and IPv6 we'd need to create two separate sockets. IPv6 on XP isn't that common // so instead, we'll just revert back to v4 and listen on that one address instead. closesocket(ctx->fd); v4Fallback = TRUE; } } struct sockaddr_in6 sockAddr = { 0 }; DWORD sockAddrSize = 0; if (v4Fallback) { struct sockaddr_in* v4Addr = (struct sockaddr_in*)&sockAddr; v4Addr->sin_addr.s_addr = localHost == NULL ? htons(INADDR_ANY) : inet_addr(localHost); v4Addr->sin_family = AF_INET; v4Addr->sin_port = htons(localPort); sockAddrSize = sizeof(struct sockaddr_in); } else { // TODO: add IPv6 address binding support sockAddr.sin6_addr = in6addr_any; sockAddr.sin6_family = AF_INET6; sockAddr.sin6_port = htons(localPort); sockAddrSize = sizeof(struct sockaddr_in6); } if (bind(ctx->fd, (SOCKADDR *)&sockAddr, sockAddrSize) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. bind failed"); } if (listen(ctx->fd, SOMAXCONN) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. listen failed"); } ctx->notify = WSACreateEvent(); if (ctx->notify == WSA_INVALID_EVENT) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSACreateEvent failed"); } if (WSAEventSelect(ctx->fd, ctx->notify, FD_ACCEPT) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSAEventSelect failed"); } ctx->ipv6 = !v4Fallback; memset(&chops, 0, sizeof(StreamChannelOps)); chops.native.context = ctx; chops.native.close = tcp_channel_server_close; ctx->channel = channel_create_stream(0, CHANNEL_FLAG_SYNCHRONOUS, &chops); if (!ctx->channel) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. channel_create_stream failed", ERROR_INVALID_HANDLE); } scheduler_insert_waitable(ctx->notify, ctx, NULL, (WaitableNotifyRoutine)tcp_channel_server_notify, NULL); packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(ctx->channel)); dprintf("[TCP-SERVER] request_net_tcp_server_channel_open. tcp server %s:%d on channel %d", localHost, localPort, channel_get_id(ctx->channel)); } while (0); packet_transmit_response(dwResult, remote, response); do { if (dwResult == ERROR_SUCCESS) { break; } dprintf("[TCP-SERVER] Error encountered %u 0x%x", dwResult, dwResult); if (!ctx) { break; } if (ctx->fd) { dprintf("[TCP-SERVER] Destroying socket"); closesocket(ctx->fd); } if (ctx->channel) { dprintf("[TCP-SERVER] Destroying channel"); channel_destroy(ctx->channel, packet); } free(ctx); } while (0); return dwResult; }
/* * Creates a connection to a remote host and builds a logical channel to * represent it. * */ DWORD create_tcp_client_channel(Remote *remote, LPCSTR remoteHost, USHORT remotePort, Channel **outChannel) { StreamChannelOps chops; TcpClientContext *ctx = NULL; DWORD result = ERROR_SUCCESS; Channel *channel = NULL; struct sockaddr_in s; SOCKET clientFd = 0; if (outChannel) *outChannel = NULL; dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d", remoteHost, remotePort ); do { // Allocate a client socket if ((clientFd = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) == INVALID_SOCKET) { clientFd = 0; result = GetLastError(); break; } s.sin_family = AF_INET; s.sin_port = htons(remotePort); s.sin_addr.s_addr = inet_addr(remoteHost); // Resolve the host name locally if (s.sin_addr.s_addr == (DWORD)-1) { struct hostent *h; if (!(h = gethostbyname(remoteHost))) { result = GetLastError(); break; } memcpy(&s.sin_addr.s_addr, h->h_addr, h->h_length); } dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d connecting...", remoteHost, remotePort ); // Try to connect to the host/port if (connect(clientFd, (struct sockaddr *)&s, sizeof(s)) == SOCKET_ERROR) { result = GetLastError(); break; } dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d connected!", remoteHost, remotePort ); // Allocate the client context for tracking the connection if (!(ctx = (TcpClientContext *)malloc( sizeof(TcpClientContext)))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } // Initialize the context attributes memset(ctx, 0, sizeof(TcpClientContext)); ctx->remote = remote; ctx->fd = clientFd; // Initialize the channel operations structure memset(&chops, 0, sizeof(chops)); chops.native.context = ctx; chops.native.write = tcp_channel_client_write; chops.native.close = tcp_channel_client_close; dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d creating the channel", remoteHost, remotePort ); // Allocate an uninitialized channel for associated with this connection if (!(channel = channel_create_stream(0, 0,&chops))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } // Save the channel context association ctx->channel = channel; // Finally, create a waitable event and insert it into the scheduler's // waitable list dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d creating the notify", remoteHost, remotePort ); if ((ctx->notify = WSACreateEvent())) { WSAEventSelect(ctx->fd, ctx->notify, FD_READ|FD_CLOSE); dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d created the notify %.8x", remoteHost, remotePort, ctx->notify ); scheduler_insert_waitable( ctx->notify, ctx, (WaitableNotifyRoutine)tcp_channel_client_local_notify); } } while (0); dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d all done", remoteHost, remotePort ); // Clean up on failure if (result != ERROR_SUCCESS) { dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d cleaning up failed connection", remoteHost, remotePort ); if (ctx) free_tcp_client_context(ctx); if (clientFd) closesocket(clientFd); channel = NULL; } if (outChannel) *outChannel = channel; return result; }
/*! * @brief Create a TCP client channel from a socket. * @param serverCtx Pointer to the TCP server context. * @param sock The socket handle. * @returns Pointer to the newly created client context. */ TcpClientContext * tcp_channel_server_create_client(TcpServerContext * serverCtx, SOCKET sock) { DWORD dwResult = ERROR_SUCCESS; TcpClientContext * clientctx = NULL; StreamChannelOps chops = { 0 }; do { if (!serverCtx) { BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_create_client. serverCtx == NULL", ERROR_INVALID_HANDLE); } clientctx = (TcpClientContext *)malloc(sizeof(TcpClientContext)); if (!clientctx) { BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_create_client. clientctx == NULL", ERROR_NOT_ENOUGH_MEMORY); } memset(clientctx, 0, sizeof(TcpClientContext)); clientctx->remote = serverCtx->remote; clientctx->fd = sock; clientctx->notify = WSACreateEvent(); if (clientctx->notify == WSA_INVALID_EVENT) { BREAK_ON_WSAERROR("[TCP-SERVER] tcp_channel_server_create_client. WSACreateEvent failed"); } if (WSAEventSelect(clientctx->fd, clientctx->notify, FD_READ | FD_CLOSE) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] tcp_channel_server_create_client. WSAEventSelect failed"); } memset(&chops, 0, sizeof(StreamChannelOps)); chops.native.context = clientctx; chops.native.write = tcp_channel_client_write; chops.native.close = tcp_channel_client_close; clientctx->channel = channel_create_stream(0, 0, &chops); if (!clientctx->channel) { BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_create_client. clientctx->channel == NULL", ERROR_INVALID_HANDLE); } dwResult = scheduler_insert_waitable(clientctx->notify, clientctx, NULL, (WaitableNotifyRoutine)tcp_channel_client_local_notify, NULL); } while (0); if (dwResult != ERROR_SUCCESS) { if (clientctx) { free(clientctx); clientctx = NULL; } } return clientctx; }
/*! * @brief Allocates a streaming TCP server channel. * @param remote Pointer to the remote instance. * @param packet Pointer to the request packet. * @returns Indication of success or failure. * @retval ERROR_SUCCESS Opening the server channel completed successfully. */ DWORD request_net_tcp_server_channel_open(Remote * remote, Packet * packet) { DWORD dwResult = ERROR_SUCCESS; TcpServerContext * ctx = NULL; Packet * response = NULL; char * lhost = NULL; SOCKADDR_IN saddr = { 0 }; StreamChannelOps chops = { 0 }; USHORT lport = 0; do { response = packet_create_response(packet); if (!response) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. response == NULL", ERROR_NOT_ENOUGH_MEMORY); } ctx = (TcpServerContext *)malloc(sizeof(TcpServerContext)); if (!ctx) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. ctx == NULL", ERROR_NOT_ENOUGH_MEMORY); } memset(ctx, 0, sizeof(TcpServerContext)); ctx->remote = remote; lport = (USHORT)(packet_get_tlv_value_uint(packet, TLV_TYPE_LOCAL_PORT) & 0xFFFF); if (!lport) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. lport == NULL", ERROR_INVALID_HANDLE); } lhost = packet_get_tlv_value_string(packet, TLV_TYPE_LOCAL_HOST); if (!lhost) { lhost = "0.0.0.0"; } ctx->fd = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0); if (ctx->fd == INVALID_SOCKET) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSASocket failed"); } saddr.sin_family = AF_INET; saddr.sin_port = htons(lport); saddr.sin_addr.s_addr = inet_addr(lhost); if (bind(ctx->fd, (SOCKADDR *)&saddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR) { #ifdef _WIN32 dwResult = WSAGetLastError(); if (dwResult != WSAEADDRNOTAVAIL) #else dwResult = errno; if (dwResult != EADDRNOTAVAIL) #endif { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. bind failed"); } dprintf("[TCP-SERVER] Failed to bind to %s, trying 0.0.0.0 ...", lhost); // try again, but this time bind to any/all interfaces. lhost = "0.0.0.0"; saddr.sin_addr.s_addr = inet_addr(lhost); if (bind(ctx->fd, (SOCKADDR *)&saddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. bind failed"); } dwResult = ERROR_SUCCESS; } if (listen(ctx->fd, SOMAXCONN) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. listen failed"); } ctx->notify = WSACreateEvent(); if (ctx->notify == WSA_INVALID_EVENT) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSACreateEvent failed"); } if (WSAEventSelect(ctx->fd, ctx->notify, FD_ACCEPT) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSAEventSelect failed"); } memset(&chops, 0, sizeof(StreamChannelOps)); chops.native.context = ctx; chops.native.close = tcp_channel_server_close; ctx->channel = channel_create_stream(0, CHANNEL_FLAG_SYNCHRONOUS, &chops); if (!ctx->channel) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. channel_create_stream failed", ERROR_INVALID_HANDLE); } scheduler_insert_waitable(ctx->notify, ctx, NULL, (WaitableNotifyRoutine)tcp_channel_server_notify, NULL); packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(ctx->channel)); dprintf("[TCP-SERVER] request_net_tcp_server_channel_open. tcp server %s:%d on channel %d", lhost, lport, channel_get_id(ctx->channel)); } while (0); packet_transmit_response(dwResult, remote, response); do { if (dwResult == ERROR_SUCCESS) { break; } dprintf("[TCP-SERVER] Error encountered %u 0x%x", dwResult, dwResult); if (!ctx) { break; } if (ctx->fd) { dprintf("[TCP-SERVER] Destroying socket"); closesocket(ctx->fd); } if (ctx->channel) { dprintf("[TCP-SERVER] Destroying channel"); channel_destroy(ctx->channel, packet); } free(ctx); } while (0); return dwResult; }