]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
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 | ||
11fdf7f2 TL |
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 * | |
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 | |
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->prompt); | |
148 | free(conn->welcome); | |
149 | free(conn); | |
150 | } | |
151 | ||
152 | int | |
153 | softnic_conn_poll_for_conn(struct softnic_conn *conn) | |
154 | { | |
155 | struct sockaddr_in client_address; | |
156 | struct epoll_event event; | |
157 | socklen_t client_address_length; | |
158 | int fd_client, status; | |
159 | ||
160 | /* Check input arguments */ | |
161 | if (conn == NULL) | |
162 | return -1; | |
163 | ||
164 | /* Server socket */ | |
165 | client_address_length = sizeof(client_address); | |
166 | fd_client = accept4(conn->fd_server, | |
167 | (struct sockaddr *)&client_address, | |
168 | &client_address_length, | |
169 | SOCK_NONBLOCK); | |
170 | if (fd_client == -1) { | |
171 | if (errno == EAGAIN || errno == EWOULDBLOCK) | |
172 | return 0; | |
173 | ||
174 | return -1; | |
175 | } | |
176 | ||
177 | /* Client group */ | |
178 | event.events = EPOLLIN | EPOLLRDHUP | EPOLLHUP; | |
179 | event.data.fd = fd_client; | |
180 | ||
181 | status = epoll_ctl(conn->fd_client_group, | |
182 | EPOLL_CTL_ADD, | |
183 | fd_client, | |
184 | &event); | |
185 | if (status == -1) { | |
186 | close(fd_client); | |
187 | return -1; | |
188 | } | |
189 | ||
190 | /* Client */ | |
191 | status = write(fd_client, | |
192 | conn->welcome, | |
193 | strlen(conn->welcome)); | |
194 | if (status == -1) { | |
195 | close(fd_client); | |
196 | return -1; | |
197 | } | |
198 | ||
199 | status = write(fd_client, | |
200 | conn->prompt, | |
201 | strlen(conn->prompt)); | |
202 | if (status == -1) { | |
203 | close(fd_client); | |
204 | return -1; | |
205 | } | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | static int | |
211 | data_event_handle(struct softnic_conn *conn, | |
212 | int fd_client) | |
213 | { | |
214 | ssize_t len, i, status; | |
215 | ||
216 | /* Read input message */ | |
217 | ||
218 | len = read(fd_client, | |
219 | conn->buf, | |
220 | conn->buf_size); | |
221 | if (len == -1) { | |
222 | if (errno == EAGAIN || errno == EWOULDBLOCK) | |
223 | return 0; | |
224 | ||
225 | return -1; | |
226 | } | |
227 | if (len == 0) | |
228 | return 0; | |
229 | ||
230 | /* Handle input messages */ | |
231 | for (i = 0; i < len; i++) { | |
232 | if (conn->buf[i] == '\n') { | |
233 | size_t n; | |
234 | ||
235 | conn->msg_in[conn->msg_in_len] = 0; | |
236 | conn->msg_out[0] = 0; | |
237 | ||
238 | conn->msg_handle(conn->msg_in, | |
239 | conn->msg_out, | |
240 | conn->msg_out_len_max, | |
241 | conn->msg_handle_arg); | |
242 | ||
243 | n = strlen(conn->msg_out); | |
244 | if (n) { | |
245 | status = write(fd_client, | |
246 | conn->msg_out, | |
247 | n); | |
248 | if (status == -1) | |
249 | return status; | |
250 | } | |
251 | ||
252 | conn->msg_in_len = 0; | |
253 | } else if (conn->msg_in_len < conn->msg_in_len_max) { | |
254 | conn->msg_in[conn->msg_in_len] = conn->buf[i]; | |
255 | conn->msg_in_len++; | |
256 | } else { | |
257 | status = write(fd_client, | |
258 | MSG_CMD_TOO_LONG, | |
259 | strlen(MSG_CMD_TOO_LONG)); | |
260 | if (status == -1) | |
261 | return status; | |
262 | ||
263 | conn->msg_in_len = 0; | |
264 | } | |
265 | } | |
266 | ||
267 | /* Write prompt */ | |
268 | status = write(fd_client, | |
269 | conn->prompt, | |
270 | strlen(conn->prompt)); | |
271 | if (status == -1) | |
272 | return status; | |
273 | ||
274 | return 0; | |
275 | } | |
276 | ||
277 | static int | |
278 | control_event_handle(struct softnic_conn *conn, | |
279 | int fd_client) | |
280 | { | |
281 | int status; | |
282 | ||
283 | status = epoll_ctl(conn->fd_client_group, | |
284 | EPOLL_CTL_DEL, | |
285 | fd_client, | |
286 | NULL); | |
287 | if (status == -1) | |
288 | return -1; | |
289 | ||
290 | status = close(fd_client); | |
291 | if (status == -1) | |
292 | return -1; | |
293 | ||
294 | return 0; | |
295 | } | |
296 | ||
297 | int | |
298 | softnic_conn_poll_for_msg(struct softnic_conn *conn) | |
299 | { | |
300 | struct epoll_event event; | |
301 | int fd_client, status, status_data = 0, status_control = 0; | |
302 | ||
303 | /* Check input arguments */ | |
304 | if (conn == NULL) | |
305 | return -1; | |
306 | ||
307 | /* Client group */ | |
308 | status = epoll_wait(conn->fd_client_group, | |
309 | &event, | |
310 | 1, | |
311 | 0); | |
312 | if (status == -1) | |
313 | return -1; | |
314 | if (status == 0) | |
315 | return 0; | |
316 | ||
317 | fd_client = event.data.fd; | |
318 | ||
319 | /* Data available */ | |
320 | if (event.events & EPOLLIN) | |
321 | status_data = data_event_handle(conn, fd_client); | |
322 | ||
323 | /* Control events */ | |
324 | if (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) | |
325 | status_control = control_event_handle(conn, fd_client); | |
326 | ||
327 | if (status_data || status_control) | |
328 | return -1; | |
329 | ||
330 | return 0; | |
331 | } |