1 /*
2 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
19 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
21 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
25 * OF SUCH DAMAGE.
26 *
27 * This file is part of and a contribution to the lwIP TCP/IP stack.
28 *
29 * Credits go to Adam Dunkels (and the current maintainers) of this software.
30 *
31 * Christiaan Simons rewrote this file to get a more stable echo example.
32 */
33
34 /**
35 * @file
36 * TCP echo server example using raw API.
37 *
38 * Echos all bytes sent by connecting client,
39 * and passively closes when client is done.
40 *
41 */
42
43 #include "lwip/opt.h"
44 #include "lwip/debug.h"
45 #include "lwip/stats.h"
46 #include "lwip/tcp.h"
47 #include "tcpecho_raw.h"
48
49 #if LWIP_TCP && LWIP_CALLBACK_API
50
51 static struct tcp_pcb *tcpecho_raw_pcb;
52
53 enum tcpecho_raw_states
54 {
55 ES_NONE = 0,
56 ES_ACCEPTED,
57 ES_RECEIVED,
58 ES_CLOSING
59 };
60
61 struct tcpecho_raw_state
62 {
63 u8_t state;
64 u8_t retries;
65 struct tcp_pcb *pcb;
66 /* pbuf (chain) to recycle */
67 struct pbuf *p;
68 };
69
70 static void
tcpecho_raw_free(struct tcpecho_raw_state * es)71 tcpecho_raw_free(struct tcpecho_raw_state *es)
72 {
73 if (es != NULL) {
74 if (es->p) {
75 /* free the buffer chain if present */
76 pbuf_free(es->p);
77 }
78
79 mem_free(es);
80 }
81 }
82
83 static void
tcpecho_raw_close(struct tcp_pcb * tpcb,struct tcpecho_raw_state * es)84 tcpecho_raw_close(struct tcp_pcb *tpcb, struct tcpecho_raw_state *es)
85 {
86 tcp_arg(tpcb, NULL);
87 tcp_sent(tpcb, NULL);
88 tcp_recv(tpcb, NULL);
89 tcp_err(tpcb, NULL);
90 tcp_poll(tpcb, NULL, 0);
91
92 tcpecho_raw_free(es);
93
94 tcp_close(tpcb);
95 }
96
97 static void
tcpecho_raw_send(struct tcp_pcb * tpcb,struct tcpecho_raw_state * es)98 tcpecho_raw_send(struct tcp_pcb *tpcb, struct tcpecho_raw_state *es)
99 {
100 struct pbuf *ptr;
101 err_t wr_err = ERR_OK;
102
103 while ((wr_err == ERR_OK) &&
104 (es->p != NULL) &&
105 (es->p->len <= tcp_sndbuf(tpcb))) {
106 ptr = es->p;
107
108 /* enqueue data for transmission */
109 wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1);
110 if (wr_err == ERR_OK) {
111 u16_t plen;
112
113 plen = ptr->len;
114 /* continue with next pbuf in chain (if any) */
115 es->p = ptr->next;
116 if(es->p != NULL) {
117 /* new reference! */
118 pbuf_ref(es->p);
119 }
120 /* chop first pbuf from chain */
121 pbuf_free(ptr);
122 /* we can read more data now */
123 tcp_recved(tpcb, plen);
124 } else if(wr_err == ERR_MEM) {
125 /* we are low on memory, try later / harder, defer to poll */
126 es->p = ptr;
127 } else {
128 /* other problem ?? */
129 }
130 }
131 }
132
133 static void
tcpecho_raw_error(void * arg,err_t err)134 tcpecho_raw_error(void *arg, err_t err)
135 {
136 struct tcpecho_raw_state *es;
137
138 LWIP_UNUSED_ARG(err);
139
140 es = (struct tcpecho_raw_state *)arg;
141
142 tcpecho_raw_free(es);
143 }
144
145 static err_t
tcpecho_raw_poll(void * arg,struct tcp_pcb * tpcb)146 tcpecho_raw_poll(void *arg, struct tcp_pcb *tpcb)
147 {
148 err_t ret_err;
149 struct tcpecho_raw_state *es;
150
151 es = (struct tcpecho_raw_state *)arg;
152 if (es != NULL) {
153 if (es->p != NULL) {
154 /* there is a remaining pbuf (chain) */
155 tcpecho_raw_send(tpcb, es);
156 } else {
157 /* no remaining pbuf (chain) */
158 if(es->state == ES_CLOSING) {
159 tcpecho_raw_close(tpcb, es);
160 }
161 }
162 ret_err = ERR_OK;
163 } else {
164 /* nothing to be done */
165 tcp_abort(tpcb);
166 ret_err = ERR_ABRT;
167 }
168 return ret_err;
169 }
170
171 static err_t
tcpecho_raw_sent(void * arg,struct tcp_pcb * tpcb,u16_t len)172 tcpecho_raw_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
173 {
174 struct tcpecho_raw_state *es;
175
176 LWIP_UNUSED_ARG(len);
177
178 es = (struct tcpecho_raw_state *)arg;
179 es->retries = 0;
180
181 if(es->p != NULL) {
182 /* still got pbufs to send */
183 tcp_sent(tpcb, tcpecho_raw_sent);
184 tcpecho_raw_send(tpcb, es);
185 } else {
186 /* no more pbufs to send */
187 if(es->state == ES_CLOSING) {
188 tcpecho_raw_close(tpcb, es);
189 }
190 }
191 return ERR_OK;
192 }
193
194 static err_t
tcpecho_raw_recv(void * arg,struct tcp_pcb * tpcb,struct pbuf * p,err_t err)195 tcpecho_raw_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
196 {
197 struct tcpecho_raw_state *es;
198 err_t ret_err;
199
200 LWIP_ASSERT("arg != NULL",arg != NULL);
201 es = (struct tcpecho_raw_state *)arg;
202 if (p == NULL) {
203 /* remote host closed connection */
204 es->state = ES_CLOSING;
205 if(es->p == NULL) {
206 /* we're done sending, close it */
207 tcpecho_raw_close(tpcb, es);
208 } else {
209 /* we're not done yet */
210 tcpecho_raw_send(tpcb, es);
211 }
212 ret_err = ERR_OK;
213 } else if(err != ERR_OK) {
214 /* cleanup, for unknown reason */
215 LWIP_ASSERT("no pbuf expected here", p == NULL);
216 ret_err = err;
217 }
218 else if(es->state == ES_ACCEPTED) {
219 /* first data chunk in p->payload */
220 es->state = ES_RECEIVED;
221 /* store reference to incoming pbuf (chain) */
222 es->p = p;
223 tcpecho_raw_send(tpcb, es);
224 ret_err = ERR_OK;
225 } else if (es->state == ES_RECEIVED) {
226 /* read some more data */
227 if(es->p == NULL) {
228 es->p = p;
229 tcpecho_raw_send(tpcb, es);
230 } else {
231 struct pbuf *ptr;
232
233 /* chain pbufs to the end of what we recv'ed previously */
234 ptr = es->p;
235 pbuf_cat(ptr,p);
236 }
237 ret_err = ERR_OK;
238 } else {
239 /* unknown es->state, trash data */
240 tcp_recved(tpcb, p->tot_len);
241 pbuf_free(p);
242 ret_err = ERR_OK;
243 }
244 return ret_err;
245 }
246
247 static err_t
tcpecho_raw_accept(void * arg,struct tcp_pcb * newpcb,err_t err)248 tcpecho_raw_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
249 {
250 err_t ret_err;
251 struct tcpecho_raw_state *es;
252
253 LWIP_UNUSED_ARG(arg);
254 if ((err != ERR_OK) || (newpcb == NULL)) {
255 return ERR_VAL;
256 }
257
258 /* Unless this pcb should have NORMAL priority, set its priority now.
259 When running out of pcbs, low priority pcbs can be aborted to create
260 new pcbs of higher priority. */
261 tcp_setprio(newpcb, TCP_PRIO_MIN);
262
263 es = (struct tcpecho_raw_state *)mem_malloc(sizeof(struct tcpecho_raw_state));
264 if (es != NULL) {
265 es->state = ES_ACCEPTED;
266 es->pcb = newpcb;
267 es->retries = 0;
268 es->p = NULL;
269 /* pass newly allocated es to our callbacks */
270 tcp_arg(newpcb, es);
271 tcp_recv(newpcb, tcpecho_raw_recv);
272 tcp_err(newpcb, tcpecho_raw_error);
273 tcp_poll(newpcb, tcpecho_raw_poll, 0);
274 tcp_sent(newpcb, tcpecho_raw_sent);
275 ret_err = ERR_OK;
276 } else {
277 ret_err = ERR_MEM;
278 }
279 return ret_err;
280 }
281
282 void
tcpecho_raw_init(void)283 tcpecho_raw_init(void)
284 {
285 tcpecho_raw_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
286 if (tcpecho_raw_pcb != NULL) {
287 err_t err;
288
289 err = tcp_bind(tcpecho_raw_pcb, IP_ANY_TYPE, 7);
290 if (err == ERR_OK) {
291 tcpecho_raw_pcb = tcp_listen(tcpecho_raw_pcb);
292 tcp_accept(tcpecho_raw_pcb, tcpecho_raw_accept);
293 } else {
294 /* abort? output diagnostic? */
295 }
296 } else {
297 /* abort? output diagnostic? */
298 }
299 }
300
301 #endif /* LWIP_TCP && LWIP_CALLBACK_API */
302