Skip to content

Commit

Permalink
Clean up error handling a bit.
Browse files Browse the repository at this point in the history
Adds some error "codes" and tries to standardize on them.
  • Loading branch information
voutilad committed Dec 13, 2023
1 parent ea9ae95 commit b4c48dc
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 49 deletions.
4 changes: 2 additions & 2 deletions client_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ main(int argc, char **argv)
printf("received payload of " SSIZE_T_PARAM " bytes:\n---\n%s\n---\n",
len, out);

assert(0 == dumb_close(&ws));
assert(DWS_OK == dumb_close(&ws));
printf("sent a CLOSE frame!\n");

// Our socket should be closed now
assert(-1 == dumb_recv(&ws, buf, sizeof(buf)));
assert(DWS_ERR_READ == dumb_recv(&ws, buf, sizeof(buf)));
printf("socket looks closed!\n");

return 0;
Expand Down
96 changes: 49 additions & 47 deletions dws.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ choose(unsigned int upper_bound)
*
* Since RFC6455 says we don't care about the random 16-byte value used
* for the key (the server never decodes it), why bother actually writing
* propery base64 encoding when we can just pick 22 valid base64 characters
* a proper base64 encoding when we can just pick 22 valid base64 characters
* to make our key?
*/
static void
Expand Down Expand Up @@ -202,8 +202,8 @@ ws_read(struct websocket *ws, void *buf, size_t buflen)
break;
}
else if (sz == -1) {
// TODO: check some common errno's and return something better.
printf("%s: err %s\n", __func__, strerror(errno));
// TODO: check some common errno's and return
// something better.
return -1;
}
}
Expand Down Expand Up @@ -315,8 +315,6 @@ ws_read_txt(struct websocket *ws, void *buf, size_t buflen)
*
* Will write the entirety of the given buffer. Does not currently use any
* poll like functionality, so will busy poll the socket!
*
* XXX: for now failures to write are fatal :X
*/
static ssize_t
ws_write(struct websocket *ws, const void *buf, size_t buflen)
Expand All @@ -325,7 +323,7 @@ ws_write(struct websocket *ws, const void *buf, size_t buflen)
char *_buf;

if (buflen > INT_MAX)
crap(1, "%s: buflen too large", __func__);
return -1;
if (buflen == 0)
return 0;

Expand Down Expand Up @@ -460,7 +458,7 @@ dumb_frame(uint8_t *frame, const uint8_t *data, size_t len)

// Just a quick safety check: we don't do large payloads
if (len > (1 << 24))
return -1;
return DWS_ERR_TOO_LARGE;

// Pretend we're in Eyes Wide Shut
dumb_mask(mask);
Expand Down Expand Up @@ -490,8 +488,8 @@ dumb_frame(uint8_t *frame, const uint8_t *data, size_t len)
*
* Returns:
* 0 on success,
* -1 if it failed to generate the handshake buffer,
* -2 if it received an invalid handshake response,
* DWS_ERR_HANDSHAKE_BUF if it failed to generate the handshake buffer,
* DWS_ERR_HANDSHAKE_ERR if it received an invalid handshake response,
* fatal error otherwise.
*/
int
Expand All @@ -507,7 +505,7 @@ dumb_handshake(struct websocket *ws, const char *path, const char *proto)
len = snprintf(buf, sizeof(buf), HANDSHAKE_TEMPLATE,
path, ws->host, ws->port, key, proto);
if (len < 1)
return -1;
return DWS_ERR_HANDSHAKE_BUF;

// Send our upgrade request.
sz = ws_write(ws, buf, len);
Expand All @@ -517,14 +515,13 @@ dumb_handshake(struct websocket *ws, const char *path, const char *proto)
memset(buf, 0, sizeof(buf));
len = ws_read_txt(ws, buf, sizeof(buf));
if (len == -1)
return -1;
return DWS_ERR_HANDSHAKE_BUF;

/* XXX: If we gave a crap, we'd validate the returned key per the
* requirements of RFC6455 sec. 4.1, but we don't.
*/
if (memcmp(server_handshake, buf, sizeof(server_handshake) - 1)) {
printf("%s: bad handshake:\n%s\n", __func__, buf);
ret = -2;
ret = DWS_ERR_HANDSHAKE_RES;
}

