static int tcp_http_handler(struct tcp_socket *sk, const unsigned char *data, int datalen) { char request[64]; char *req = request; dprintf("tcp: http: got %d bytes\n", datalen); if (datalen <= 5) /* minimum HTTP request */ return 0; if (datalen > sizeof(request)-1) datalen = sizeof(request)-1; memcpy(req, data, datalen); req[datalen] = '\0'; if (strncmp(req, "GET ", 4) == 0) { req += 4; if (index(req, ' ')) *(index(req, ' ')) = '\0'; dprintf("tcp: http: req = %s\n", req); if ((strcmp(req, "/") == 0) || (strncmp(req, "/index", 6) == 0)) { tcp_send_data(sk, httpindex, strlen(httpindex)); tcp_send_fin(sk); tcp_changestate(sk, STATE_FIN_WAIT_1); return 1; } } return 0; }
/** * Closes the TX side of a connection held by the PCB. * For tcp_close(), a RST is sent if the application didn't receive all data * (tcp_recved() not called for all data passed to recv callback). * * Listening pcbs are freed and may not be referenced any more. * Connection pcbs are freed if not yet connected and may not be referenced * any more. If a connection is established (at least SYN received or in * a closing state), the connection is closed, and put in a closing state. * The pcb is then automatically freed in tcp_slowtmr(). It is therefore * unsafe to reference it. * * @param pcb the tcp_pcb to close * @return ERR_OK if connection has been closed * another err_t if closing failed and pcb is not freed */ static err_t tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) { err_t err; if (rst_on_unacked_data && (pcb->state != LISTEN)) { if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) { /* Not all data received by application, send RST to tell the remote side about this. */ LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED); /* don't call tcp_abort here: we must not deallocate the pcb since that might not be expected when calling tcp_close */ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port); tcp_pcb_purge(pcb); /* TODO: to which state do we move now? */ /* move to TIME_WAIT since we close actively */ TCP_RMV(&tcp_active_pcbs, pcb); pcb->state = TIME_WAIT; TCP_REG(&tcp_tw_pcbs, pcb); return ERR_OK; } } switch (pcb->state) { case CLOSED: /* Closing a pcb in the CLOSED state might seem erroneous, * however, it is in this state once allocated and as yet unused * and the user needs some way to free it should the need arise. * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) * or for a pcb that has been used and then entered the CLOSED state * is erroneous, but this should never happen as the pcb has in those cases * been freed, and so any remaining handles are bogus. */ err = ERR_OK; if (pcb->local_port != 0) { TCP_RMV(&tcp_bound_pcbs, pcb); } memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; break; case LISTEN: err = ERR_OK; tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb); memp_free(MEMP_TCP_PCB_LISTEN, pcb); pcb = NULL; break; case SYN_SENT: err = ERR_OK; tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; snmp_inc_tcpattemptfails(); break; case SYN_RCVD: err = tcp_send_fin(pcb); if (err == ERR_OK) { snmp_inc_tcpattemptfails(); pcb->state = FIN_WAIT_1; } break; case ESTABLISHED: err = tcp_send_fin(pcb); if (err == ERR_OK) { snmp_inc_tcpestabresets(); pcb->state = FIN_WAIT_1; } break; case CLOSE_WAIT: err = tcp_send_fin(pcb); if (err == ERR_OK) { snmp_inc_tcpestabresets(); pcb->state = LAST_ACK; } break; default: /* Has already been closed, do nothing. */ err = ERR_OK; pcb = NULL; break; } if (pcb != NULL && err == ERR_OK) { /* To ensure all data has been sent when tcp_close returns, we have to make sure tcp_output doesn't fail. Since we don't really have to ensure all data has been sent when tcp_close returns (unsent data is sent from tcp timer functions, also), we don't care for the return value of tcp_output for now. */ /* @todo: When implementing SO_LINGER, this must be changed somehow: If SOF_LINGER is set, the data should be sent and acked before close returns. This can only be valid for sequential APIs, not for the raw API. */ tcp_output(pcb); } return err; }
static int tcp_consumeseg(struct tcp_socket *sk, unsigned long srcip, unsigned char *pkt, int pktlen) { struct tcphdr *hdr = (struct tcphdr *)pkt; sk->rcv_nxt = ntohl(hdr->seqnum)+1; if (hdr->cntrlbits & TCPBIT_ACK) { struct tcpvec *cur, **prev; unsigned long newoldest, curack; newoldest = sk->snd_nxt; curack = ntohl(hdr->acknum); if (curack < sk->snd_una) return 0; /* been there, done that */ tcp_dumpsockets(); for (prev = &sk->txqueue; (cur = *prev); ) { if (cur->seqnum < curack) { int offset; offset = curack - cur->seqnum; dprintf("tcp_consumeseq: %d-%d have been acked\n", cur->seqnum, cur->seqnum+offset); if (offset >= cur->len) { *prev = cur->next; if (cur->flags & TCPBIT_SYN) tcp_changestate(sk, STATE_ESTABLISHED); RimFree(cur->buf); RimFree(cur); } else { cur->seqnum += offset; cur->start += offset; cur->len -= offset; dprintf("tcp_consumeseq: still %d left in this vec\n"); prev = &cur->next; } } else { if (cur->seqnum < newoldest) newoldest = cur->seqnum; prev = &cur->next; } } sk->snd_una = newoldest; } if ((pktlen - (hdr->hdrlen*4)) > 0) { /* we have data! */ dprintf("*********REAL DATA!!! %u+(%u-%u)=%u\n", ntohl(hdr->seqnum), pktlen, hdr->hdrlen*4, ntohl(hdr->seqnum)+(pktlen-(hdr->hdrlen*4))); dumpbox(pkt+(hdr->hdrlen*4), pktlen-(hdr->hdrlen*4)); /* XXX implement delayed ACKs and mitigate ACK-only segments */ sk->rcv_nxt = ntohl(hdr->seqnum)+(pktlen-(hdr->hdrlen*4))+0; if (!sk->datahandler || !sk->datahandler(sk, pkt+(hdr->hdrlen*4), pktlen-(hdr->hdrlen*4))) tcp_send_ack(sk); } if (hdr->cntrlbits & TCPBIT_FIN) { dprintf("connection closed!\n"); /* No need to send a seperate ACK, it will be in the FIN */ //sk->rcv_nxt = ntohl(hdr->seqnum)+1; if (sk->state == STATE_FIN_WAIT_1) { tcp_changestate(sk, STATE_TIME_WAIT); } else if (sk->state != STATE_TIME_WAIT) { tcp_send_fin(sk); tcp_changestate(sk, STATE_FIN_WAIT_1); } } return 0; }
/* * This is a bit complicated because we attempt to stuff everything * we've already sent that hasn't been acked into this packet, along * with the new data. * * XXX check the endpoint window and MSS to make sure we don't overflow it. * */ static int tcp_send_data(struct tcp_socket *sk, const unsigned char *data, int datalen) { unsigned char *pkt; struct tcphdr *hdr; struct tcpvec *cur; unsigned short flags = 0; unsigned long seqnum; /* First, enqueue the new data and increment the next seqnum */ if (data && datalen) tcp_txenqueue(sk, 0, sk->snd_nxt, bufdup(data, datalen), datalen); for (cur = sk->txqueue, datalen = 0, seqnum = sk->snd_nxt; cur && (datalen < sk->rcv_max); cur = cur->next) { if (cur->txcount >= TCP_MAX_RETRIES) { tcp_changestate(sk, STATE_TIME_WAIT); tcp_send_fin(sk); return -1; } flags |= cur->flags; if (cur->seqnum < seqnum) seqnum = cur->seqnum; /* get the lowest seqnum queued */ datalen += cur->len; } dprintf("tcp_send_data: procesing %d queued bytes\n", datalen); if (!(pkt = RimMalloc(20+datalen))) return -1; hdr = (struct tcphdr *)pkt; hdr->srcport = htons(sk->localport); hdr->dstport = htons(sk->remoteport); if (sk->snd_nxt < sk->snd_una) sk->snd_una = sk->snd_nxt; hdr->seqnum = htonl(seqnum); hdr->acknum = htonl(sk->rcv_nxt); hdr->hdrlen = 5; /* 20 / 4 = 5 */ hdr->reserved1 = hdr->reserved2 = 0; hdr->cntrlbits = TCPBIT_ACK | flags | (!flags ? TCPBIT_PSH : 0); hdr->window = htons(TCP_DEFAULT_WINDOW); hdr->csum = 0; hdr->urgptr = 0; for (cur = sk->txqueue, flags = 20; cur; cur = cur->next) { memcpy(pkt+flags, cur->buf+cur->start, cur->len); flags += cur->len; cur->lasttx = RimGetTicks(); cur->txcount++; } hdr->csum = tcp_checksum(pkt, (hdr->hdrlen*4)+datalen, htonl(INTERMOBI_OURIP), htonl(sk->remoteaddr)); ip_send(htonl(sk->remoteaddr), IPPROTO_TCP, pkt, 20+datalen); RimFree(pkt); return 0; }