/** * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * This file is part of and a contribution to the lwIP TCP/IP stack. * * Credits go to Adam Dunkels (and the current maintainers) of this software. * * Christiaan Simons rewrote this file to get a more stable echo application. * **/ /* This file was modified by ST */ #include "lwip/debug.h" #include "lwip/stats.h" #include "lwip/tcp.h" #include #if LWIP_TCP /* ECHO protocol states */ enum tcp_echoserver_states { ES_NONE = 0, ES_ACCEPTED, ES_RECEIVED, ES_CLOSING }; /* structure for maintaining connection infos to be passed as argument to LwIP callbacks*/ struct tcp_echoserver_struct { u8_t state; /* current connection state */ u8_t retries; struct tcp_pcb *pcb; /* pointer on the current tcp_pcb */ struct pbuf *p; /* pointer on the received/to be transmitted pbuf */ }; static struct tcp_pcb *tcp_es_pcb; static struct tcp_echoserver_struct *tcp_es; static char buf[256]; static int counter_server = 0; static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err); static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); static void tcp_echoserver_error(void *arg, err_t err); static err_t tcp_echoserver_poll(void *arg, struct tcp_pcb *tpcb); static err_t tcp_echoserver_sent(void *arg, struct tcp_pcb *tpcb, u16_t len); static void tcp_echoserver_send(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es); static void tcp_echoserver_connection_close(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es); static void tcp_echoserver_handle(void); /** * @brief Initializes the tcp echo server * @param None * @retval None */ void tcp_echoserver_init(void) { /* create new tcp pcb */ tcp_es_pcb = tcp_new(); if (tcp_es_pcb != NULL) { err_t err; /* bind echo_pcb (ECHO protocol) */ ip_addr_t myIPADDR; IP_ADDR4(&myIPADDR, 10, 0, 0, 101); err = tcp_bind(tcp_es_pcb, &myIPADDR, 10); if (err == ERR_OK) { /* start tcp listening for echo_pcb */ tcp_es_pcb = tcp_listen(tcp_es_pcb); /* initialize LwIP tcp_accept callback function */ tcp_accept(tcp_es_pcb, tcp_echoserver_accept); } else { /* deallocate the pcb */ memp_free(MEMP_TCP_PCB, tcp_es_pcb); } } } /** * @brief This function is the implementation of tcp_accept LwIP callback * @param arg: not used * @param newpcb: pointer on tcp_pcb struct for the newly created tcp connection * @param err: not used * @retval err_t: error status */ static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err) { err_t ret_err; LWIP_UNUSED_ARG(arg); LWIP_UNUSED_ARG(err); /* set priority for the newly accepted tcp connection newpcb */ tcp_setprio(newpcb, TCP_PRIO_MIN); /* allocate structure es to maintain tcp connection information */ tcp_es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct)); if (tcp_es != NULL) { tcp_es->state = ES_ACCEPTED; tcp_es->pcb = newpcb; tcp_es->retries = 0; tcp_es->p = NULL; /* pass newly allocated es structure as argument to newpcb */ tcp_arg(newpcb, tcp_es); /* initialize lwip tcp_recv callback function for newpcb */ tcp_recv(newpcb, tcp_echoserver_recv); /* initialize lwip tcp_err callback function for newpcb */ tcp_err(newpcb, tcp_echoserver_error); /* initialize lwip tcp_poll callback function for newpcb */ tcp_poll(newpcb, tcp_echoserver_poll, 0); ret_err = ERR_OK; } else { /* close tcp connection */ tcp_echoserver_connection_close(newpcb, tcp_es); /* return memory error */ ret_err = ERR_MEM; } return ret_err; } /** * @brief This function is the implementation for tcp_recv LwIP callback * @param arg: pointer on a argument for the tcp_pcb connection * @param tpcb: pointer on the tcp_pcb connection * @param pbuf: pointer on the received pbuf * @param err: error information regarding the reveived pbuf * @retval err_t: error code */ static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { err_t ret_err; LWIP_ASSERT("arg != NULL",arg != NULL); tcp_es = (struct tcp_echoserver_struct *)arg; tcp_es_pcb = tpcb; /* if we receive an empty tcp frame from client => close connection */ if (p == NULL) { /* remote host closed connection */ tcp_es->state = ES_CLOSING; if(tcp_es->p == NULL) { /* we're done sending, close connection */ tcp_echoserver_connection_close(tpcb, tcp_es); } else { /* we're not done yet */ /* acknowledge received packet */ tcp_sent(tpcb, tcp_echoserver_sent); /* send remaining data*/ tcp_echoserver_send(tpcb, tcp_es); } ret_err = ERR_OK; } /* else : a non empty frame was received from client but for some reason err != ERR_OK */ else if(err != ERR_OK) { /* free received pbuf*/ if (p != NULL) { tcp_es->p = NULL; pbuf_free(p); } ret_err = err; } else if(tcp_es->state == ES_ACCEPTED) { /* first data chunk in p->payload */ tcp_es->state = ES_RECEIVED; /* store reference to incoming pbuf (chain) */ tcp_es->p = p; /* initialize LwIP tcp_sent callback function */ tcp_sent(tpcb, tcp_echoserver_sent); /* send back received data */ //tcp_echoserver_send(tpcb, es); /* handle the received data */ tcp_echoserver_handle(); ret_err = ERR_OK; } else if (tcp_es->state == ES_RECEIVED) { /* more data received from client and previous data has been already sent*/ if(tcp_es->p == NULL) { tcp_es->p = p; /* send back received data */ //tcp_echoserver_send(tpcb, es); /* handle the received data */ tcp_echoserver_handle(); } else { struct pbuf *ptr; /* chain pbufs to the end of what we recv'ed previously */ ptr = tcp_es->p; pbuf_chain(ptr,p); } ret_err = ERR_OK; } else if(tcp_es->state == ES_CLOSING) { /* odd case, remote side closing twice, trash data */ tcp_recved(tpcb, p->tot_len); tcp_es->p = NULL; pbuf_free(p); ret_err = ERR_OK; } else { /* unknown es->state, trash data */ tcp_recved(tpcb, p->tot_len); tcp_es->p = NULL; pbuf_free(p); ret_err = ERR_OK; } return ret_err; } /** * @brief This function implements the tcp_err callback function (called * when a fatal tcp_connection error occurs. * @param arg: pointer on argument parameter * @param err: not used * @retval None */ static void tcp_echoserver_error(void *arg, err_t err) { LWIP_UNUSED_ARG(err); tcp_es = (struct tcp_echoserver_struct *)arg; if (tcp_es != NULL) { /* free es structure */ mem_free(tcp_es); } } /** * @brief This function implements the tcp_poll LwIP callback function * @param arg: pointer on argument passed to callback * @param tpcb: pointer on the tcp_pcb for the current tcp connection * @retval err_t: error code */ static err_t tcp_echoserver_poll(void *arg, struct tcp_pcb *tpcb) { err_t ret_err; tcp_es = (struct tcp_echoserver_struct *)arg; tcp_es_pcb = tpcb; if (tcp_es != NULL) { if (tcp_es->p != NULL) { tcp_sent(tpcb, tcp_echoserver_sent); /* there is a remaining pbuf (chain) , try to send data */ tcp_echoserver_send(tpcb, tcp_es); } else { /* no remaining pbuf (chain) */ if(tcp_es->state == ES_CLOSING) { /* close tcp connection */ tcp_echoserver_connection_close(tpcb, tcp_es); } } ret_err = ERR_OK; } else { /* nothing to be done */ tcp_abort(tpcb); ret_err = ERR_ABRT; } return ret_err; } /** * @brief This function implements the tcp_sent LwIP callback (called when ACK * is received from remote host for sent data) * @param None * @retval None */ static err_t tcp_echoserver_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) { LWIP_UNUSED_ARG(len); tcp_es = (struct tcp_echoserver_struct *)arg; tcp_es->retries = 0; tcp_es_pcb = tpcb; if(tcp_es->p != NULL) { /* still got pbufs to send */ tcp_sent(tpcb, tcp_echoserver_sent); tcp_echoserver_send(tpcb, tcp_es); } else { /* if no more data to send and client closed connection*/ if(tcp_es->state == ES_CLOSING) tcp_echoserver_connection_close(tpcb, tcp_es); } return ERR_OK; } /** * @brief This function is used to send data for tcp connection * @param tpcb: pointer on the tcp_pcb connection * @param es: pointer on echo_state structure * @retval None */ static void tcp_echoserver_send(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es) { struct pbuf *ptr; err_t wr_err = ERR_OK; while ((wr_err == ERR_OK) && (es->p != NULL) && (es->p->len <= tcp_sndbuf(tpcb))) { /* get pointer on pbuf from es structure */ ptr = es->p; /* enqueue data for transmission */ wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1); if (wr_err == ERR_OK) { u16_t plen; u8_t freed; plen = ptr->len; /* continue with next pbuf in chain (if any) */ es->p = ptr->next; if(es->p != NULL) { /* increment reference count for es->p */ pbuf_ref(es->p); } /* chop first pbuf from chain */ do { /* try hard to free pbuf */ freed = pbuf_free(ptr); } while(freed == 0); /* we can read more data now */ tcp_recved(tpcb, plen); } else if(wr_err == ERR_MEM) { /* we are low on memory, try later / harder, defer to poll */ es->p = ptr; } else { /* other problem ?? */ } } } /** * @brief This functions closes the tcp connection * @param tcp_pcb: pointer on the tcp connection * @param es: pointer on echo_state structure * @retval None */ static void tcp_echoserver_connection_close(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es) { /* remove all callbacks */ tcp_arg(tpcb, NULL); tcp_sent(tpcb, NULL); tcp_recv(tpcb, NULL); tcp_err(tpcb, NULL); tcp_poll(tpcb, NULL, 0); /* delete es structure */ if (es != NULL) { mem_free(es); } /* close tcp connection */ tcp_close(tpcb); } /** * @brief This function is used to handle data for tcp connection * @retval None */ static void tcp_echoserver_handle() { /* get the Remote IP */ //ip4_addr_t inIP = tpcb->remote_ip; //uint16_t inPort = tpcb->remote_port; /* Extract the IP */ //char *remIP = ipaddr_ntoa(&inIP); memset (buf, '\0', sizeof(buf)); strncpy(buf, (char *)tcp_es->p->payload, tcp_es->p->tot_len); strcat (buf, "!"); tcp_es->p->payload = (void *)buf; tcp_es->p->tot_len = (tcp_es->p->tot_len - tcp_es->p->len) + strlen (buf) + 1; // + 1 for NULL terminating char tcp_es->p->len = strlen (buf) + 1; // + 1 for NULL terminating char tcp_echoserver_send(tcp_es_pcb, tcp_es); pbuf_free(tcp_es->p); counter_server++; } /** * @brief This function is used to notify data for tcp connection * @param tpcb: pointer on the tcp_pcb connection * @param es: pointer on echo_state structure * @retval None */ void tcp_echoserver_periodic_notification(void) { if( tcp_es->state != ES_ACCEPTED) return; memset (buf, '\0', sizeof(buf)); sprintf (buf, "Received messages: %d", counter_server); tcp_es->p->payload = (void *)buf; tcp_es->p->tot_len = strlen (buf) + 1; // + 1 for NULL terminating char tcp_es->p->len = strlen (buf) + 1; // + 1 for NULL terminating char tcp_echoserver_send(tcp_es_pcb, tcp_es); pbuf_free(tcp_es->p); } #endif /* LWIP_TCP */