return ret;
Expand All @@ -543,9 +540,9 @@ dumb_handshake(struct websocket *ws, const char *path, const char *proto)
*
* Returns:
* 0 on success,
* -1 if it failed to create a socket,
* -2 if it failed to resolve host (check h_errno),
* -3 if it failed to connect(2).
* DWS_ERR_CONN_CREATE if it failed to create a socket,
* DWS_ERR_CONN_RESOLVE if it failed to resolve host (check h_errno),
* DWS_ERR_CONN_CONNECT if it failed to connect(2).
*/
int
dumb_connect(struct websocket *ws, const char *host, uint16_t port)
Expand All @@ -564,7 +561,7 @@ dumb_connect(struct websocket *ws, const char *host, uint16_t port)

s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
return -1;
return DWS_ERR_CONN_CREATE;

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
Expand All @@ -575,15 +572,15 @@ dumb_connect(struct websocket *ws, const char *host, uint16_t port)
memset(port_buf, 0, sizeof(port_buf));
snprintf(port_buf, sizeof(port_buf), "%d", port);
if (getaddrinfo(host, port_buf, &hints, &res))
return -2;
return DWS_ERR_CONN_RESOLVE;

// XXX: for now we're lazy and only try the first addrinfo
if (connect(s, res->ai_addr, res->ai_addrlen))
return -3;
return DWS_ERR_CONN_CONNECT;

// Set to non blocking
if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
return -3;
return DWS_ERR_CONN_CONNECT;

// Store some state
ws->port = port;
Expand Down Expand Up @@ -651,8 +648,8 @@ dumb_connect_tls(struct websocket *ws, const char *host, uint16_t port,
*
* Returns:
* the amount of bytes sent,
* -1 on failure to calloc(3) a buffer for the dumb websocket frame,
* or whatever ws_write might return on error (zero or a negative value)
* DWS_ERR_MALLOC on failure to calloc(3) a buffer for the dumb websocket
* frame, or whatever ws_write might return on error (zero or a negative value)
*/
ssize_t
dumb_send(struct websocket *ws, const void *payload, size_t len)
Expand All @@ -663,7 +660,7 @@ dumb_send(struct websocket *ws, const void *payload, size_t len)
// We need payload size + 14 bytes minimum, but pad a little extra
frame = calloc(1, len + 16);
if (frame == NULL)
return -1;
return DWS_ERR_MALLOC;

frame_len = dumb_frame(frame, payload, len);
if (frame_len < 0)
Expand Down Expand Up @@ -691,7 +688,7 @@ dumb_send(struct websocket *ws, const void *payload, size_t len)
*
* Returns:
* the number of bytes received in the payload (not including frame headers),
* -1 on failure to read(2) data, DWS_WANT_POLL or DWS_SHUTDOWN.
* DWS_ERR_READ on failure to read(2) data, DWS_WANT_POLL or DWS_SHUTDOWN.
*/
ssize_t
dumb_recv(struct websocket *ws, void *buf, size_t buflen)
Expand All @@ -702,12 +699,15 @@ dumb_recv(struct websocket *ws, void *buf, size_t buflen)

// Read first 2 bytes to figure out the framing details.
n = ws_read(ws, frame, 2);
if (n < 0)
if (n < 0) {
if (n == -1)
return DWS_ERR_READ;
return n;
}

// Now to validate the frame...
if (!(frame[0] & 0x80)) {
// XXX: We don't currently fragmentation
// XXX: We don't currently support fragmentation
crap(1, "%s: fragmentation unsupported", __func__);
}

Expand Down Expand Up @@ -737,20 +737,20 @@ dumb_recv(struct websocket *ws, void *buf, size_t buflen)
// arrives in network byte order.
n = ws_read_all(ws, frame + 2, 2);
if (n < 2)
return -1;
return DWS_ERR_READ;
payload_len = frame[2] << 8;
payload_len += frame[3];
} else if (payload_len > 126)
crap(1, "%s: unsupported payload size", __func__);

// We can now read the the payload, if there is one.
payload_len = MIN(payload_len, buflen);
payload_len = MIN((size_t)payload_len, buflen);
if (payload_len == 0)
return 0;

n = ws_read_all(ws, buf, (size_t) payload_len);
n = ws_read_all(ws, buf, (size_t)payload_len);
if (n < payload_len)
return -1;
return DWS_ERR_READ;

