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 * 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->buf); 148 free(conn->prompt); 149 free(conn->welcome); 150 free(conn); 151 } 152 153 int 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 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 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 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