1*5f657a7fSCristian Dumitrescu /* SPDX-License-Identifier: BSD-3-Clause
2*5f657a7fSCristian Dumitrescu * Copyright(c) 2020 Intel Corporation
3*5f657a7fSCristian Dumitrescu */
4*5f657a7fSCristian Dumitrescu
5*5f657a7fSCristian Dumitrescu #include <string.h>
6*5f657a7fSCristian Dumitrescu #include <stdlib.h>
7*5f657a7fSCristian Dumitrescu #include <stdio.h>
8*5f657a7fSCristian Dumitrescu #include <unistd.h>
9*5f657a7fSCristian Dumitrescu #include <sys/types.h>
10*5f657a7fSCristian Dumitrescu
11*5f657a7fSCristian Dumitrescu #include <sys/socket.h>
12*5f657a7fSCristian Dumitrescu
13*5f657a7fSCristian Dumitrescu #include <sys/epoll.h>
14*5f657a7fSCristian Dumitrescu #include <netinet/in.h>
15*5f657a7fSCristian Dumitrescu #include <arpa/inet.h>
16*5f657a7fSCristian Dumitrescu #include <errno.h>
17*5f657a7fSCristian Dumitrescu
18*5f657a7fSCristian Dumitrescu #include "conn.h"
19*5f657a7fSCristian Dumitrescu
20*5f657a7fSCristian Dumitrescu #define MSG_CMD_TOO_LONG "Command too long."
21*5f657a7fSCristian Dumitrescu
22*5f657a7fSCristian Dumitrescu struct conn {
23*5f657a7fSCristian Dumitrescu char *welcome;
24*5f657a7fSCristian Dumitrescu char *prompt;
25*5f657a7fSCristian Dumitrescu char *buf;
26*5f657a7fSCristian Dumitrescu char *msg_in;
27*5f657a7fSCristian Dumitrescu char *msg_out;
28*5f657a7fSCristian Dumitrescu size_t buf_size;
29*5f657a7fSCristian Dumitrescu size_t msg_in_len_max;
30*5f657a7fSCristian Dumitrescu size_t msg_out_len_max;
31*5f657a7fSCristian Dumitrescu size_t msg_in_len;
32*5f657a7fSCristian Dumitrescu int fd_server;
33*5f657a7fSCristian Dumitrescu int fd_client_group;
34*5f657a7fSCristian Dumitrescu conn_msg_handle_t msg_handle;
35*5f657a7fSCristian Dumitrescu void *msg_handle_arg;
36*5f657a7fSCristian Dumitrescu };
37*5f657a7fSCristian Dumitrescu
38*5f657a7fSCristian Dumitrescu struct conn *
conn_init(struct conn_params * p)39*5f657a7fSCristian Dumitrescu conn_init(struct conn_params *p)
40*5f657a7fSCristian Dumitrescu {
41*5f657a7fSCristian Dumitrescu struct sockaddr_in server_address;
42*5f657a7fSCristian Dumitrescu struct conn *conn;
43*5f657a7fSCristian Dumitrescu int fd_server, fd_client_group, status;
44*5f657a7fSCristian Dumitrescu
45*5f657a7fSCristian Dumitrescu memset(&server_address, 0, sizeof(server_address));
46*5f657a7fSCristian Dumitrescu
47*5f657a7fSCristian Dumitrescu /* Check input arguments */
48*5f657a7fSCristian Dumitrescu if ((p == NULL) ||
49*5f657a7fSCristian Dumitrescu (p->welcome == NULL) ||
50*5f657a7fSCristian Dumitrescu (p->prompt == NULL) ||
51*5f657a7fSCristian Dumitrescu (p->addr == NULL) ||
52*5f657a7fSCristian Dumitrescu (p->buf_size == 0) ||
53*5f657a7fSCristian Dumitrescu (p->msg_in_len_max == 0) ||
54*5f657a7fSCristian Dumitrescu (p->msg_out_len_max == 0) ||
55*5f657a7fSCristian Dumitrescu (p->msg_handle == NULL))
56*5f657a7fSCristian Dumitrescu return NULL;
57*5f657a7fSCristian Dumitrescu
58*5f657a7fSCristian Dumitrescu status = inet_aton(p->addr, &server_address.sin_addr);
59*5f657a7fSCristian Dumitrescu if (status == 0)
60*5f657a7fSCristian Dumitrescu return NULL;
61*5f657a7fSCristian Dumitrescu
62*5f657a7fSCristian Dumitrescu /* Memory allocation */
63*5f657a7fSCristian Dumitrescu conn = calloc(1, sizeof(struct conn));
64*5f657a7fSCristian Dumitrescu if (conn == NULL)
65*5f657a7fSCristian Dumitrescu return NULL;
66*5f657a7fSCristian Dumitrescu
67*5f657a7fSCristian Dumitrescu conn->welcome = calloc(1, CONN_WELCOME_LEN_MAX + 1);
68*5f657a7fSCristian Dumitrescu conn->prompt = calloc(1, CONN_PROMPT_LEN_MAX + 1);
69*5f657a7fSCristian Dumitrescu conn->buf = calloc(1, p->buf_size);
70*5f657a7fSCristian Dumitrescu conn->msg_in = calloc(1, p->msg_in_len_max + 1);
71*5f657a7fSCristian Dumitrescu conn->msg_out = calloc(1, p->msg_out_len_max + 1);
72*5f657a7fSCristian Dumitrescu
73*5f657a7fSCristian Dumitrescu if ((conn->welcome == NULL) ||
74*5f657a7fSCristian Dumitrescu (conn->prompt == NULL) ||
75*5f657a7fSCristian Dumitrescu (conn->buf == NULL) ||
76*5f657a7fSCristian Dumitrescu (conn->msg_in == NULL) ||
77*5f657a7fSCristian Dumitrescu (conn->msg_out == NULL)) {
78*5f657a7fSCristian Dumitrescu conn_free(conn);
79*5f657a7fSCristian Dumitrescu return NULL;
80*5f657a7fSCristian Dumitrescu }
81*5f657a7fSCristian Dumitrescu
82*5f657a7fSCristian Dumitrescu /* Server socket */
83*5f657a7fSCristian Dumitrescu server_address.sin_family = AF_INET;
84*5f657a7fSCristian Dumitrescu server_address.sin_port = htons(p->port);
85*5f657a7fSCristian Dumitrescu
86*5f657a7fSCristian Dumitrescu fd_server = socket(AF_INET,
87*5f657a7fSCristian Dumitrescu SOCK_STREAM | SOCK_NONBLOCK,
88*5f657a7fSCristian Dumitrescu 0);
89*5f657a7fSCristian Dumitrescu if (fd_server == -1) {
90*5f657a7fSCristian Dumitrescu conn_free(conn);
91*5f657a7fSCristian Dumitrescu return NULL;
92*5f657a7fSCristian Dumitrescu }
93*5f657a7fSCristian Dumitrescu
94*5f657a7fSCristian Dumitrescu status = bind(fd_server,
95*5f657a7fSCristian Dumitrescu (struct sockaddr *) &server_address,
96*5f657a7fSCristian Dumitrescu sizeof(server_address));
97*5f657a7fSCristian Dumitrescu if (status == -1) {
98*5f657a7fSCristian Dumitrescu conn_free(conn);
99*5f657a7fSCristian Dumitrescu close(fd_server);
100*5f657a7fSCristian Dumitrescu return NULL;
101*5f657a7fSCristian Dumitrescu }
102*5f657a7fSCristian Dumitrescu
103*5f657a7fSCristian Dumitrescu status = listen(fd_server, 16);
104*5f657a7fSCristian Dumitrescu if (status == -1) {
105*5f657a7fSCristian Dumitrescu conn_free(conn);
106*5f657a7fSCristian Dumitrescu close(fd_server);
107*5f657a7fSCristian Dumitrescu return NULL;
108*5f657a7fSCristian Dumitrescu }
109*5f657a7fSCristian Dumitrescu
110*5f657a7fSCristian Dumitrescu /* Client group */
111*5f657a7fSCristian Dumitrescu fd_client_group = epoll_create(1);
112*5f657a7fSCristian Dumitrescu if (fd_client_group == -1) {
113*5f657a7fSCristian Dumitrescu conn_free(conn);
114*5f657a7fSCristian Dumitrescu close(fd_server);
115*5f657a7fSCristian Dumitrescu return NULL;
116*5f657a7fSCristian Dumitrescu }
117*5f657a7fSCristian Dumitrescu
118*5f657a7fSCristian Dumitrescu /* Fill in */
119*5f657a7fSCristian Dumitrescu strncpy(conn->welcome, p->welcome, CONN_WELCOME_LEN_MAX);
120*5f657a7fSCristian Dumitrescu strncpy(conn->prompt, p->prompt, CONN_PROMPT_LEN_MAX);
121*5f657a7fSCristian Dumitrescu conn->buf_size = p->buf_size;
122*5f657a7fSCristian Dumitrescu conn->msg_in_len_max = p->msg_in_len_max;
123*5f657a7fSCristian Dumitrescu conn->msg_out_len_max = p->msg_out_len_max;
124*5f657a7fSCristian Dumitrescu conn->msg_in_len = 0;
125*5f657a7fSCristian Dumitrescu conn->fd_server = fd_server;
126*5f657a7fSCristian Dumitrescu conn->fd_client_group = fd_client_group;
127*5f657a7fSCristian Dumitrescu conn->msg_handle = p->msg_handle;
128*5f657a7fSCristian Dumitrescu conn->msg_handle_arg = p->msg_handle_arg;
129*5f657a7fSCristian Dumitrescu
130*5f657a7fSCristian Dumitrescu return conn;
131*5f657a7fSCristian Dumitrescu }
132*5f657a7fSCristian Dumitrescu
133*5f657a7fSCristian Dumitrescu void
conn_free(struct conn * conn)134*5f657a7fSCristian Dumitrescu conn_free(struct conn *conn)
135*5f657a7fSCristian Dumitrescu {
136*5f657a7fSCristian Dumitrescu if (conn == NULL)
137*5f657a7fSCristian Dumitrescu return;
138*5f657a7fSCristian Dumitrescu
139*5f657a7fSCristian Dumitrescu if (conn->fd_client_group)
140*5f657a7fSCristian Dumitrescu close(conn->fd_client_group);
141*5f657a7fSCristian Dumitrescu
142*5f657a7fSCristian Dumitrescu if (conn->fd_server)
143*5f657a7fSCristian Dumitrescu close(conn->fd_server);
144*5f657a7fSCristian Dumitrescu
145*5f657a7fSCristian Dumitrescu free(conn->msg_out);
146*5f657a7fSCristian Dumitrescu free(conn->msg_in);
147*5f657a7fSCristian Dumitrescu free(conn->prompt);
148*5f657a7fSCristian Dumitrescu free(conn->welcome);
149*5f657a7fSCristian Dumitrescu free(conn);
150*5f657a7fSCristian Dumitrescu }
151*5f657a7fSCristian Dumitrescu
152*5f657a7fSCristian Dumitrescu int
conn_poll_for_conn(struct conn * conn)153*5f657a7fSCristian Dumitrescu conn_poll_for_conn(struct conn *conn)
154*5f657a7fSCristian Dumitrescu {
155*5f657a7fSCristian Dumitrescu struct sockaddr_in client_address;
156*5f657a7fSCristian Dumitrescu struct epoll_event event;
157*5f657a7fSCristian Dumitrescu socklen_t client_address_length;
158*5f657a7fSCristian Dumitrescu int fd_client, status;
159*5f657a7fSCristian Dumitrescu
160*5f657a7fSCristian Dumitrescu /* Check input arguments */
161*5f657a7fSCristian Dumitrescu if (conn == NULL)
162*5f657a7fSCristian Dumitrescu return -1;
163*5f657a7fSCristian Dumitrescu
164*5f657a7fSCristian Dumitrescu /* Server socket */
165*5f657a7fSCristian Dumitrescu client_address_length = sizeof(client_address);
166*5f657a7fSCristian Dumitrescu fd_client = accept4(conn->fd_server,
167*5f657a7fSCristian Dumitrescu (struct sockaddr *) &client_address,
168*5f657a7fSCristian Dumitrescu &client_address_length,
169*5f657a7fSCristian Dumitrescu SOCK_NONBLOCK);
170*5f657a7fSCristian Dumitrescu if (fd_client == -1) {
171*5f657a7fSCristian Dumitrescu if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
172*5f657a7fSCristian Dumitrescu return 0;
173*5f657a7fSCristian Dumitrescu
174*5f657a7fSCristian Dumitrescu return -1;
175*5f657a7fSCristian Dumitrescu }
176*5f657a7fSCristian Dumitrescu
177*5f657a7fSCristian Dumitrescu /* Client group */
178*5f657a7fSCristian Dumitrescu event.events = EPOLLIN | EPOLLRDHUP | EPOLLHUP;
179*5f657a7fSCristian Dumitrescu event.data.fd = fd_client;
180*5f657a7fSCristian Dumitrescu
181*5f657a7fSCristian Dumitrescu status = epoll_ctl(conn->fd_client_group,
182*5f657a7fSCristian Dumitrescu EPOLL_CTL_ADD,
183*5f657a7fSCristian Dumitrescu fd_client,
184*5f657a7fSCristian Dumitrescu &event);
185*5f657a7fSCristian Dumitrescu if (status == -1) {
186*5f657a7fSCristian Dumitrescu close(fd_client);
187*5f657a7fSCristian Dumitrescu return -1;
188*5f657a7fSCristian Dumitrescu }
189*5f657a7fSCristian Dumitrescu
190*5f657a7fSCristian Dumitrescu /* Client */
191*5f657a7fSCristian Dumitrescu status = write(fd_client,
192*5f657a7fSCristian Dumitrescu conn->welcome,
193*5f657a7fSCristian Dumitrescu strlen(conn->welcome));
194*5f657a7fSCristian Dumitrescu if (status == -1) {
195*5f657a7fSCristian Dumitrescu close(fd_client);
196*5f657a7fSCristian Dumitrescu return -1;
197*5f657a7fSCristian Dumitrescu }
198*5f657a7fSCristian Dumitrescu
199*5f657a7fSCristian Dumitrescu status = write(fd_client,
200*5f657a7fSCristian Dumitrescu conn->prompt,
201*5f657a7fSCristian Dumitrescu strlen(conn->prompt));
202*5f657a7fSCristian Dumitrescu if (status == -1) {
203*5f657a7fSCristian Dumitrescu close(fd_client);
204*5f657a7fSCristian Dumitrescu return -1;
205*5f657a7fSCristian Dumitrescu }
206*5f657a7fSCristian Dumitrescu
207*5f657a7fSCristian Dumitrescu return 0;
208*5f657a7fSCristian Dumitrescu }
209*5f657a7fSCristian Dumitrescu
210*5f657a7fSCristian Dumitrescu static int
data_event_handle(struct conn * conn,int fd_client)211*5f657a7fSCristian Dumitrescu data_event_handle(struct conn *conn,
212*5f657a7fSCristian Dumitrescu int fd_client)
213*5f657a7fSCristian Dumitrescu {
214*5f657a7fSCristian Dumitrescu ssize_t len, i, status;
215*5f657a7fSCristian Dumitrescu
216*5f657a7fSCristian Dumitrescu /* Read input message */
217*5f657a7fSCristian Dumitrescu
218*5f657a7fSCristian Dumitrescu len = read(fd_client,
219*5f657a7fSCristian Dumitrescu conn->buf,
220*5f657a7fSCristian Dumitrescu conn->buf_size);
221*5f657a7fSCristian Dumitrescu if (len == -1) {
222*5f657a7fSCristian Dumitrescu if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
223*5f657a7fSCristian Dumitrescu return 0;
224*5f657a7fSCristian Dumitrescu
225*5f657a7fSCristian Dumitrescu return -1;
226*5f657a7fSCristian Dumitrescu }
227*5f657a7fSCristian Dumitrescu if (len == 0)
228*5f657a7fSCristian Dumitrescu return 0;
229*5f657a7fSCristian Dumitrescu
230*5f657a7fSCristian Dumitrescu /* Handle input messages */
231*5f657a7fSCristian Dumitrescu for (i = 0; i < len; i++) {
232*5f657a7fSCristian Dumitrescu if (conn->buf[i] == '\n') {
233*5f657a7fSCristian Dumitrescu size_t n;
234*5f657a7fSCristian Dumitrescu
235*5f657a7fSCristian Dumitrescu conn->msg_in[conn->msg_in_len] = 0;
236*5f657a7fSCristian Dumitrescu conn->msg_out[0] = 0;
237*5f657a7fSCristian Dumitrescu
238*5f657a7fSCristian Dumitrescu conn->msg_handle(conn->msg_in,
239*5f657a7fSCristian Dumitrescu conn->msg_out,
240*5f657a7fSCristian Dumitrescu conn->msg_out_len_max,
241*5f657a7fSCristian Dumitrescu conn->msg_handle_arg);
242*5f657a7fSCristian Dumitrescu
243*5f657a7fSCristian Dumitrescu n = strlen(conn->msg_out);
244*5f657a7fSCristian Dumitrescu if (n) {
245*5f657a7fSCristian Dumitrescu status = write(fd_client,
246*5f657a7fSCristian Dumitrescu conn->msg_out,
247*5f657a7fSCristian Dumitrescu n);
248*5f657a7fSCristian Dumitrescu if (status == -1)
249*5f657a7fSCristian Dumitrescu return status;
250*5f657a7fSCristian Dumitrescu }
251*5f657a7fSCristian Dumitrescu
252*5f657a7fSCristian Dumitrescu conn->msg_in_len = 0;
253*5f657a7fSCristian Dumitrescu } else if (conn->msg_in_len < conn->msg_in_len_max) {
254*5f657a7fSCristian Dumitrescu conn->msg_in[conn->msg_in_len] = conn->buf[i];
255*5f657a7fSCristian Dumitrescu conn->msg_in_len++;
256*5f657a7fSCristian Dumitrescu } else {
257*5f657a7fSCristian Dumitrescu status = write(fd_client,
258*5f657a7fSCristian Dumitrescu MSG_CMD_TOO_LONG,
259*5f657a7fSCristian Dumitrescu strlen(MSG_CMD_TOO_LONG));
260*5f657a7fSCristian Dumitrescu if (status == -1)
261*5f657a7fSCristian Dumitrescu return status;
262*5f657a7fSCristian Dumitrescu
263*5f657a7fSCristian Dumitrescu conn->msg_in_len = 0;
264*5f657a7fSCristian Dumitrescu }
265*5f657a7fSCristian Dumitrescu }
266*5f657a7fSCristian Dumitrescu
267*5f657a7fSCristian Dumitrescu /* Write prompt */
268*5f657a7fSCristian Dumitrescu status = write(fd_client,
269*5f657a7fSCristian Dumitrescu conn->prompt,
270*5f657a7fSCristian Dumitrescu strlen(conn->prompt));
271*5f657a7fSCristian Dumitrescu if (status == -1)
272*5f657a7fSCristian Dumitrescu return status;
273*5f657a7fSCristian Dumitrescu
274*5f657a7fSCristian Dumitrescu return 0;
275*5f657a7fSCristian Dumitrescu }
276*5f657a7fSCristian Dumitrescu
277*5f657a7fSCristian Dumitrescu static int
control_event_handle(struct conn * conn,int fd_client)278*5f657a7fSCristian Dumitrescu control_event_handle(struct conn *conn,
279*5f657a7fSCristian Dumitrescu int fd_client)
280*5f657a7fSCristian Dumitrescu {
281*5f657a7fSCristian Dumitrescu int status;
282*5f657a7fSCristian Dumitrescu
283*5f657a7fSCristian Dumitrescu status = epoll_ctl(conn->fd_client_group,
284*5f657a7fSCristian Dumitrescu EPOLL_CTL_DEL,
285*5f657a7fSCristian Dumitrescu fd_client,
286*5f657a7fSCristian Dumitrescu NULL);
287*5f657a7fSCristian Dumitrescu if (status == -1)
288*5f657a7fSCristian Dumitrescu return -1;
289*5f657a7fSCristian Dumitrescu
290*5f657a7fSCristian Dumitrescu status = close(fd_client);
291*5f657a7fSCristian Dumitrescu if (status == -1)
292*5f657a7fSCristian Dumitrescu return -1;
293*5f657a7fSCristian Dumitrescu
294*5f657a7fSCristian Dumitrescu return 0;
295*5f657a7fSCristian Dumitrescu }
296*5f657a7fSCristian Dumitrescu
297*5f657a7fSCristian Dumitrescu int
conn_poll_for_msg(struct conn * conn)298*5f657a7fSCristian Dumitrescu conn_poll_for_msg(struct conn *conn)
299*5f657a7fSCristian Dumitrescu {
300*5f657a7fSCristian Dumitrescu struct epoll_event event;
301*5f657a7fSCristian Dumitrescu int fd_client, status, status_data = 0, status_control = 0;
302*5f657a7fSCristian Dumitrescu
303*5f657a7fSCristian Dumitrescu /* Check input arguments */
304*5f657a7fSCristian Dumitrescu if (conn == NULL)
305*5f657a7fSCristian Dumitrescu return -1;
306*5f657a7fSCristian Dumitrescu
307*5f657a7fSCristian Dumitrescu /* Client group */
308*5f657a7fSCristian Dumitrescu status = epoll_wait(conn->fd_client_group,
309*5f657a7fSCristian Dumitrescu &event,
310*5f657a7fSCristian Dumitrescu 1,
311*5f657a7fSCristian Dumitrescu 0);
312*5f657a7fSCristian Dumitrescu if (status == -1)
313*5f657a7fSCristian Dumitrescu return -1;
314*5f657a7fSCristian Dumitrescu if (status == 0)
315*5f657a7fSCristian Dumitrescu return 0;
316*5f657a7fSCristian Dumitrescu
317*5f657a7fSCristian Dumitrescu fd_client = event.data.fd;
318*5f657a7fSCristian Dumitrescu
319*5f657a7fSCristian Dumitrescu /* Data available */
320*5f657a7fSCristian Dumitrescu if (event.events & EPOLLIN)
321*5f657a7fSCristian Dumitrescu status_data = data_event_handle(conn, fd_client);
322*5f657a7fSCristian Dumitrescu
323*5f657a7fSCristian Dumitrescu /* Control events */
324*5f657a7fSCristian Dumitrescu if (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP))
325*5f657a7fSCristian Dumitrescu status_control = control_event_handle(conn, fd_client);
326*5f657a7fSCristian Dumitrescu
327*5f657a7fSCristian Dumitrescu if (status_data || status_control)
328*5f657a7fSCristian Dumitrescu return -1;
329*5f657a7fSCristian Dumitrescu
330*5f657a7fSCristian Dumitrescu return 0;
331*5f657a7fSCristian Dumitrescu }
332