return payload_len;
}
Expand All @@ -766,9 +766,9 @@ dumb_recv(struct websocket *ws, void *buf, size_t buflen)
*
* Returns:
* 0 on success,
* -1 on failure during write(2),
* -2 on failure to receive(2) the response,
* -3 on the response being invalid (i.e. not a PONG)
* DWS_ERR_WRITE on failure during write(2),
* DWS_ERR_READ on failure to receive(2) the response,
* DWS_ERR_INVALID on the response being invalid (i.e. not a PONG)
*/
int
dumb_ping(struct websocket *ws)
Expand All @@ -784,28 +784,29 @@ dumb_ping(struct websocket *ws)

len = ws_write(ws, frame, (size_t) len);
if (len < 1)
return -1;
return DWS_ERR_WRITE;

memset(frame, 0, sizeof(frame));

// Read first 2 bytes.
len = ws_read_all(ws, frame, 2);
if (len < 0)
return -2;
return DWS_ERR_READ;

// We should have a PONG reply.
if (frame[0] != (0x80 + PONG))
return -3;
return DWS_ERR_INVALID;

payload_len = frame[1] & 0x7F;
if (payload_len >= 126)
crap(1, "dumb_ping: unsupported pong payload size > 125");

// Dump the rest of the data on the floor.
if (payload_len > 0) {
len = ws_read_all(ws, frame + 2, MIN(payload_len, sizeof(frame) - 2));
len = ws_read_all(ws, frame + 2,
MIN((size_t)payload_len, sizeof(frame) - 2));
if (len < 1)
return -3;
return DWS_ERR_INVALID;
}

return 0;
Expand Down Expand Up @@ -851,9 +852,9 @@ ws_shutdown(struct websocket *ws)
*
* Returns:
* 0 on success,
* -1 on failure to send(2) the close frame,
* -2 on failure to read(2) a response,
* -3 on a response being invalid (i.e. not a CLOSE),
* DWS_ERR_WRITE on failure to send(2) the close frame,
* DWS_ERR_READ on failure to read(2) a response,
* DWS_ERR_INVALID on a response being invalid (i.e. not a CLOSE),
*/
int
dumb_close(struct websocket *ws)
Expand All @@ -869,30 +870,31 @@ dumb_close(struct websocket *ws)

len = ws_write(ws, frame, (size_t) len);
if (len < 1)
return -1;
return DWS_ERR_WRITE;

memset(frame, 0, sizeof(frame));

// A valid RFC6455 websocket server MUST send a Close frame in response
// Read first 2 bytes.
len = ws_read_all(ws, frame, 2);
len = ws_read_all(ws, frame, 2);
if (len != 2)
return -2;
return DWS_ERR_READ;

// If we don't have a CLOSE frame...someone screwed up before calling
// dumb_close and there's still unread data!
if (frame[0] != (0x80 + CLOSE))
return -3;
return DWS_ERR_INVALID;

payload_len = frame[1] & 0x7F;
if (payload_len > 126)
crap(1, "dumb_close: unsupported close payload size > 125");

// Dump the rest of the data on the floor.
if (payload_len > 0) {
len = ws_read_all(ws, frame + 2, MIN(payload_len, sizeof(frame) - 2));
len = ws_read_all(ws, frame + 2,
MIN((size_t)payload_len, sizeof(frame) - 2));
if (len < 1)
return -4;
return DWS_ERR_READ;
}

ws_shutdown(ws);
Expand Down
19 changes: 19 additions & 0 deletions dws.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,29 @@ struct websocket {
// TODO: add basic auth details?
};

/*
* Possible non-error responses from dumb_recv() based on the state of the
* socket or the next websocket control message (e.g. PING).
*/
#define DWS_WANT_POLL -2
#define DWS_WANT_PONG -3
#define DWS_SHUTDOWN -4

/*
* Simplistic error code approach using define's.
*/
#define DWS_OK 0
#define DWS_ERR_CONN_CREATE -1
#define DWS_ERR_CONN_RESOLVE -2
#define DWS_ERR_CONN_CONNECT -3
#define DWS_ERR_MALLOC -4
#define DWS_ERR_READ -5
#define DWS_ERR_WRITE -6
#define DWS_ERR_INVALID -7
#define DWS_ERR_HANDSHAKE_BUF -8
#define DWS_ERR_HANDSHAKE_RES -9
#define DWS_ERR_TOO_LARGE -10

int dumb_connect(struct websocket *ws, const char*, uint16_t);
int dumb_connect_tls(struct websocket *ws, const char*, uint16_t, int);
int dumb_handshake(struct websocket *s, const char*, const char*);
Expand Down

0 comments on commit b4c48dc

Please sign in to comment.