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