Commit da94499c authored by Kai Vehmanen's avatar Kai Vehmanen

Fixes to ICMP error handling. Fixed issues with role conflicts in STUN code.

darcs-hash:20070720212737-77cd4-8fe19842428b5eee01428a4fa0ab4d1f14f83479.gz
parent db89c760
......@@ -78,19 +78,31 @@ int stun_bind_run (int fd,
unsigned delay = stun_bind_timeout (ctx);
memset (ufd, 0, sizeof (ufd));
ufd[0].fd = stun_bind_fd (ctx),
ufd[0].fd = fd = stun_bind_fd (ctx),
ufd[0].events = POLLIN;
poll (ufd, sizeof (ufd) / sizeof (ufd[0]), delay);
val = recv (ufd[0].fd, buf + len, sizeof (buf) - len, MSG_DONTWAIT);
if (val == -1)
if (poll (ufd, sizeof (ufd) / sizeof (ufd[0]), delay) <= 0)
{
val = stun_bind_elapse (ctx);
else
continue;
}
val = recv (fd, buf + len, sizeof (buf) - len, MSG_DONTWAIT);
if (val == -1)
{
#ifdef MSG_ERRQUEUE
/* FIXME: do proper ICMP error handling? */
struct msghdr hdr;
memset (&hdr, 0, sizeof (hdr));
recvmsg (fd, &hdr, MSG_ERRQUEUE);
#endif
val = EAGAIN;
continue;
}
len += val;
val = stun_bind_process (ctx, buf, len, addr, addrlen);
}
}
while (val == EAGAIN);
return val;
......@@ -224,11 +236,11 @@ int stun_bind_process (stun_bind_t *restrict ctx,
const void *restrict buf, size_t len,
struct sockaddr *restrict addr, socklen_t *addrlen)
{
int val;
int val, code;
assert (ctx != NULL);
val = stun_trans_preprocess (&ctx->trans, buf, len);
val = stun_trans_preprocess (&ctx->trans, &code, buf, len);
switch (val)
{
case EAGAIN:
......@@ -236,6 +248,8 @@ int stun_bind_process (stun_bind_t *restrict ctx,
case 0:
break;
default:
if (code == STUN_ROLE_CONFLICT)
val = ECONNRESET;
stun_bind_cancel (ctx);
return val;
}
......@@ -265,14 +279,16 @@ int
stun_bind_keepalive (int fd, const struct sockaddr *restrict srv,
socklen_t srvlen)
{
size_t val;
uint8_t buf[28];
size_t len = sizeof (buf);
int val;
stun_init_indication (buf, STUN_BINDING);
(void)stun_finish (buf, &val);
val = stun_finish (buf, &len);
assert (val == 0);
/* NOTE: hopefully, this is only needed for non-stream sockets */
if (sendto (fd, buf, val, MSG_DONTWAIT, srv, srvlen) == -1)
if (stun_sendto (fd, buf, len, srv, srvlen) == -1)
return errno;
return 0;
}
......
......@@ -142,7 +142,8 @@ int stun_bind_fd (const stun_bind_t *context);
*
* @return 0 on success, an error code otherwise:
* - EAGAIN: ignored invalid message (non-fatal error)
* - ECONNREFUSED: error message from server
* - ECONNRESET: role conflict error from server
* - ECONNREFUSED: any other fatal error message from server
* - EPROTO: unsupported message from server
* - ENOENT: no mapped address in message from server
* - EAFNOSUPPORT: unsupported mapped address family from server
......
......@@ -50,14 +50,21 @@ static int
stun_bind_error (uint8_t *buf, size_t *plen, const uint8_t *req,
stun_error_t code, const char *pass)
{
int val = stun_init_error (buf, *plen, req, code);
size_t len = *plen;
int val;
*plen = 0;
DBG ("STUN Binding Error Reply (buffer size: %u)...\n", (unsigned)len);
val = stun_init_error (buf, len, req, code);
if (val)
return val;
val = stun_finish_short (buf, plen, NULL, pass, NULL, 0);
val = stun_finish_short (buf, &len, NULL, pass, NULL, 0);
if (val)
return val;
*plen = len;
DBG (" Error response (%u) of %u bytes\n", (unsigned)code,
(unsigned)*plen);
return 0;
......@@ -71,11 +78,16 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
{
size_t len = *plen;
int val;
*plen = 0;
#define err( code ) \
stun_bind_error (buf, &len, msg, code, pass); \
*plen = len
#define autherr( code ) \
stun_bind_error (buf, &len, msg, code, NULL); \
*plen = len
*plen = 0;
DBG ("STUN Binding Reply (buffer size = %u)...\n", (unsigned)len);
if (stun_get_class (msg) != STUN_REQUEST)
{
......@@ -102,30 +114,23 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
if (!stun_has_integrity (msg))
{
DBG (" Message Authentication Code missing.\n");
err (STUN_BAD_REQUEST);
autherr (STUN_UNAUTHORIZED);
return EPERM;
}
if (!stun_present (msg, STUN_USERNAME))
{
DBG (" Username missing.\n");
err (STUN_BAD_REQUEST);
autherr (STUN_UNAUTHORIZED);
return EPERM;
}
/* FIXME: verify USERNAME, return 401 if wrong */
/* NOTE: should check in that order:
* - missing realm
* - missing nonce
* - stale nonce
* - unknown user / stale credentials
*/
/* FIXME: verify USERNAME, return STUN_UNAUTHORIZED if wrong */
if (stun_verify_password (msg, pass))
{
DBG (" Integrity check failed.\n");
err (STUN_INTEGRITY_CHECK_FAILURE);
autherr (STUN_INTEGRITY_CHECK_FAILURE);
return EPERM;
}
}
......@@ -151,7 +156,7 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
return EPROTO;
}
stun_init_response (buf, msg);
stun_init_response (buf, len, msg);
val = muxed
? stun_append_xor_addr (buf, len, STUN_XOR_MAPPED_ADDRESS, src, srclen)
: stun_append_addr (buf, len, STUN_MAPPED_ADDRESS, src, srclen);
......@@ -166,9 +171,12 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
goto failure;
*plen = len;
DBG (" All done (response size: %u)\n", (unsigned)len);
return 0;
failure:
assert (*plen == 0);
DBG (" Fatal error formatting Binding Response: %s\n", strerror (val));
return val;
}
#undef err
......@@ -191,6 +199,7 @@ stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
const struct sockaddr *restrict src, socklen_t srclen,
const char *pass, bool *restrict control, uint64_t tie)
{
size_t len = *plen;
uint64_t q;
int val = stun_binding_reply (buf, plen, msg, src, srclen, true, pass);
......@@ -198,8 +207,7 @@ stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
return val;
/* Role conflict handling */
// FIXME: breaks if buf == msg
assert (buf != msg); /* cannot operate in place */
assert (val == 0);
if (!stun_find64 (msg, *control ? STUN_ICE_CONTROLLING
: STUN_ICE_CONTROLLED, &q))
......@@ -217,9 +225,16 @@ stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
{
DBG (" staying \"controll%s\" (sending error)\n",
*control ? "ing" : "ed");
*plen = len;
stun_bind_error (buf, plen, msg, STUN_ROLE_CONFLICT, pass);
}
}
#ifndef NDEBUG
else
if (stun_find64 (msg, *control ? STUN_ICE_CONTROLLED
: STUN_ICE_CONTROLLING, &q))
DBG ("STUN Role not specified by peer!\n");
#endif
return val;
}
......
......@@ -179,7 +179,7 @@ typedef enum
STUN_ROLE_CONFLICT=487, /* ICE-17 */
STUN_SERVER_ERROR=500, /* RFC3489bis-07 */
STUN_SERVER_CAPACITY=507, /* TURN-04 */
STUN_GLOBAL_FAILURE=600 // FIXME
STUN_ERROR_MAX=699
} stun_error_t;
......@@ -441,21 +441,23 @@ void stun_init_request (uint8_t *msg, stun_method_t m);
void stun_init_indication (uint8_t *msg, stun_method_t m);
/**
* Initializes a STUN message buffer with no attributes,
* in response to a given valid STUN request messsage.
* Initializes a STUN message buffer with a SERVER attribute (if there is
* enough room for it), in response to a given valid STUN request messsage.
* STUN method and transaction ID are copied from the request message.
*
* @param ans [OUT] STUN message buffer
* @param msize STUN message buffer size
* @param req STUN message query
*
* ans == req is allowed.
*/
void stun_init_response (uint8_t *ans, const uint8_t *req);
void stun_init_response (uint8_t *ans, size_t msize, const uint8_t *req);
/**
* Initializes a STUN error response message buffer with an ERROR-CODE
* attribute, in response to a given valid STUN request messsage.
* STUN method and transaction ID are copied from the request message.
* and a SERVER attributes, in response to a given valid STUN request
* messsage. STUN method and transaction ID are copied from the request
* message.
*
* @param ans [OUT] STUN message buffer
* @param msize STUN message buffer size
......@@ -612,4 +614,22 @@ static inline bool stun_has_integrity (const uint8_t *msg)
return stun_present (msg, STUN_MESSAGE_INTEGRITY);
}
# ifndef NDEBUG
/**
* This function is for debugging only, which is why it is only defined under
* !NDEBUG. It should really only be used in run-time assertions, as it cannot
* detect all possible errors. stun_validate() should be used instead in real
* code.
*
* @param msg pointer to a potential STUN message
* @return whether the pointer refers to a valid STUN message
*/
static inline bool stun_valid (const uint8_t *msg)
{
size_t length = 20u + stun_length (msg);
return stun_validate (msg, length) == (ssize_t)length;
}
# endif
#endif
......@@ -523,20 +523,19 @@ int stun_find_errno (const uint8_t *restrict msg, int *restrict code)
{
uint16_t alen;
const uint8_t *ptr = stun_find (msg, STUN_ERROR_CODE, &alen);
*code = -1;
uint8_t class, number;
if (ptr == NULL)
return ENOENT;
if (alen < 4)
return EINVAL;
*code = ((ptr[2] & 0x7) * 100) + ptr[3];
/* NOTE: we are a bit laxist here. We should also ignore packets where
* the "Number" (ptr[3]) > 99 */
if ((*code < 100) || (*code > 699))
class = ptr[2] & 0x7;
number = ptr[3];
if ((class < 3) || (class > 6) || (number > 99))
return EINVAL;
*code = (class * 100) + number;
return 0;
}
......@@ -561,6 +560,11 @@ stun_match_answer (const uint8_t *msg, stun_method_t method,
assert (stun_valid (msg));
assert (error != NULL);
if ((stun_get_method (msg) != method) /* wrong request type */
|| !check_cookie (msg) /* response to old-style request */
|| memcmp (msg + 8, id, 12)) /* wrong transaction ID */
return false;
switch (stun_get_class (msg))
{
case STUN_REQUEST:
......@@ -573,18 +577,10 @@ stun_match_answer (const uint8_t *msg, stun_method_t method,
case STUN_ERROR:
if (stun_find_errno (msg, error))
{
assert (*error == -1);
return false; // missing ERROR-CODE?!
}
return false; // missing ERROR-CODE: ignore message
break;
}
if ((stun_get_method (msg) != method) /* wrong request type */
|| !check_cookie (msg) /* response to old-style request */
|| memcmp (msg + 8, id, 12)) /* wrong transaction ID */
return false;
if ((key != NULL) && stun_verify_key (msg, key, keylen))
return false;
......
......@@ -111,15 +111,6 @@ void stun_init_indication (uint8_t *req, stun_method_t m)
}
void stun_init_response (uint8_t *ans, const uint8_t *req)
{
//assert (stun_valid (req));
assert (stun_get_class (req) == STUN_REQUEST);
stun_init (ans, STUN_RESPONSE, stun_get_method (req), stun_id (req));
}
/**
* Reserves room for appending an attribute to an unfinished STUN message.
* @param msg STUN message buffer
......@@ -137,6 +128,7 @@ stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length)
uint8_t *a;
uint16_t mlen = stun_length (msg);
assert (stun_valid (msg));
assert (stun_padding (mlen) == 0);
if (msize > STUN_MAXMSG)
......@@ -216,6 +208,26 @@ stun_append_string (uint8_t *restrict msg, size_t msize,
}
static int stun_append_server (uint8_t *restrict msg, size_t msize)
{
static const char server[] = PACKAGE_STRING;
assert (strlen (server) < 128);
return stun_append_string (msg, msize, STUN_SERVER, server);
}
void stun_init_response (uint8_t *ans, size_t msize, const uint8_t *req)
{
assert (stun_valid (req));
assert (stun_get_class (req) == STUN_REQUEST);
assert (msize >= 20u);
stun_init (ans, STUN_RESPONSE, stun_get_method (req), stun_id (req));
(void)stun_append_server (ans, msize);
}
/**
* @param code host-byte order error code
* @return a static pointer to a nul-terminated error message string.
......@@ -257,15 +269,21 @@ static const char *stun_strerror (stun_error_t code)
{ STUN_SERVER_CAPACITY, "Temporary server congestion" },
{ 0, "" }
};
const char *str = "Unknown error";
unsigned i;
for (i = 0; tab[i].phrase[0]; i++)
{
if (tab[i].code == code)
return tab[i].phrase;
{
str = tab[i].phrase;
break;
}
}
return "Unknown error";
/* Maximum allowed error message length */
assert (strlen (str) < 128);
return str;
}
......@@ -299,9 +317,11 @@ stun_append_error (uint8_t *restrict msg, size_t msize, stun_error_t code)
int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req,
stun_error_t err)
{
//assert (stun_valid (req));
assert (stun_valid (req));
assert (msize >= 20u);
stun_init (ans, STUN_ERROR, stun_get_method (req), stun_id (req));
(void)stun_append_server (ans, msize);
return stun_append_error (ans, msize, err);
}
......@@ -315,7 +335,7 @@ int stun_init_error_unknown (uint8_t *ans, size_t msize, const uint8_t *req)
uint16_t ids[256];
#endif
//assert (stun_valid (req));
assert (stun_valid (req));
assert (stun_get_class (req) == STUN_REQUEST);
counter = stun_find_unknown (req, ids, sizeof (ids) / sizeof (ids[0]));
......@@ -390,13 +410,16 @@ int stun_append_xor_addr (uint8_t *restrict msg, size_t msize,
const struct sockaddr *restrict addr,
socklen_t addrlen)
{
struct sockaddr_storage xor;
int val;
union
{
struct sockaddr addr;
char fill[addrlen];
} xor;
if (addrlen > sizeof (xor))
return ENOBUFS;
assert (sizeof (xor) >= addrlen);
memcpy (&xor, addr, addrlen);
val = stun_xor_address (msg, (struct sockaddr *)&xor, addrlen);
if (val)
return val;
......
......@@ -71,8 +71,8 @@ int stun_trans_init (stun_trans_t *restrict tr, int fd,
tr->key.length = 0;
tr->key.value = NULL;
if (getsockopt (fd, SOL_SOCKET, SO_TYPE, &sotype, &solen))
return errno;
assert (getsockopt (fd, SOL_SOCKET, SO_TYPE, &sotype, &solen) == 0);
(void)getsockopt (fd, SOL_SOCKET, SO_TYPE, &sotype, &solen);
switch (sotype)
{
......@@ -186,14 +186,36 @@ static inline int stun_err_dequeue (int fd)
{
#ifdef MSG_ERRQUEUE
struct msghdr hdr;
int saved_errno = errno, ret;
memset (&hdr, 0, sizeof (hdr));
return recvmsg (fd, &hdr, MSG_ERRQUEUE) == 0;
ret = (recvmsg (fd, &hdr, MSG_ERRQUEUE) >= 0);
errno = saved_errno;
return ret;
#else
return 0;
#endif
}
ssize_t stun_sendto (int fd, const uint8_t *buf, size_t len,
const struct sockaddr *dst, socklen_t dstlen)
{
static const int flags = MSG_DONTWAIT | MSG_NOSIGNAL;
ssize_t val;
do
{
if (dstlen > 0)
val = sendto (fd, buf, len, flags, dst, dstlen);
else
val = send (fd, buf, len, flags);
}
while ((val == -1) && stun_err_dequeue (fd));
return val;
}
/**
* Try to send STUN request/indication
*/
......@@ -203,31 +225,18 @@ stun_trans_send (stun_trans_t *tr)
const uint8_t *data = tr->msg.buf + tr->msg.offset;
size_t len = tr->msg.length - tr->msg.offset;
ssize_t val;
int errval;
do
{
if (tr->sock.dstlen > 0)
val = sendto (tr->sock.fd, data, len, MSG_DONTWAIT | MSG_NOSIGNAL,
val = stun_sendto (tr->sock.fd, data, len,
(struct sockaddr *)&tr->sock.dst, tr->sock.dstlen);
else
val = send (tr->sock.fd, data, len, MSG_DONTWAIT | MSG_NOSIGNAL);
if (val < 0)
return errno;
if (val >= 0)
{
/* Message sent succesfully! */
tr->msg.offset += val;
assert (tr->msg.offset <= tr->msg.length);
if (tr->msg.offset == tr->msg.length)
tr->msg.offset = 0;
tr->msg.offset = 0; // FIXME: temporary hack, may break TCP
return 0;
}
errval = errno;
}
while (stun_err_dequeue (tr->sock.fd));
return errval;
}
......@@ -248,26 +257,27 @@ int stun_trans_tick (stun_trans_t *tr)
}
int stun_trans_preprocess (stun_trans_t *restrict tr,
int stun_trans_preprocess (stun_trans_t *restrict tr, int *pcode,
const void *restrict buf, size_t len)
{
int code;
assert (pcode != NULL);
if (stun_validate (buf, len) <= 0)
return EAGAIN;
DBG ("Received %u-bytes STUN message\n",
(unsigned)stun_validate (buf, len));
/* FIXME: some error messages cannot be authenticated!! */
/* NOTE: currently we ignore unauthenticated messages if the context
* is authenticated, for security reasons. */
if (!stun_match_messages (buf, tr->msg.buf, tr->key.value, tr->key.length,
&code))
pcode))
return EAGAIN;
if (code >= 0)
if (*pcode >= 0)
{
DBG (" STUN error message received (code: %d)\n", code);
return ECONNREFUSED; // FIXME: better error value
DBG (" STUN error message received (code: %d)\n", *pcode);
return ECONNREFUSED;
}
if (stun_has_unknown (buf))
......
......@@ -103,7 +103,7 @@ void stun_trans_deinit (stun_trans_t *restrict tr);
int stun_trans_start (stun_trans_t *restrict tr);
int stun_trans_tick (stun_trans_t *tr);
int stun_trans_preprocess (stun_trans_t *restrict tr,
int stun_trans_preprocess (stun_trans_t *restrict tr, int *code,
const void *restrict buf, size_t len);
/**
......@@ -122,6 +122,9 @@ unsigned stun_trans_timeout (const stun_trans_t *tr);
*/
int stun_trans_fd (const stun_trans_t *tr);
ssize_t stun_sendto (int fd, const uint8_t *buf, size_t len,
const struct sockaddr *dst, socklen_t dstlen);
# ifdef __cplusplus
}
# endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment