void tcpsock_shutdown(Socket *sock, int shutflags) { TCPSocket *tcpsock = (TCPSocket*) sock; semWait(&tcpsock->lock); if (shutflags & SHUT_RD) { if ((tcpsock->shutflags & SHUT_RD) == 0) { tcpsock->shutflags |= SHUT_RD; // do not terminate the receive semaphore. this should only happen // if we actually receive a FIN. }; }; if (shutflags & SHUT_WR) { if ((tcpsock->shutflags & SHUT_WR) == 0) { tcpsock->shutflags |= SHUT_WR; semTerminate(&tcpsock->semSendPut); }; }; semSignal(&tcpsock->lock); };
static void task_delete_hook(WIND_TCB * tcb) { if (tcb != main_thread && taskIdCurrent != main_thread) { EventInfo info; VX_COUNTING_SEMAPHORE(signal_mem); info.signal = semCInitialize(signal_mem, SEM_Q_FIFO, 0); info.pid = (UINT32)tcb; post_event(task_delete_event, &info); semTake(info.signal, WAIT_FOREVER); semTerminate(info.signal); } }
static void tcpsock_close(Socket *sock) { TCPSocket *tcpsock = (TCPSocket*) sock; if (tcpsock->thread != NULL) { // terminate the semSendFetch semaphore, causing the handler thread to send a FIN and // later terminate. semTerminate(&tcpsock->semSendFetch); } else { FreeSocket(sock); }; };
static int tcpsock_packet(Socket *sock, const struct sockaddr *src, const struct sockaddr *dest, size_t addrlen, const void *packet, size_t size, int proto) { // TODO: 4-mapped-6 addresses if (src->sa_family != sock->domain) { return SOCK_CONT; }; if (size < sizeof(TCPSegment)) { return SOCK_CONT; }; TCPSocket *tcpsock = (TCPSocket*) sock; if (tcpsock->state == TCP_CLOSED) { return SOCK_CONT; }; if (proto == IPPROTO_TCP) { if (tcpsock->state == TCP_LISTENING) { TCPSegment *seg = (TCPSegment*) packet; uint16_t listenPort; static uint64_t zeroAddr[2] = {0, 0}; if (sock->domain == AF_INET) { const struct sockaddr_in *inname = (const struct sockaddr_in*) &tcpsock->sockname; listenPort = inname->sin_port; const struct sockaddr_in *indst = (const struct sockaddr_in*) dest; if (memcmp(&indst->sin_addr, &inname->sin_addr, 4) != 0 && memcmp(&inname->sin_addr, zeroAddr, 4) != 0) { return SOCK_CONT; }; } else { const struct sockaddr_in6 *inname = (const struct sockaddr_in6*) &tcpsock->sockname; listenPort = inname->sin6_port; const struct sockaddr_in6 *indst = (const struct sockaddr_in6*) dest; if (memcmp(&indst->sin6_addr, &inname->sin6_addr, 16) != 0 && memcmp(&inname->sin6_addr, zeroAddr, 16) != 0) { return SOCK_CONT; }; }; if (seg->dstport != listenPort) { return SOCK_CONT; }; if ((seg->flags & TCP_SYN) == 0) { return SOCK_CONT; }; struct sockaddr local; memset(&local, 0, sizeof(struct sockaddr)); struct sockaddr peer; memset(&peer, 0, sizeof(struct sockaddr)); memcpy(&local, dest, addrlen); memcpy(&peer, src, addrlen); if (sock->domain == AF_INET) { ((struct sockaddr_in*)&local)->sin_port = seg->dstport; ((struct sockaddr_in*)&peer)->sin_port = seg->srcport; } else { ((struct sockaddr_in6*)&local)->sin6_port = seg->dstport; ((struct sockaddr_in6*)&peer)->sin6_port = seg->srcport; ((struct sockaddr_in6*)&local)->sin6_flowinfo = 0; ((struct sockaddr_in6*)&peer)->sin6_flowinfo = 0; }; // we just received a connection semWait(&tcpsock->lock); // first check if the connection is already pending int found = 0; TCPPending *pend; for (pend=tcpsock->firstPending; pend!=NULL; pend=pend->next) { if (memcmp(&pend->local, &local, sizeof(struct sockaddr)) == 0 && memcmp(&pend->peer, &peer, sizeof(struct sockaddr)) == 0) { found = 1; break; }; }; // if not yet on the pending list, add it if (!found) { pend = NEW(TCPPending); pend->next = NULL; memcpy(&pend->local, &local, sizeof(struct sockaddr)); memcpy(&pend->peer, &peer, sizeof(struct sockaddr)); pend->ackno = ntohl(seg->seqno)+1; if (tcpsock->firstPending == NULL) { tcpsock->firstPending = pend; } else { TCPPending *last = tcpsock->firstPending; while (last->next != NULL) last = last->next; last->next = pend; }; }; semSignal(&tcpsock->lock); semSignal(&tcpsock->semConnWaiting); return SOCK_STOP; }; if (ValidateChecksum(src, dest, packet, size) != 0) { return SOCK_CONT; }; uint16_t localPort, remotePort; if (sock->domain == AF_INET) { const struct sockaddr_in *inname = (const struct sockaddr_in*) &tcpsock->sockname; const struct sockaddr_in *inpeer = (const struct sockaddr_in*) &tcpsock->peername; localPort = inname->sin_port; remotePort = inpeer->sin_port; const struct sockaddr_in *insrc = (const struct sockaddr_in*) src; const struct sockaddr_in *indst = (const struct sockaddr_in*) dest; if (memcmp(&insrc->sin_addr, &inpeer->sin_addr, 4) != 0) { return SOCK_CONT; }; if (memcmp(&indst->sin_addr, &inname->sin_addr, 4) != 0) { return SOCK_CONT; }; } else { const struct sockaddr_in6 *inname = (const struct sockaddr_in6*) &tcpsock->sockname; const struct sockaddr_in6 *inpeer = (const struct sockaddr_in6*) &tcpsock->peername; localPort = inname->sin6_port; remotePort = inpeer->sin6_port; const struct sockaddr_in6 *insrc = (const struct sockaddr_in6*) src; const struct sockaddr_in6 *indst = (const struct sockaddr_in6*) dest; if (memcmp(&insrc->sin6_addr, &inpeer->sin6_addr, 16) != 0) { return SOCK_CONT; }; if (memcmp(&indst->sin6_addr, &inname->sin6_addr, 16) != 0) { return SOCK_CONT; }; }; TCPSegment *seg = (TCPSegment*) packet; if ((seg->srcport != remotePort) || (seg->dstport != localPort)) { return SOCK_CONT; }; // at this point, we know that this packet is destined to this socket, so from now on return SOCK_STOP only, // to avoid it arriving at other sockets if (seg->flags & TCP_ACK) { uint64_t ackno = (uint64_t) ntohl(seg->ackno); if (atomic_compare_and_swap64(&tcpsock->expectedAck, ackno, (1UL << 32)) == ackno) { semSignal(&tcpsock->semAck); }; }; if (seg->flags & TCP_RST) { semWait(&tcpsock->lock); if (ntohl(seg->seqno) == tcpsock->nextAckNo) { tcpsock->sockErr = ECONNRESET; tcpsock->shutflags |= SHUT_WR | SHUT_RD; tcpsock->state = TCP_TERMINATED; semSignal(&tcpsock->semStop); }; semSignal(&tcpsock->lock); }; if (tcpsock->state == TCP_TERMINATED) { semSignal(&tcpsock->semAckOut); return SOCK_STOP; }; size_t headerSize = (size_t)(seg->dataOffsetNS >> 4) * 4; if (seg->flags & TCP_SYN) { Semaphore *semConn = &tcpsock->semConnected; uint8_t bitmap = 0; semPoll(1, &semConn, &bitmap, SEM_W_NONBLOCK, 0); if (bitmap == 0) { tcpsock->nextAckNo = ntohl(seg->seqno)+1; semSignal(&tcpsock->semConnected); }; semSignal(&tcpsock->semAckOut); } else if (seg->flags & TCP_FIN) { uint32_t seqno = ntohl(seg->seqno); semWait(&tcpsock->lock); if (seqno == tcpsock->nextAckNo) { tcpsock->shutflags |= SHUT_RD; tcpsock->nextAckNo = seqno+1; semTerminate(&tcpsock->semRecvFetch); semSignal(&tcpsock->semAckOut); }; semSignal(&tcpsock->lock); } else if (seg->flags & TCP_PSH || size > headerSize) { size_t payloadSize = size - headerSize; const uint8_t *scan = (const uint8_t*)seg + headerSize; uint32_t seqno = ntohl(seg->seqno); semWait(&tcpsock->lock); if (seqno == tcpsock->nextAckNo) { if (tcpsock->cntRecvPut < payloadSize) { // we can't fit this payload in our buffer! drop it as it will // be re-transmitted soon semSignal(&tcpsock->lock); return SOCK_STOP; }; tcpsock->cntRecvPut -= payloadSize; size_t count = payloadSize; while (count--) { tcpsock->bufRecv[tcpsock->idxRecvPut] = *scan++; tcpsock->idxRecvPut = (tcpsock->idxRecvPut+1) % TCP_BUFFER_SIZE; }; semSignal2(&tcpsock->semRecvFetch, (int)payloadSize); tcpsock->nextAckNo += (uint32_t)payloadSize; }; semSignal(&tcpsock->semAckOut); semSignal(&tcpsock->lock); }; return SOCK_STOP; }; // TODO: ICMP messages relating to TCP return SOCK_CONT; };