Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
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;
}