1#include "utils.h"
2#include <pico_socket.h>
3#include <pico_ipv4.h>
4/*** START TCP ECHO ***/
5#define BSIZE (1024 * 10)
6static char recvbuf[BSIZE];
7static int pos = 0, len = 0;
8static int flag = 0;
9
10int send_tcpecho(struct pico_socket *s)
11{
12    int w, ww = 0;
13    if (len > pos) {
14        do {
15            w = pico_socket_write(s, recvbuf + pos, len - pos);
16            if (w > 0) {
17                pos += w;
18                ww += w;
19                if (pos >= len) {
20                    pos = 0;
21                    len = 0;
22                }
23            }
24        } while((w > 0) && (pos < len));
25    }
26
27    return ww;
28}
29
30void cb_tcpecho(uint16_t ev, struct pico_socket *s)
31{
32    int r = 0;
33
34    picoapp_dbg("tcpecho> wakeup ev=%u\n", ev);
35
36    if (ev & PICO_SOCK_EV_RD) {
37        if (flag & PICO_SOCK_EV_CLOSE)
38            printf("SOCKET> EV_RD, FIN RECEIVED\n");
39
40        while (len < BSIZE) {
41            r = pico_socket_read(s, recvbuf + len, BSIZE - len);
42            if (r > 0) {
43                len += r;
44                flag &= ~(PICO_SOCK_EV_RD);
45            } else {
46                flag |= PICO_SOCK_EV_RD;
47                break;
48            }
49        }
50        if (flag & PICO_SOCK_EV_WR) {
51            flag &= ~PICO_SOCK_EV_WR;
52            send_tcpecho(s);
53        }
54    }
55
56    if (ev & PICO_SOCK_EV_CONN) {
57        uint32_t ka_val = 0;
58        struct pico_socket *sock_a = {
59            0
60        };
61        struct pico_ip4 orig = {
62            0
63        };
64        uint16_t port = 0;
65        char peer[30] = {
66            0
67        };
68        int yes = 1;
69
70        sock_a = pico_socket_accept(s, &orig, &port);
71        pico_ipv4_to_string(peer, orig.addr);
72        printf("Connection established with %s:%d.\n", peer, short_be(port));
73        pico_socket_setoption(sock_a, PICO_TCP_NODELAY, &yes);
74        /* Set keepalive options */
75        ka_val = 5;
76        pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPCNT, &ka_val);
77        ka_val = 30000;
78        pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPIDLE, &ka_val);
79        ka_val = 5000;
80        pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPINTVL, &ka_val);
81    }
82
83    if (ev & PICO_SOCK_EV_FIN) {
84        printf("Socket closed. Exit normally. \n");
85        if (!pico_timer_add(2000, deferred_exit, NULL)) {
86            printf("Failed to start exit timer, exiting now\n");
87            exit(1);
88        }
89    }
90
91    if (ev & PICO_SOCK_EV_ERR) {
92        printf("Socket error received: %s. Bailing out.\n", strerror(pico_err));
93        exit(1);
94    }
95
96    if (ev & PICO_SOCK_EV_CLOSE) {
97        printf("Socket received close from peer.\n");
98        if (flag & PICO_SOCK_EV_RD) {
99            pico_socket_shutdown(s, PICO_SHUT_WR);
100            printf("SOCKET> Called shutdown write, ev = %d\n", ev);
101        }
102    }
103
104    if (ev & PICO_SOCK_EV_WR) {
105        r = send_tcpecho(s);
106        if (r == 0)
107            flag |= PICO_SOCK_EV_WR;
108        else
109            flag &= (~PICO_SOCK_EV_WR);
110    }
111}
112
113void app_tcpecho(char *arg)
114{
115    char *nxt = arg;
116    char *lport = NULL;
117    uint16_t listen_port = 0;
118    int ret = 0, yes = 1;
119    struct pico_socket *s = NULL;
120    union {
121        struct pico_ip4 ip4;
122        struct pico_ip6 ip6;
123    } inaddr_any = {
124        .ip4 = {0}, .ip6 = {{0}}
125    };
126
127    /* start of argument parsing */
128    if (nxt) {
129        nxt = cpy_arg(&lport, nxt);
130        if (lport && atoi(lport)) {
131            listen_port = short_be(atoi(lport));
132        } else {
133            goto out;
134        }
135    } else {
136        /* missing listen_port */
137        goto out;
138    }
139
140    /* end of argument parsing */
141
142    if (!IPV6_MODE)
143        s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &cb_tcpecho);
144    else
145        s = pico_socket_open(PICO_PROTO_IPV6, PICO_PROTO_TCP, &cb_tcpecho);
146
147    if (!s) {
148        printf("%s: error opening socket: %s\n", __FUNCTION__, strerror(pico_err));
149        exit(1);
150    }
151
152    pico_socket_setoption(s, PICO_TCP_NODELAY, &yes);
153
154
155
156    if (!IPV6_MODE)
157        ret = pico_socket_bind(s, &inaddr_any.ip4, &listen_port);
158    else
159        ret = pico_socket_bind(s, &inaddr_any.ip6, &listen_port);
160
161    if (ret < 0) {
162        printf("%s: error binding socket to port %u: %s\n", __FUNCTION__, short_be(listen_port), strerror(pico_err));
163        exit(1);
164    }
165
166    if (pico_socket_listen(s, 40) != 0) {
167        printf("%s: error listening on port %u\n", __FUNCTION__, short_be(listen_port));
168        exit(1);
169    }
170
171    printf("Launching PicoTCP echo server\n");
172    return;
173
174out:
175    fprintf(stderr, "tcpecho expects the following format: tcpecho:listen_port\n");
176    exit(255);
177}
178/*** END TCP ECHO ***/
179