2014-08-16 08:24:38 +02:00
|
|
|
/*
|
|
|
|
This file is part of libcapwap.
|
|
|
|
|
|
|
|
libcapwap is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
libcapwap is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
*/
|
2014-07-11 22:12:11 +02:00
|
|
|
#include <stdlib.h>
|
2014-07-28 06:56:37 +02:00
|
|
|
#include <string.h>
|
2015-04-10 17:14:55 +02:00
|
|
|
#include <errno.h>
|
2014-07-11 22:12:11 +02:00
|
|
|
|
2016-03-11 22:23:00 +01:00
|
|
|
#include "cw.h"
|
2015-04-10 17:14:55 +02:00
|
|
|
#include "dbg.h"
|
|
|
|
|
2015-04-10 17:52:01 +02:00
|
|
|
#include "log.h"
|
2014-08-17 18:39:48 +02:00
|
|
|
#include "cw_util.h"
|
|
|
|
|
2014-07-11 22:12:11 +02:00
|
|
|
#include "conn.h"
|
2014-08-16 08:24:38 +02:00
|
|
|
#include "sock.h"
|
2014-07-11 22:12:11 +02:00
|
|
|
|
2018-03-15 20:07:17 +01:00
|
|
|
/*#include "stravltree.h"*/
|
2016-03-08 01:20:22 +01:00
|
|
|
#include "mod.h"
|
2018-03-12 11:22:06 +01:00
|
|
|
#include "msgset.h"
|
|
|
|
|
2022-07-31 17:15:32 +02:00
|
|
|
#include "val.h"
|
2018-03-05 21:42:11 +01:00
|
|
|
|
2018-03-05 09:56:39 +01:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Init response message header
|
|
|
|
*/
|
2022-08-09 22:35:47 +02:00
|
|
|
void cw_init_response(struct cw_Conn *conn, uint8_t * req)
|
2015-04-10 17:14:55 +02:00
|
|
|
{
|
2018-03-05 09:56:39 +01:00
|
|
|
uint8_t *buffer;
|
|
|
|
int shbytes, dhbytes;
|
|
|
|
uint8_t *msgptr, *dmsgptr;
|
2022-08-09 09:52:30 +02:00
|
|
|
|
2018-03-05 09:56:39 +01:00
|
|
|
buffer = conn->resp_buffer;
|
|
|
|
shbytes = cw_get_hdr_msg_offset(req);
|
2022-08-09 09:52:30 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
memcpy(buffer, req, shbytes);
|
2015-04-18 11:20:24 +02:00
|
|
|
|
2016-02-21 09:25:36 +01:00
|
|
|
cw_set_hdr_rmac(buffer, conn->base_rmac);
|
2018-03-05 09:56:39 +01:00
|
|
|
/*
|
2016-02-21 09:25:36 +01:00
|
|
|
// cw_set_hdr_hlen(buffer, 2);
|
|
|
|
// cw_set_hdr_flags(buffer, CW_FLAG_HDR_M, 1);
|
2018-03-05 09:56:39 +01:00
|
|
|
*/
|
2015-04-18 11:20:24 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
dhbytes = cw_get_hdr_msg_offset(buffer);
|
|
|
|
|
2018-03-05 09:56:39 +01:00
|
|
|
msgptr = req + shbytes;
|
|
|
|
dmsgptr = buffer + dhbytes;
|
2015-04-10 17:14:55 +02:00
|
|
|
|
|
|
|
cw_set_msg_type(dmsgptr, cw_get_msg_type(msgptr) + 1);
|
|
|
|
cw_set_msg_seqnum(dmsgptr, cw_get_msg_seqnum(msgptr));
|
|
|
|
cw_set_msg_flags(dmsgptr, 0);
|
|
|
|
}
|
|
|
|
|
2022-08-09 22:35:47 +02:00
|
|
|
void cw_init_request(struct cw_Conn *conn, int msg_id)
|
2015-04-10 17:14:55 +02:00
|
|
|
{
|
|
|
|
uint8_t *buffer = conn->req_buffer;
|
2018-03-05 09:56:39 +01:00
|
|
|
uint8_t *msgptr;
|
2022-08-09 09:52:30 +02:00
|
|
|
|
2015-04-12 16:57:00 +02:00
|
|
|
/* zero the first 8 bytes */
|
2018-03-25 10:35:53 +02:00
|
|
|
cw_set_dword(buffer + 0, 0);
|
|
|
|
cw_set_dword(buffer + 4, 0);
|
2015-04-12 16:57:00 +02:00
|
|
|
|
|
|
|
/* unencrypted */
|
2018-02-23 09:12:39 +01:00
|
|
|
cw_set_hdr_preamble(buffer, CAPWAP_VERSION << 4 | 0);
|
2016-02-21 09:25:36 +01:00
|
|
|
|
|
|
|
cw_set_hdr_rmac(buffer, conn->base_rmac);
|
2018-03-05 09:56:39 +01:00
|
|
|
/*
|
2015-04-18 11:20:24 +02:00
|
|
|
//cw_set_hdr_hlen(buffer, 2);
|
2018-03-05 09:56:39 +01:00
|
|
|
*/
|
2015-04-12 16:57:00 +02:00
|
|
|
|
2016-03-09 18:39:09 +01:00
|
|
|
|
|
|
|
cw_set_hdr_wbid(buffer, conn->wbid);
|
2015-04-10 17:14:55 +02:00
|
|
|
cw_set_hdr_rid(buffer, 0);
|
2015-04-18 11:20:24 +02:00
|
|
|
|
|
|
|
|
2018-03-05 09:56:39 +01:00
|
|
|
msgptr = cw_get_hdr_msg_offset(buffer) + buffer;
|
2015-04-10 17:14:55 +02:00
|
|
|
cw_set_msg_type(msgptr, msg_id);
|
|
|
|
cw_set_msg_flags(msgptr, 0);
|
|
|
|
cw_set_msg_elems_len(msgptr, 0);
|
|
|
|
}
|
2014-07-11 22:12:11 +02:00
|
|
|
|
2022-08-09 22:35:47 +02:00
|
|
|
void cw_init_data_msg(struct cw_Conn *conn)
|
2016-03-21 08:25:32 +01:00
|
|
|
{
|
|
|
|
uint8_t *buffer = conn->req_buffer;
|
2018-03-25 10:35:53 +02:00
|
|
|
cw_set_dword(buffer + 0, 0);
|
|
|
|
cw_set_dword(buffer + 4, 0);
|
2016-03-21 08:25:32 +01:00
|
|
|
|
2016-03-27 04:50:04 +02:00
|
|
|
/* unencrypted */
|
2018-02-23 09:12:39 +01:00
|
|
|
cw_set_hdr_preamble(buffer, CAPWAP_VERSION << 4 | 0);
|
2016-03-27 04:50:04 +02:00
|
|
|
|
|
|
|
|
2016-03-21 08:25:32 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
/**
|
|
|
|
* send a response
|
|
|
|
*/
|
2022-08-09 22:35:47 +02:00
|
|
|
int cw_send_response(struct cw_Conn *conn, uint8_t * rawmsg, int len)
|
2014-07-11 22:12:11 +02:00
|
|
|
{
|
2018-03-17 12:32:40 +01:00
|
|
|
int rc;
|
2015-04-10 17:14:55 +02:00
|
|
|
cw_init_response(conn, rawmsg);
|
2022-08-10 13:16:09 +02:00
|
|
|
rc = cw_assemble_message(conn, conn->resp_buffer);
|
2018-03-17 12:32:40 +01:00
|
|
|
if (!cw_result_is_ok(rc))
|
2014-07-11 22:12:11 +02:00
|
|
|
return 0;
|
2015-04-10 17:14:55 +02:00
|
|
|
conn_send_msg(conn, conn->resp_buffer);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Special case error message, which is sent when an unexpected messages
|
2016-02-16 08:13:34 +01:00
|
|
|
* was received or something else happened.
|
2015-04-10 17:14:55 +02:00
|
|
|
* @param conn conection
|
|
|
|
* @param rawmsg the received request message, which the response belongs to
|
2016-03-07 18:56:02 +01:00
|
|
|
* @param result_code result code to send
|
2015-04-10 17:14:55 +02:00
|
|
|
* @return 1
|
|
|
|
*/
|
2022-08-09 22:35:47 +02:00
|
|
|
int cw_send_error_response(struct cw_Conn *conn, uint8_t * rawmsg,
|
2022-08-09 09:52:30 +02:00
|
|
|
uint32_t result_code)
|
2015-04-10 17:14:55 +02:00
|
|
|
{
|
2022-08-09 09:52:30 +02:00
|
|
|
uint8_t *out, *dst;
|
2018-03-05 09:56:39 +01:00
|
|
|
int l;
|
2022-08-09 09:52:30 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
cw_init_response(conn, rawmsg);
|
|
|
|
|
2018-03-05 09:56:39 +01:00
|
|
|
out = conn->resp_buffer;
|
2015-04-10 17:14:55 +02:00
|
|
|
|
2018-03-05 09:56:39 +01:00
|
|
|
dst = cw_get_hdr_msg_elems_ptr(out);
|
|
|
|
l = cw_put_elem_result_code(dst, result_code);
|
2014-07-24 23:54:53 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
cw_set_msg_elems_len(out + cw_get_hdr_msg_offset(out), l);
|
|
|
|
|
|
|
|
conn_send_msg(conn, conn->resp_buffer);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2014-07-24 23:54:53 +02:00
|
|
|
|
2016-03-08 01:20:22 +01:00
|
|
|
|
2022-08-09 22:35:47 +02:00
|
|
|
static struct cw_MsgSet *load_msg_set(struct cw_Conn *conn, uint8_t * rawmsg,
|
2022-08-09 09:52:30 +02:00
|
|
|
int len, int elems_len,
|
|
|
|
struct sockaddr *from)
|
2016-03-08 01:20:22 +01:00
|
|
|
{
|
2018-03-05 12:23:16 +01:00
|
|
|
char sock_buf[SOCK_ADDR_BUFSIZE];
|
2018-03-05 20:39:15 +01:00
|
|
|
struct cw_Mod *cmod, *bmod;
|
2022-08-09 09:52:30 +02:00
|
|
|
|
2018-03-05 20:39:15 +01:00
|
|
|
cmod =
|
2022-08-09 09:52:30 +02:00
|
|
|
cw_mod_detect(conn, rawmsg, len, elems_len, from,
|
|
|
|
CW_MOD_MODE_CAPWAP);
|
2016-03-08 01:20:22 +01:00
|
|
|
if (cmod == MOD_NULL) {
|
2016-03-13 18:38:51 +01:00
|
|
|
cw_dbg(DBG_MSG_ERR,
|
2018-02-25 19:12:28 +01:00
|
|
|
"Can't find mod to handle connection from %s, discarding message",
|
2022-08-09 09:52:30 +02:00
|
|
|
sock_addr2str_p(from, sock_buf));
|
2016-03-08 01:20:22 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-08-09 09:52:30 +02:00
|
|
|
bmod =
|
|
|
|
cw_mod_detect(conn, rawmsg, len, elems_len, from,
|
|
|
|
CW_MOD_MODE_BINDINGS);
|
|
|
|
|
2016-03-08 01:20:22 +01:00
|
|
|
|
2016-03-13 18:38:51 +01:00
|
|
|
cw_dbg(DBG_INFO, "Mods deteced: %s,%s", cmod->name, bmod->name);
|
2016-03-08 01:20:22 +01:00
|
|
|
|
2018-04-02 21:00:37 +02:00
|
|
|
conn->cmod = cmod;
|
|
|
|
conn->bmod = bmod;
|
2016-03-08 01:20:22 +01:00
|
|
|
|
2022-08-09 09:52:30 +02:00
|
|
|
return cw_mod_get_msg_set(conn, cmod, bmod);
|
2018-02-26 20:18:53 +01:00
|
|
|
|
2016-03-08 01:20:22 +01:00
|
|
|
}
|
|
|
|
|
2018-03-19 08:31:50 +01:00
|
|
|
/*
|
2022-08-09 22:35:47 +02:00
|
|
|
int cw_in_check_generic(struct cw_Conn *conn, struct cw_action_in *a, uint8_t * data,
|
2016-03-19 12:57:47 +01:00
|
|
|
int len,struct sockaddr *from)
|
|
|
|
{
|
2018-03-19 08:31:50 +01:00
|
|
|
// if (cw_is_request(a->msg_id)){
|
|
|
|
// return cw_in_check_generic_req(conn,a,data,len,from);
|
|
|
|
// }
|
|
|
|
// return cw_in_check_generic_resp(conn,a,data,len,from);
|
|
|
|
|
2016-03-19 12:57:47 +01:00
|
|
|
|
|
|
|
}
|
2018-03-19 08:31:50 +01:00
|
|
|
*/
|
2016-03-08 01:20:22 +01:00
|
|
|
|
2018-03-07 08:57:04 +01:00
|
|
|
/*
|
2022-08-09 22:35:47 +02:00
|
|
|
void cw_read_elem(struct cw_ElemHandler * handler, struct cw_Conn * conn,
|
2018-03-05 21:42:11 +01:00
|
|
|
uint8_t * elem_data, int elem_len, struct sockaddr * from){
|
|
|
|
mavldata_t data, *result;
|
|
|
|
char str[30];
|
|
|
|
|
|
|
|
result = handler->type->get(&data,elem_data,elem_len);
|
|
|
|
|
|
|
|
handler->type->to_str(result,str,30);
|
|
|
|
printf("Read %d-%s: %s %s\n", handler->id, handler->name, handler->key, str);
|
2022-07-18 01:15:17 +02:00
|
|
|
//mavl_insert(conn->remote_cfg
|
2018-03-05 21:42:11 +01:00
|
|
|
}
|
2018-03-07 08:57:04 +01:00
|
|
|
*/
|
|
|
|
|
2014-08-17 18:39:48 +02:00
|
|
|
|
2022-08-09 22:35:47 +02:00
|
|
|
static int process_elements(struct cw_Conn *conn, uint8_t * rawmsg, int len,
|
2016-02-21 09:25:36 +01:00
|
|
|
struct sockaddr *from)
|
2015-04-10 17:14:55 +02:00
|
|
|
{
|
2018-03-11 10:34:20 +01:00
|
|
|
mavl_t mand_found;
|
|
|
|
mlist_t unrecognized;
|
2018-03-19 08:31:50 +01:00
|
|
|
struct cw_MsgData search;
|
2022-08-09 09:52:30 +02:00
|
|
|
struct cw_MsgData *message;
|
2018-03-25 10:07:39 +02:00
|
|
|
int result_code;
|
2018-05-07 10:57:12 +02:00
|
|
|
cw_State_t *ui;
|
|
|
|
|
2018-03-25 10:07:39 +02:00
|
|
|
uint8_t *elems_ptr;
|
|
|
|
uint8_t *elem;
|
2022-08-09 09:52:30 +02:00
|
|
|
struct cw_ElemHandlerParams params;
|
|
|
|
|
2018-03-05 20:39:15 +01:00
|
|
|
char sock_buf[SOCK_ADDR_BUFSIZE]; /**< to hold str from sockaddr2str */
|
2022-08-09 09:52:30 +02:00
|
|
|
|
|
|
|
/*struct cw_action_in as, *af, *afm; */
|
2014-08-17 18:39:48 +02:00
|
|
|
|
2016-02-21 09:25:36 +01:00
|
|
|
int offset = cw_get_hdr_msg_offset(rawmsg);
|
2015-04-12 16:57:00 +02:00
|
|
|
|
|
|
|
uint8_t *msg_ptr = rawmsg + offset;
|
2014-08-17 18:39:48 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
int elems_len = cw_get_msg_elems_len(msg_ptr);
|
2014-07-11 22:12:11 +02:00
|
|
|
|
2016-02-21 09:25:36 +01:00
|
|
|
int payloadlen = len - offset;
|
|
|
|
|
2016-03-01 19:10:08 +01:00
|
|
|
|
2015-04-12 16:57:00 +02:00
|
|
|
/* pre-check message */
|
2016-02-21 09:25:36 +01:00
|
|
|
if (payloadlen - 8 != elems_len) {
|
2015-04-12 16:57:00 +02:00
|
|
|
|
2015-04-13 11:00:46 +02:00
|
|
|
if (conn->strict_hdr) {
|
2015-04-12 16:57:00 +02:00
|
|
|
cw_dbg(DBG_MSG_ERR,
|
|
|
|
"Discarding message from %s, msgelems len=%d, payload len=%d, (Strict CAPWAP) ",
|
2022-08-09 09:52:30 +02:00
|
|
|
sock_addr2str(&conn->addr, sock_buf), elems_len,
|
|
|
|
payloadlen - 8);
|
2016-02-21 09:25:36 +01:00
|
|
|
errno = EAGAIN;
|
2015-04-12 16:57:00 +02:00
|
|
|
return -1;
|
2014-08-17 18:39:48 +02:00
|
|
|
}
|
2016-03-01 19:10:08 +01:00
|
|
|
|
|
|
|
|
2016-02-21 09:25:36 +01:00
|
|
|
if (elems_len < payloadlen - 8) {
|
2015-04-12 16:57:00 +02:00
|
|
|
cw_dbg(DBG_RFC,
|
|
|
|
"Packet from from %s has %d bytes of extra data, ignoring.",
|
2022-08-09 09:52:30 +02:00
|
|
|
sock_addr2str(&conn->addr, sock_buf),
|
|
|
|
payloadlen - 8 - elems_len);
|
2015-04-12 16:57:00 +02:00
|
|
|
elems_len = len - 8;
|
2014-08-17 18:39:48 +02:00
|
|
|
}
|
2014-07-11 22:12:11 +02:00
|
|
|
|
2016-02-21 09:25:36 +01:00
|
|
|
if (elems_len > payloadlen - 8) {
|
2016-03-01 19:10:08 +01:00
|
|
|
|
2015-04-12 16:57:00 +02:00
|
|
|
cw_dbg(DBG_RFC,
|
|
|
|
"Packet from from %s has msgelems len of %d bytes, but has only %d bytes of data, truncating.",
|
2022-08-09 09:52:30 +02:00
|
|
|
sock_addr2str(&conn->addr, sock_buf), elems_len,
|
|
|
|
payloadlen - 8);
|
2016-02-21 09:25:36 +01:00
|
|
|
elems_len = payloadlen - 8;
|
2014-08-17 18:39:48 +02:00
|
|
|
}
|
|
|
|
}
|
2014-07-11 22:12:11 +02:00
|
|
|
|
2016-03-01 19:10:08 +01:00
|
|
|
|
2022-08-13 09:47:12 +02:00
|
|
|
/* Detect the connecting WTP type */
|
2016-02-23 08:58:05 +01:00
|
|
|
if (!conn->detected) {
|
2018-03-02 08:49:37 +01:00
|
|
|
|
2022-08-09 09:52:30 +02:00
|
|
|
struct cw_MsgSet *set =
|
|
|
|
load_msg_set(conn, rawmsg, len, elems_len, from);
|
2018-02-26 18:28:12 +01:00
|
|
|
if (!set) {
|
2018-03-05 20:39:15 +01:00
|
|
|
/*
|
2018-02-26 14:26:17 +01:00
|
|
|
//cw_log(LOG_ERR, "Error");
|
2022-08-09 09:52:30 +02:00
|
|
|
*/
|
2016-03-13 18:38:51 +01:00
|
|
|
errno = EAGAIN;
|
2016-03-08 01:20:22 +01:00
|
|
|
return -1;
|
2016-02-23 08:58:05 +01:00
|
|
|
}
|
2018-04-03 07:35:55 +02:00
|
|
|
conn->cmod->setup_cfg(conn);
|
2022-08-09 09:52:30 +02:00
|
|
|
conn->msgset = set;
|
2016-03-13 18:38:51 +01:00
|
|
|
conn->detected = 1;
|
2022-08-13 09:47:12 +02:00
|
|
|
|
|
|
|
if (conn->setup_complete)
|
|
|
|
conn->setup_complete(conn);
|
|
|
|
|
2016-02-23 19:38:10 +01:00
|
|
|
}
|
|
|
|
|
2018-03-02 08:49:37 +01:00
|
|
|
/** debug the received message */
|
2018-02-26 20:18:53 +01:00
|
|
|
cw_dbg_msg(DBG_MSG_IN, conn, rawmsg, len, from);
|
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
/* prepare struct for search operation */
|
2018-02-26 20:18:53 +01:00
|
|
|
search.type = cw_get_msg_id(msg_ptr);
|
2022-08-09 09:52:30 +02:00
|
|
|
|
2018-03-02 08:49:37 +01:00
|
|
|
/* Search message */
|
2022-08-09 09:52:30 +02:00
|
|
|
|
|
|
|
message = mavl_get(conn->msgset->msgdata, &search);
|
|
|
|
|
2018-03-25 10:07:39 +02:00
|
|
|
result_code = 0;
|
2022-08-09 09:52:30 +02:00
|
|
|
|
|
|
|
if (!message) {
|
2018-02-26 23:14:32 +01:00
|
|
|
/* Message is unknown */
|
2022-08-09 09:52:30 +02:00
|
|
|
if (search.type & 1) {
|
|
|
|
cw_dbg(DBG_MSG_ERR,
|
|
|
|
"Message type %d [%s] unrecognized, sending response.",
|
|
|
|
search.type, cw_strmsg(search.type),
|
|
|
|
cw_strstate(conn->capwap_state));
|
|
|
|
|
2018-04-07 21:47:35 +02:00
|
|
|
result_code = CAPWAP_RESULT_MSG_UNRECOGNIZED;
|
|
|
|
cw_send_error_response(conn, rawmsg, result_code);
|
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
|
|
|
}
|
2022-08-09 09:52:30 +02:00
|
|
|
cw_dbg(DBG_MSG_ERR,
|
|
|
|
"Message type %d [%s] unrecognized, discarding.",
|
|
|
|
search.type, cw_strmsg(search.type),
|
|
|
|
cw_strstate(conn->capwap_state));
|
2018-02-26 23:14:32 +01:00
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
2018-04-07 21:47:35 +02:00
|
|
|
|
2018-02-26 23:14:32 +01:00
|
|
|
}
|
2015-03-31 08:04:03 +02:00
|
|
|
|
2018-02-28 07:38:02 +01:00
|
|
|
/* Throw away unexpected messages */
|
2018-03-02 08:49:37 +01:00
|
|
|
/* maybe we have to check this too: if (!(message->type & 1))
|
|
|
|
* means unexpected response message
|
|
|
|
* */
|
2018-04-01 10:06:22 +02:00
|
|
|
if (!(message->receiver & conn->role)) {
|
2018-02-28 07:38:02 +01:00
|
|
|
cw_dbg(DBG_MSG_ERR,
|
2018-05-07 10:57:12 +02:00
|
|
|
"Message type %d (%s) unexpected/illegal in %s State, discarding.",
|
2018-02-28 07:38:02 +01:00
|
|
|
search.type, cw_strmsg(search.type),
|
2018-05-07 10:57:12 +02:00
|
|
|
cw_strstate(conn->capwap_state));
|
2018-02-28 07:38:02 +01:00
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if current state is in state of message */
|
2018-05-05 00:36:19 +02:00
|
|
|
ui = message->states;
|
2018-05-07 10:57:12 +02:00
|
|
|
|
2022-08-09 09:52:30 +02:00
|
|
|
for (ui = message->states; ui->state; ui++) {
|
2022-07-28 01:36:16 +02:00
|
|
|
/* printf("Comparing %d and %d\n", conn->capwap_state, ui->state);*/
|
2022-08-09 09:52:30 +02:00
|
|
|
if (ui->state == conn->capwap_state) {
|
2018-02-28 07:38:02 +01:00
|
|
|
break;
|
2018-05-07 10:57:12 +02:00
|
|
|
}
|
2018-02-28 07:38:02 +01:00
|
|
|
}
|
2022-08-09 09:52:30 +02:00
|
|
|
|
|
|
|
if (!ui->state) {
|
2018-02-28 07:38:02 +01:00
|
|
|
/* Message found, but it was in wrong state */
|
|
|
|
cw_dbg(DBG_MSG_ERR,
|
2022-08-09 09:52:30 +02:00
|
|
|
"Message type %d (%s) not allowed in %s State, sending response.",
|
|
|
|
search.type, cw_strmsg(search.type),
|
2018-05-07 10:57:12 +02:00
|
|
|
cw_strstate(conn->capwap_state));
|
2018-02-28 07:38:02 +01:00
|
|
|
result_code = CAPWAP_RESULT_MSG_INVALID_IN_CURRENT_STATE;
|
|
|
|
cw_send_error_response(conn, rawmsg, result_code);
|
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-02 08:49:37 +01:00
|
|
|
|
2015-03-31 08:04:03 +02:00
|
|
|
|
2018-03-25 10:07:39 +02:00
|
|
|
elems_ptr = cw_get_msg_elems_ptr(msg_ptr);
|
|
|
|
|
2016-03-06 21:50:01 +01:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
|
2022-08-09 09:52:30 +02:00
|
|
|
mand_found = mavl_create_conststr();
|
|
|
|
unrecognized = mlist_create(NULL, NULL, sizeof(uint8_t *));
|
|
|
|
|
2018-03-09 07:44:17 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2022-08-09 09:52:30 +02:00
|
|
|
cw_dbg(DBG_MSG_PARSING, "*** Parsing message of type %d - (%s) ***",
|
|
|
|
message->type, message->name);
|
2022-07-28 01:36:16 +02:00
|
|
|
|
2022-08-13 09:47:12 +02:00
|
|
|
params.cfg = cw_cfg_create(); //conn->remote_cfg;
|
|
|
|
params.default_cfg = NULL;
|
|
|
|
params.from = from;
|
|
|
|
params.msgdata = message;
|
|
|
|
params.mand_found = mand_found;
|
|
|
|
params.msgset=conn->msgset;
|
|
|
|
params.conn = conn;
|
2022-07-28 01:36:16 +02:00
|
|
|
|
2022-08-09 09:52:30 +02:00
|
|
|
cw_decode_elements(¶ms,elems_ptr, elems_len);
|
2018-03-15 20:07:17 +01:00
|
|
|
|
2015-04-12 19:19:29 +02:00
|
|
|
/* all message elements are processed, do now after processing
|
|
|
|
by calling the "end" function for the message */
|
2022-08-09 09:52:30 +02:00
|
|
|
|
|
|
|
cw_check_missing_mand(message, mand_found);
|
|
|
|
|
2022-08-10 13:16:09 +02:00
|
|
|
cw_dbg(DBG_MSG_PARSING, " *** End parsing message of type %d (%s) ***",
|
2022-08-09 09:52:30 +02:00
|
|
|
message->type, message->name);
|
|
|
|
|
2022-08-10 13:16:09 +02:00
|
|
|
|
2018-03-15 20:07:17 +01:00
|
|
|
mavl_destroy(mand_found);
|
2022-08-09 09:52:30 +02:00
|
|
|
|
2018-04-19 07:20:45 +02:00
|
|
|
/* cw_dbg_ktv_dump(conn->remote_cfg,DBG_CFG_DMP,
|
2018-03-19 17:26:01 +01:00
|
|
|
" *** Remote CFG dump ***", "CFG:", " *** End of remote CFG dump");
|
2018-04-19 07:20:45 +02:00
|
|
|
*/
|
2018-03-06 03:08:14 +01:00
|
|
|
|
2022-08-13 11:48:49 +02:00
|
|
|
cw_MsgCallbackFun cb_fun = cw_conn_get_msg_cb(conn,message->type);
|
|
|
|
if (cb_fun != NULL){
|
|
|
|
cb_fun(¶ms,elems_ptr, elems_len);
|
|
|
|
}
|
|
|
|
|
2016-04-11 18:02:30 +02:00
|
|
|
|
2022-08-09 09:52:30 +02:00
|
|
|
if (message->postprocess) {
|
2022-08-13 09:47:12 +02:00
|
|
|
// message->postprocess(conn);
|
|
|
|
message->postprocess(¶ms,elems_ptr, elems_len);
|
2016-03-19 12:57:47 +01:00
|
|
|
}
|
2018-03-17 12:32:40 +01:00
|
|
|
|
2016-03-06 21:50:01 +01:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
/* if we've got a request message, we always have to send a response message */
|
2018-03-17 12:32:40 +01:00
|
|
|
if (message->type & 1) {
|
2015-04-10 17:14:55 +02:00
|
|
|
if (result_code > 0) {
|
2015-04-21 08:24:59 +02:00
|
|
|
/* the end method gave us an result code>0, so
|
2015-04-10 17:14:55 +02:00
|
|
|
send an error message */
|
2018-04-04 19:23:40 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
cw_send_error_response(conn, rawmsg, result_code);
|
2016-02-21 09:25:36 +01:00
|
|
|
} else if (result_code == 0) {
|
2022-08-09 09:52:30 +02:00
|
|
|
cw_ktv_set_dword(conn->local_cfg, "result-code",
|
|
|
|
result_code);
|
|
|
|
if (ui->next) {
|
2018-05-07 10:57:12 +02:00
|
|
|
conn->capwap_prevstate = conn->capwap_state;
|
|
|
|
conn->capwap_state = ui->next;
|
2018-05-05 00:36:19 +02:00
|
|
|
}
|
2022-08-09 09:52:30 +02:00
|
|
|
|
2018-03-20 07:09:09 +01:00
|
|
|
/* All is ok, send regular response message */
|
2015-04-10 17:14:55 +02:00
|
|
|
cw_send_response(conn, rawmsg, len);
|
2015-04-12 19:19:29 +02:00
|
|
|
} else {
|
|
|
|
/* the request message is ignored, no response
|
2015-04-21 08:24:59 +02:00
|
|
|
will be sent */
|
2016-02-21 09:25:36 +01:00
|
|
|
errno = EAGAIN;
|
2015-04-10 17:14:55 +02:00
|
|
|
}
|
2015-04-12 16:57:00 +02:00
|
|
|
} else {
|
2015-04-12 19:19:29 +02:00
|
|
|
/*
|
|
|
|
* Whe have got a response message.
|
|
|
|
* Put further actions here, if needed.
|
|
|
|
*/
|
2014-07-11 22:12:11 +02:00
|
|
|
}
|
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
|
|
|
|
return result_code;
|
|
|
|
|
2014-07-11 22:12:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-17 18:39:48 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2022-08-09 22:35:47 +02:00
|
|
|
int process_message(struct cw_Conn *conn, uint8_t * rawmsg, int rawlen,
|
2016-02-27 05:35:25 +01:00
|
|
|
struct sockaddr *from)
|
2014-08-17 18:39:48 +02:00
|
|
|
{
|
2018-03-05 12:23:16 +01:00
|
|
|
char sock_buf[SOCK_ADDR_BUFSIZE];
|
2018-03-25 10:07:39 +02:00
|
|
|
uint8_t seqnum;
|
2022-08-09 09:52:30 +02:00
|
|
|
int s1, s2, sd;
|
2016-03-01 19:10:08 +01:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
uint8_t *msgptr = rawmsg + cw_get_hdr_msg_offset(rawmsg);
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t type = cw_get_msg_type(msgptr);
|
2014-08-17 18:39:48 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
if (!(type & 0x1)) {
|
|
|
|
/* It's a response message, no further examination required. */
|
2015-04-12 23:28:55 +02:00
|
|
|
return process_elements(conn, rawmsg, rawlen, from);
|
2015-04-10 17:14:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* It's a request message, check if seqnum is right and if
|
|
|
|
* we have already sent a response message*/
|
2015-03-23 07:48:27 +01:00
|
|
|
|
2018-03-25 10:07:39 +02:00
|
|
|
seqnum = cw_get_msg_seqnum(msgptr);
|
2014-08-17 18:39:48 +02:00
|
|
|
|
2018-03-25 10:07:39 +02:00
|
|
|
s1 = conn->last_seqnum_received;
|
|
|
|
s2 = seqnum;
|
|
|
|
sd = s2 - s1;
|
2014-08-17 18:39:48 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
if ((sd > 0 && sd < 128) || (sd < 0 && sd < -128) || s1 < 0) {
|
|
|
|
/* seqnum is ok, normal message processing */
|
|
|
|
conn->last_seqnum_received = seqnum;
|
2022-07-30 19:44:57 +02:00
|
|
|
|
2016-02-21 09:25:36 +01:00
|
|
|
return process_elements(conn, rawmsg, rawlen, from);
|
2015-04-10 17:14:55 +02:00
|
|
|
}
|
2014-08-17 18:39:48 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
if (sd != 0) {
|
|
|
|
cw_dbg(DBG_MSG_ERR,
|
|
|
|
"Discarding message from %s, old seqnum, seqnum = %d, last seqnum=%d",
|
2022-08-09 09:52:30 +02:00
|
|
|
sock_addr2str(&conn->addr, sock_buf), s2, s1);
|
2015-04-10 17:14:55 +02:00
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
2014-08-17 18:39:48 +02:00
|
|
|
}
|
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
/* the received request message was retransmittet by our peer,
|
|
|
|
* let's retransmit our response message if we have one*/
|
|
|
|
|
2016-03-01 19:10:08 +01:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
cw_dbg(DBG_MSG_ERR,
|
|
|
|
"Retransmitted request message from %s detected, seqnum=%d, type=%d",
|
2022-08-09 09:52:30 +02:00
|
|
|
sock_addr2str(&conn->addr, sock_buf), s2, type);
|
2015-04-10 17:14:55 +02:00
|
|
|
|
2014-08-17 18:39:48 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
if (cw_get_hdr_msg_type(conn->resp_buffer) - 1 != type) {
|
2022-08-13 10:19:06 +02:00
|
|
|
/* cw_dbg(DBG_MSG_ERR,
|
2015-04-10 17:14:55 +02:00
|
|
|
"No cached response for retransmission, request seqnum=%d,in cache=%d",
|
2022-08-13 10:19:06 +02:00
|
|
|
s2, conn->resp_msg.type);*/
|
2015-04-10 17:14:55 +02:00
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
|
|
|
}
|
2014-08-17 18:39:48 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
cw_dbg(DBG_MSG_ERR, "Retransmitting response message to %s, seqnum=%d",
|
2018-03-05 12:23:16 +01:00
|
|
|
sock_addr2str(&conn->addr, sock_buf), s2);
|
2015-04-12 16:57:00 +02:00
|
|
|
|
2022-08-09 09:52:30 +02:00
|
|
|
/*// XXX untested */
|
2015-04-12 16:57:00 +02:00
|
|
|
conn_send_msg(conn, conn->resp_buffer);
|
2015-04-10 17:14:55 +02:00
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
2014-08-17 18:39:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-12 16:57:00 +02:00
|
|
|
/*
|
|
|
|
* Process an incomming CAPWAP packet, assuming the packet is already decrypted
|
2015-04-28 10:21:48 +02:00
|
|
|
* @param conn conection object
|
2015-04-12 16:57:00 +02:00
|
|
|
* @param packet pointer to packet data
|
|
|
|
* @param len lenght of packet data
|
|
|
|
*/
|
2022-08-09 22:35:47 +02:00
|
|
|
int conn_process_packet2(struct cw_Conn *conn, uint8_t * packet, int len,
|
2022-08-09 09:52:30 +02:00
|
|
|
struct sockaddr *from)
|
2014-07-11 22:12:11 +02:00
|
|
|
{
|
2018-03-05 12:23:16 +01:00
|
|
|
char sock_buf[SOCK_ADDR_BUFSIZE];
|
2018-03-25 10:07:39 +02:00
|
|
|
int preamble;
|
|
|
|
int offs;
|
|
|
|
int payloadlen;
|
2022-08-09 09:52:30 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
if (len < 8) {
|
2014-07-11 22:12:11 +02:00
|
|
|
/* packet too short */
|
2015-04-11 19:00:51 +02:00
|
|
|
cw_dbg(DBG_PKT_ERR,
|
2015-04-12 16:57:00 +02:00
|
|
|
"Discarding packet from %s, packet too short, len=%d, at least 8 expected.",
|
2018-03-05 12:23:16 +01:00
|
|
|
sock_addr2str(&conn->addr, sock_buf), len);
|
2015-04-11 19:00:51 +02:00
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
2014-07-11 22:12:11 +02:00
|
|
|
}
|
|
|
|
|
2018-03-25 10:07:39 +02:00
|
|
|
preamble = cw_get_hdr_preamble(packet);
|
2014-07-11 22:12:11 +02:00
|
|
|
|
2018-02-23 09:12:39 +01:00
|
|
|
if ((preamble & 0xf0) != (CAPWAP_VERSION << 4)) {
|
2014-07-11 22:12:11 +02:00
|
|
|
/* wrong version */
|
2015-04-11 19:00:51 +02:00
|
|
|
cw_dbg(DBG_PKT_ERR,
|
2015-04-12 16:57:00 +02:00
|
|
|
"Discarding packet from %s, wrong version, version=%d, version %d expected.",
|
2022-08-09 09:52:30 +02:00
|
|
|
sock_addr2str(&conn->addr, sock_buf),
|
|
|
|
(preamble & 0xf0) >> 4, CAPWAP_VERSION);
|
2015-04-12 16:57:00 +02:00
|
|
|
errno = EAGAIN;
|
2015-04-11 19:00:51 +02:00
|
|
|
return -1;
|
2014-07-11 22:12:11 +02:00
|
|
|
}
|
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
if (preamble & 0xf) {
|
2015-04-12 16:57:00 +02:00
|
|
|
/* Encrypted data, this shuold never happen here */
|
2015-04-11 19:00:51 +02:00
|
|
|
cw_dbg(DBG_PKT_ERR,
|
2015-04-12 16:57:00 +02:00
|
|
|
"Discarding packet from %s, encrypted data after decryption ...",
|
2022-08-09 09:52:30 +02:00
|
|
|
sock_addr2str(&conn->addr, sock_buf));
|
2015-04-11 19:00:51 +02:00
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
2014-07-11 22:12:11 +02:00
|
|
|
}
|
|
|
|
|
2015-03-23 07:48:27 +01:00
|
|
|
|
2018-03-25 10:07:39 +02:00
|
|
|
offs = cw_get_hdr_msg_offset(packet);
|
2015-03-23 07:48:27 +01:00
|
|
|
|
2014-07-24 23:54:53 +02:00
|
|
|
|
2018-03-25 10:07:39 +02:00
|
|
|
payloadlen = len - offs;
|
2015-04-10 17:14:55 +02:00
|
|
|
if (payloadlen < 0) {
|
2015-04-12 16:57:00 +02:00
|
|
|
/* Eleminate messages with wrong header size */
|
2015-04-11 19:00:51 +02:00
|
|
|
cw_dbg(DBG_PKT_ERR,
|
2015-04-12 16:57:00 +02:00
|
|
|
"Discarding packet from %s, header length (%d) greater than packet len (%d).",
|
2022-08-09 09:52:30 +02:00
|
|
|
sock_addr2str(&conn->addr, sock_buf), offs, len);
|
2015-04-12 16:57:00 +02:00
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
2014-07-11 22:12:11 +02:00
|
|
|
}
|
|
|
|
|
2016-02-21 09:25:36 +01:00
|
|
|
/* Check if Radio MAC is present */
|
2015-04-10 17:14:55 +02:00
|
|
|
if (cw_get_hdr_flag_m(packet)) {
|
|
|
|
|
|
|
|
if (cw_get_hdr_rmac_len(packet) + 8 > offs) {
|
2014-07-11 22:12:11 +02:00
|
|
|
/* wrong rmac size */
|
2015-04-11 19:00:51 +02:00
|
|
|
cw_dbg(DBG_PKT_ERR,
|
2015-04-12 16:57:00 +02:00
|
|
|
"Discarding packet from %s, wrong R-MAC size, size=%d",
|
2022-08-09 09:52:30 +02:00
|
|
|
sock_addr2str(&conn->addr, sock_buf),
|
|
|
|
*(packet + 8));
|
2015-04-12 16:57:00 +02:00
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
2014-07-11 22:12:11 +02:00
|
|
|
}
|
2015-04-12 16:57:00 +02:00
|
|
|
|
2014-07-24 23:54:53 +02:00
|
|
|
}
|
2014-07-11 22:12:11 +02:00
|
|
|
|
|
|
|
|
2015-04-12 16:57:00 +02:00
|
|
|
if (cw_get_hdr_flag_f(packet)) {
|
|
|
|
/* fragmented, add the packet to fragman */
|
2015-04-10 17:14:55 +02:00
|
|
|
uint8_t *f;
|
2018-03-25 10:07:39 +02:00
|
|
|
int rc;
|
2022-08-09 09:52:30 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
f = fragman_add(conn->fragman, packet, offs, payloadlen);
|
2016-02-21 09:25:36 +01:00
|
|
|
if (f == NULL) {
|
|
|
|
errno = EAGAIN;
|
2015-04-19 16:44:20 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2014-08-16 08:24:38 +02:00
|
|
|
|
2015-04-28 10:21:48 +02:00
|
|
|
|
2016-02-21 09:25:36 +01:00
|
|
|
cw_dbg_pkt(DBG_PKT_IN, conn, f + 4, *(uint32_t *) f, from);
|
2018-03-25 10:07:39 +02:00
|
|
|
/*// cw_dbg_msg(DBG_MSG_IN, conn, f + 4, *(uint32_t *) f, from);*/
|
2015-04-28 10:21:48 +02:00
|
|
|
|
2018-03-25 10:07:39 +02:00
|
|
|
/* // XXX: Modify fragman to not throw away CAPWAP headers*/
|
2015-04-28 10:21:48 +02:00
|
|
|
|
2018-03-25 10:07:39 +02:00
|
|
|
rc = conn->process_message(conn, f + 4, *(uint32_t *) f, from);
|
2015-03-31 08:04:03 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
free(f);
|
|
|
|
return rc;
|
2014-07-11 22:12:11 +02:00
|
|
|
}
|
2015-04-11 19:00:51 +02:00
|
|
|
|
2015-04-12 16:57:00 +02:00
|
|
|
/* not fragmented, we have a complete message */
|
2018-03-25 10:07:39 +02:00
|
|
|
/*// cw_dbg_msg(DBG_MSG_IN, conn, packet, len, from);*/
|
2016-02-21 09:25:36 +01:00
|
|
|
return conn->process_message(conn, packet, len, from);
|
2014-07-11 22:12:11 +02:00
|
|
|
}
|
|
|
|
|
2022-08-09 22:35:47 +02:00
|
|
|
int conn_process_packet(struct cw_Conn *conn, uint8_t * packet, int len,
|
2022-08-09 09:52:30 +02:00
|
|
|
struct sockaddr *from)
|
|
|
|
{
|
2016-04-10 16:01:13 +02:00
|
|
|
|
|
|
|
/* show this packet in debug output */
|
|
|
|
cw_dbg_pkt(DBG_PKT_IN, conn, packet, len, from);
|
|
|
|
|
2022-08-09 09:52:30 +02:00
|
|
|
return conn_process_packet2(conn, packet, len, from);
|
2016-04-10 16:01:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-09 22:35:47 +02:00
|
|
|
int conn_process_data_packet(struct cw_Conn *conn, uint8_t * packet, int len,
|
2022-08-09 09:52:30 +02:00
|
|
|
struct sockaddr *from)
|
|
|
|
{
|
2016-04-10 16:01:13 +02:00
|
|
|
|
|
|
|
/* show this packet in debug output */
|
|
|
|
cw_dbg_pkt(DBG_PKT_IN, conn, packet, len, from);
|
|
|
|
|
2022-08-09 09:52:30 +02:00
|
|
|
return conn_process_packet2(conn, packet, len, from);
|
2016-04-10 16:01:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-04-07 07:42:36 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Used as main message loop
|
2015-04-10 17:14:55 +02:00
|
|
|
*/
|
2022-08-09 22:35:47 +02:00
|
|
|
int cw_read_messages(struct cw_Conn *conn)
|
2015-04-07 07:42:36 +02:00
|
|
|
{
|
2015-04-10 17:14:55 +02:00
|
|
|
uint8_t buf[2024];
|
|
|
|
int len = 2024;
|
2015-04-07 07:42:36 +02:00
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
int n = conn->read(conn, buf, len);
|
|
|
|
if (n < 0)
|
2015-04-07 07:42:36 +02:00
|
|
|
return n;
|
|
|
|
|
2015-04-10 17:14:55 +02:00
|
|
|
if (n > 0) {
|
2016-02-21 09:25:36 +01:00
|
|
|
return conn->process_packet(conn, buf, n,
|
|
|
|
(struct sockaddr *) &conn->addr);
|
2015-04-10 17:14:55 +02:00
|
|
|
}
|
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
2015-04-12 23:28:55 +02:00
|
|
|
}
|