websockify/utils/wswrapper.c

835 lines
25 KiB
C
Raw Normal View History

/*
* wswrap/wswrapper: Add WebSockets support to any service.
* Copyright 2010 Joel Martin
* Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
*
* wswrapper.so is meant to be LD preloaded. Use wswrap to run a program using
* wswrapper.so.
*/
/*
* Limitations:
* - multi-threaded programs may not work
* - programs using ppoll or epoll will not work correctly
*/
#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU 1 // Pull in RTLD_NEXT
#include <dlfcn.h>
#include <poll.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <resolv.h> /* base64 encode/decode */
#include "md5.h"
#include "wswrapper.h"
/*
* If WSWRAP_PORT environment variable is set then listen to the bind fd that
* matches WSWRAP_PORT, otherwise listen to the first socket fd that bind is
* called on.
*/
int _WS_listen_fd = -1;
int _WS_nfds = 0;
int _WS_fds[WS_MAX_FDS];
_WS_connection *_WS_connections[65536];
/*
* WebSocket handshake routines
*/
/* For WebSockets v76, use key1, key2 and key3 to generate md5 hash */
int _WS_gen_md5(char *key1, char *key2, char *key3, char *target) {
unsigned int i, spaces1 = 0, spaces2 = 0;
unsigned long num1 = 0, num2 = 0;
unsigned char buf[17];
/* Parse number 1 from key 1 */
for (i=0; i < strlen(key1); i++) {
if (key1[i] == ' ') {
spaces1 += 1;
}
if ((key1[i] >= 48) && (key1[i] <= 57)) {
num1 = num1 * 10 + (key1[i] - 48);
}
}
num1 = num1 / spaces1;
/* Parse number 2 from key 2 */
for (i=0; i < strlen(key2); i++) {
if (key2[i] == ' ') {
spaces2 += 1;
}
if ((key2[i] >= 48) && (key2[i] <= 57)) {
num2 = num2 * 10 + (key2[i] - 48);
}
}
num2 = num2 / spaces2;
/* Pack it big-endian as the first 8 bytes */
buf[0] = (num1 & 0xff000000) >> 24;
buf[1] = (num1 & 0xff0000) >> 16;
buf[2] = (num1 & 0xff00) >> 8;
buf[3] = num1 & 0xff;
buf[4] = (num2 & 0xff000000) >> 24;
buf[5] = (num2 & 0xff0000) >> 16;
buf[6] = (num2 & 0xff00) >> 8;
buf[7] = num2 & 0xff;
/* Add key 3 as the last 8 bytes */
strncpy(buf+8, key3, 8);
buf[16] = '\0';
/* md5 hash all 16 bytes to generate 16 byte target */
md5_buffer(buf, 16, target);
target[16] = '\0';
return 1;
}
/* Do v75 and v76 handshake on WebSocket connection */
int _WS_handshake(int sockfd)
{
int sz = 0, len, idx;
int ret = -1, save_errno = EPROTO;
char *last, *start, *end;
long flags;
char handshake[4096], response[4096],
path[1024], prefix[5] = "", scheme[10] = "ws", host[1024],
origin[1024], key1[100], key2[100], key3[9], chksum[17];
static void * (*rfunc)(), * (*wfunc)();
if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv");
if (!wfunc) wfunc = (void *(*)()) dlsym(RTLD_NEXT, "send");
DEBUG("_WS_handshake starting\n");
/* Disable NONBLOCK if set */
flags = fcntl(sockfd, F_GETFL, 0);
if (flags & O_NONBLOCK) {
fcntl(sockfd, F_SETFL, flags^O_NONBLOCK);
}
while (1) {
len = (int) rfunc(sockfd, handshake+sz, 4095, 0);
if (len < 1) {
ret = len;
save_errno = errno;
break;
}
sz += len;
handshake[sz] = '\x00';
if (sz < 4) {
// Not enough yet
continue;
}
if (strstr(handshake, "GET ") != handshake) {
MSG("Got non-WebSockets client connection\n");
break;
}
last = strstr(handshake, "\r\n\r\n");
if (! last) {
continue;
}
if (! strstr(handshake, "Upgrade: WebSocket\r\n")) {
MSG("Invalid WebSockets handshake\n");
break;
}
/* Now parse out the data elements */
start = handshake+4;
end = strstr(start, " HTTP/1.1");
if (!end) { break; }
snprintf(path, end-start+1, "%s", start);
start = strstr(handshake, "\r\nHost: ");
if (!start) { break; }
start += 8;
end = strstr(start, "\r\n");
snprintf(host, end-start+1, "%s", start);
start = strstr(handshake, "\r\nOrigin: ");
if (!start) { break; }
start += 10;
end = strstr(start, "\r\n");
snprintf(origin, end-start+1, "%s", start);
start = strstr(handshake, "\r\n\r\n") + 4;
if (strlen(start) == 8) {
sprintf(prefix, "Sec-");
snprintf(key3, 8+1, "%s", start);
start = strstr(handshake, "\r\nSec-WebSocket-Key1: ");
if (!start) { break; }
start += 22;
end = strstr(start, "\r\n");
snprintf(key1, end-start+1, "%s", start);
start = strstr(handshake, "\r\nSec-WebSocket-Key2: ");
if (!start) { break; }
start += 22;
end = strstr(start, "\r\n");
snprintf(key2, end-start+1, "%s", start);
_WS_gen_md5(key1, key2, key3, chksum);
//DEBUG("Got handshake (v76): %s\n", handshake);
MSG("New WebSockets client (v76)\n");
} else {
sprintf(prefix, "");
sprintf(key1, "");
sprintf(key2, "");
sprintf(key3, "");
sprintf(chksum, "");
//DEBUG("Got handshake (v75): %s\n", handshake);
MSG("New WebSockets client (v75)\n");
}
sprintf(response, _WS_response, prefix, origin, prefix, scheme,
host, path, prefix, chksum);
//DEBUG("Handshake response: %s\n", response);
wfunc(sockfd, response, strlen(response), 0);
save_errno = 0;
ret = 0;
break;
}
/* Re-enable NONBLOCK if it was set */
if (flags & O_NONBLOCK) {
fcntl(sockfd, F_SETFL, flags);
}
errno = save_errno;
return ret;
}
/*
* Check WebSockets socket and return a positive value if there is enough data
* to base64 decode (a 4 byte chunk). If nonblock is not set then it will
* block until there is enough data (or until an error occurs).
*/
ssize_t _WS_ready(int sockfd, int nonblock)
{
_WS_connection *ws = _WS_connections[sockfd];
char buf[6];
int count, len, flags, i;
static void * (*rfunc)();
if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv");
TRACE(">> _WS_ready(%d, %d)\n", sockfd, nonblock);
count = 4 + ws->newframe;
flags = MSG_PEEK;
if (nonblock) {
flags |= MSG_DONTWAIT;
}
while (1) {
len = (int) rfunc(sockfd, buf, count, flags);
if (len < 1) {
TRACE("<< _WS_ready(%d, %d) len < 1, errno: %d\n",
sockfd, nonblock, errno);
return len;
}
if (len >= 2 && buf[0] == '\x00' && buf[1] == '\xff') {
/* Strip emtpy frame */
DEBUG("_WS_ready(%d, %d), strip empty\n", sockfd, nonblock);
len = (int) rfunc(sockfd, buf, 2, 0);
if (len < 2) {
MSG("Failed to strip empty frame headers\n");
TRACE("<< _WS_ready: failed to strip empty frame headers\n");
return len;
} else if (len == 2 && nonblock) {
errno = EAGAIN;
TRACE("<< _WS_ready(%d, %d), len == 2, EAGAIN\n",
sockfd, nonblock);
return -1;
}
continue;
}
if (len < count) {
if (nonblock) {
errno = EAGAIN;
TRACE("<< _WS_ready(%d, %d), len < count, EAGAIN\n",
sockfd, nonblock);
return -1;
} else {
fprintf(stderr, "_WS_ready(%d, %d), loop: len %d, buf:",
sockfd, nonblock, len, (unsigned char) buf[0]);
for (i = 0; i < len; i++) {
fprintf(stderr, "%d", (unsigned char) buf[i]);
}
fprintf(stderr, "\n");
continue;
}
}
TRACE("<< _WS_ready(%d, %d) len: %d\n", sockfd, nonblock, len);
return len;
}
}
/*
* WebSockets recv/read interposer routine
*/
ssize_t _WS_recv(int recvf, int sockfd, const void *buf,
size_t len, int flags)
{
_WS_connection *ws = _WS_connections[sockfd];
int rawcount, deccount, left, striplen, decodelen, ready;
ssize_t retlen, rawlen;
int sockflags;
int i;
char *fstart, *fend, *cstart;
static void * (*rfunc)(), * (*rfunc2)();
if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv");
if (!rfunc2) rfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "read");
if (! ws) {
// Not our file descriptor, just pass through
if (recvf) {
return (ssize_t) rfunc(sockfd, buf, len, flags);
} else {
return (ssize_t) rfunc2(sockfd, buf, len);
}
}
TRACE(">> _WS_recv(%d)\n", sockfd);
if (len == 0) {
TRACE("<< _WS_recv(%d) len == 0\n", sockfd);
return 0;
}
sockflags = fcntl(sockfd, F_GETFL, 0);
if (sockflags & O_NONBLOCK) {
TRACE("_WS_recv(%d, _, %d) with O_NONBLOCK\n", sockfd, len);
} else {
TRACE("_WS_recv(%d, _, %d) without O_NONBLOCK\n", sockfd, len);
}
left = len;
retlen = 0;
/* first copy in carry-over bytes from previous recv/read */
if (ws->rcarry_cnt) {
if (ws->rcarry_cnt == 1) {
DEBUG("Using carry byte: %u (", ws->rcarry[0]);
} else if (ws->rcarry_cnt == 2) {
DEBUG("Using carry bytes: %u,%u (", ws->rcarry[0],
ws->rcarry[1]);
} else {
RET_ERROR(EIO, "Too many carry-over bytes\n");
}
if (len <= ws->rcarry_cnt) {
DEBUG("final)\n");
memcpy((char *) buf, ws->rcarry, len);
ws->rcarry_cnt -= len;
return len;
} else {
DEBUG("prepending)\n");
memcpy((char *) buf, ws->rcarry, ws->rcarry_cnt);
retlen += ws->rcarry_cnt;
left -= ws->rcarry_cnt;
ws->rcarry_cnt = 0;
}
}
/* Determine the number of base64 encoded bytes needed */
rawcount = (left * 4) / 3 + 3;
rawcount -= rawcount%4;
if (rawcount > WS_BUFSIZE - 1) {
RET_ERROR(ENOMEM, "recv of %d bytes is larger than buffer\n", rawcount);
}
ready = _WS_ready(sockfd, 0);
if (ready < 1) {
if (retlen) {
/* We had some carry over, don't error until next call */
errno = 0;
} else {
retlen = ready;
}
TRACE("<< _WS_recv(%d, _, %d) retlen %d\n", sockfd, len, retlen);
return retlen;
}
/* We have enough data to return something */
/* Peek at everything available */
rawlen = (ssize_t) rfunc(sockfd, ws->rbuf, WS_BUFSIZE-1,
flags | MSG_PEEK);
if (rawlen <= 0) {
RET_ERROR(EPROTO, "Socket was ready but then had failure");
}
fstart = ws->rbuf;
fstart[rawlen] = '\x00';
if (ws->newframe) {
if (fstart[0] != '\x00') {
RET_ERROR(EPROTO, "Missing frame start\n");
}
fstart++;
rawlen--;
ws->newframe = 0;
}
fend = memchr(fstart, '\xff', rawlen);
if (fend) {
ws->newframe = 1;
if ((fend - fstart) % 4) {
RET_ERROR(EPROTO, "Frame length is not multiple of 4\n");
}
} else {
fend = fstart + rawlen - (rawlen % 4);
if (fend - fstart < 4) {
RET_ERROR(EPROTO, "Frame too short\n");
}
}
/* Determine amount to consume */
if (rawcount < fend - fstart) {
ws->newframe = 0;
deccount = rawcount;
} else {
deccount = fend - fstart;
}
/* Now consume what was processed */
if (flags & MSG_PEEK) {
DEBUG("_WS_recv(%d, _, %d) MSG_PEEK, not consuming\n", sockfd, len);
} else {
rfunc(sockfd, ws->rbuf, fstart - ws->rbuf + deccount + ws->newframe, flags);
}
fstart[deccount] = '\x00'; // base64 terminator
/* Do base64 decode into the return buffer */
decodelen = b64_pton(fstart, (char *) buf + retlen, deccount);
if (decodelen <= 0) {
RET_ERROR(EPROTO, "Base64 decode error\n");
}
/* Calculate return length and carry-over */
if (decodelen <= left) {
retlen += decodelen;
} else {
retlen += left;
if (! (flags & MSG_PEEK)) {
/* Add anything left over to the carry-over */
ws->rcarry_cnt = decodelen - left;
if (ws->rcarry_cnt > 2) {
RET_ERROR(EPROTO, "Got too much base64 data\n");
}
memcpy(ws->rcarry, buf + retlen, ws->rcarry_cnt);
if (ws->rcarry_cnt == 1) {
DEBUG("Saving carry byte: %u\n", ws->rcarry[0]);
} else if (ws->rcarry_cnt == 2) {
DEBUG("Saving carry bytes: %u,%u\n", ws->rcarry[0],
ws->rcarry[1]);
} else {
RET_ERRO(EPROTO, "Too many carry bytes!\n");
}
}
}
((char *) buf)[retlen] = '\x00';
TRACE("<< _WS_recv(%d) retlen %d\n", sockfd, retlen);
return retlen;
}
/*
* WebSockets send and write interposer routine
*/
ssize_t _WS_send(int sendf, int sockfd, const void *buf,
size_t len, int flags)
{
_WS_connection *ws = _WS_connections[sockfd];
int rawlen, enclen, rlen, over, left, clen, retlen, dbufsize;
int sockflags;
char * target;
int i;
static void * (*sfunc)(), * (*sfunc2)();
if (!sfunc) sfunc = (void *(*)()) dlsym(RTLD_NEXT, "send");
if (!sfunc2) sfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "write");
if (! ws) {
// Not our file descriptor, just pass through
if (sendf) {
return (ssize_t) sfunc(sockfd, buf, len, flags);
} else {
return (ssize_t) sfunc2(sockfd, buf, len);
}
}
TRACE(">> _WS_send(%d, _, %d)\n", sockfd, len);
sockflags = fcntl(sockfd, F_GETFL, 0);
dbufsize = (WS_BUFSIZE * 3)/4 - 2;
if (len > dbufsize) {
RET_ERROR(ENOMEM, "send of %d bytes is larger than send buffer\n", len);
}
/* base64 encode and add frame markers */
rawlen = 0;
ws->sbuf[rawlen++] = '\x00';
enclen = b64_ntop(buf, len, ws->sbuf+rawlen, WS_BUFSIZE-rawlen);
if (enclen < 0) {
RET_ERROR(EPROTO, "Base64 encoding error\n");
}
rawlen += enclen;
ws->sbuf[rawlen++] = '\xff';
rlen = (int) sfunc(sockfd, ws->sbuf, rawlen, flags);
if (rlen <= 0) {
TRACE("<< _WS_send(%d, _, %d) send failed, returning\n", sockfd, len);
return rlen;
} else if (rlen < rawlen) {
/* Spin until we can send a whole base64 chunck and frame end */
over = (rlen - 1) % 4;
left = (4 - over) % 4 + 1; // left to send
DEBUG("_WS_send: rlen: %d (over: %d, left: %d), rawlen: %d\n",
rlen, over, left, rawlen);
rlen += left;
ws->sbuf[rlen-1] = '\xff';
i = 0;
do {
i++;
clen = (int) sfunc(sockfd, ws->sbuf + rlen - left, left, flags);
if (clen > 0) {
left -= clen;
} else if (clen == 0) {
MSG("_WS_send: got clen %d\n", clen);
} else if (!(sockflags & O_NONBLOCK)) {
MSG("<< _WS_send: clen %d\n", clen);
return clen;
}
if (i > 1000000) {
RET_ERROR(EIO, "Could not send final part of frame\n");
}
} while (left > 0);
//DEBUG("_WS_send: spins until finished %d\n", i);
DEBUG("_WS_send: spins until finished %d\n", i);
}
/*
* Report back the number of original characters sent,
* not the raw number sent
*/
/* Adjust for framing */
retlen = rlen - 2;
/* Adjust for base64 padding */
if (ws->sbuf[rlen-1] == '=') { retlen --; }
if (ws->sbuf[rlen-2] == '=') { retlen --; }
/* Scale return value for base64 encoding size */
retlen = (retlen*3)/4;
TRACE(">> _WS_send(%d, _, %d) retlen %d\n", sockfd, len, retlen);
return (ssize_t) retlen;
}
int _WS_select(int mode, int nfds, fd_set *readfds,
fd_set *writefds, fd_set *exceptfds,
void *timeptr, const sigset_t *sigmask)
{
_WS_connection *ws;
fd_set carryfds, savefds;
struct timeval savetv;
struct timespec savets;
int carrycnt = 0;
int ret, i, ready, fd;
static void * (*func)(), * (*func2)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "select");
if (!func2) func2 = (void *(*)()) dlsym(RTLD_NEXT, "pselect");
if ((_WS_listen_fd == -1) || (_WS_nfds == 0)) {
if (mode == 0) {
ret = (int) func(nfds, readfds, writefds, exceptfds,
(struct timeval *)timeptr);
} else if (mode == 1) {
ret = (int) func2(nfds, readfds, writefds, exceptfds,
(struct timespec *)timeptr, sigmask);
}
return ret;
}
TRACE(">> _WS_select(%d, %d, _, _, _, _) called\n", mode, nfds);
memcpy(&savetv, timeptr, sizeof(savetv));
memcpy(&savets, timeptr, sizeof(savets));
/* If we have carry-over return it right away */
FD_ZERO(&carryfds);
if (readfds) {
memcpy(&savefds, readfds, sizeof(savefds));
for (i = 0; i < _WS_nfds; i++) {
fd = _WS_fds[i];
ws = _WS_connections[fd];
if ((ws->rcarry_cnt) && (FD_ISSET(fd, readfds))) {
FD_SET(fd, &carryfds);
carrycnt++;
}
}
}
if (carrycnt) {
if (writefds) {
FD_ZERO(writefds);
}
if (exceptfds) {
FD_ZERO(writefds);
}
memcpy(readfds, &carryfds, sizeof(carryfds));
TRACE("<< _WS_select(%d, %d, _, _, _, _) carrycnt %d\n",
mode, nfds, carrycnt);
return carrycnt;
}
do {
if (mode == 0) {
ret = (int) func(nfds, readfds, writefds, exceptfds,
(struct timeval *)timeptr);
} else if (mode == 1) {
ret = (int) func2(nfds, readfds, writefds, exceptfds,
(struct timespec *)timeptr, sigmask);
}
if (! readfds) {
break;
}
if (ret <= 0) {
break;
}
for (i = 0; i < _WS_nfds; i++) {
fd = _WS_fds[i];
ws = _WS_connections[fd];
if (FD_ISSET(fd, readfds)) {
ready = _WS_ready(fd, 1);
if (ready == 0) {
/* 0 means EOF which is also a ready condition */
DEBUG("_WS_select: detected %d is closed\n", fd);
} else if (ready < 0) {
DEBUG("_WS_select: FD_CLR(%d,readfds) - not enough to decode\n", fd);
FD_CLR(fd, readfds);
ret--;
}
}
}
errno = 0; /* errno could be set by _WS_ready */
/*
* If all the ready readfds were WebSockets, but none of
* them were really ready (empty frames) then repeat.
*/
if (ret == 0) {
/* Restore original values*/
/* TODO: fix timeptr for repeat */
memcpy(timeptr, &savetv, sizeof(savetv));
memcpy(timeptr, &savets, sizeof(savets));
memcpy(readfds, &savefds, sizeof(savefds));
}
} while (ret == 0);
TRACE("<< _WS_select(%d, %d, _, _, _, _) ret %d, errno %d\n",
mode, nfds, ret, errno);
return ret;
}
/*
* Overload (LD_PRELOAD) standard library network routines
*/
/*
int socket(int domain, int type, int protocol)
{
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "socket");
DEBUG("socket(_, %d, _) called\n", type);
return (int) func(domain, type, protocol);
}
*/
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
static void * (*func)();
struct sockaddr_in * addr_in = (struct sockaddr_in *)addr;
char * WSWRAP_PORT, * end;
int ret, envport, bindport = htons(addr_in->sin_port);
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "bind");
TRACE(">> bind(%d, _, %d) called\n", sockfd, addrlen);
ret = (int) func(sockfd, addr, addrlen);
if (addr_in->sin_family != AF_INET) {
// TODO: handle IPv6
TRACE("<< bind, ignoring non-IPv4 socket\n");
return ret;
}
WSWRAP_PORT = getenv("WSWRAP_PORT");
if ((! WSWRAP_PORT) || (*WSWRAP_PORT == '\0')) {
// TODO: interpose on all sockets when WSWRAP_PORT not set
TRACE("<< bind, not interposing: WSWRAP_PORT is not set\n");
return ret;
}
envport = strtol(WSWRAP_PORT, &end, 10);
if ((envport == 0) || (*end != '\0')) {
TRACE("<< bind, not interposing: WSWRAP_PORT is not a number\n");
return ret;
}
if (envport != bindport) {
TRACE("<< bind, not interposing on port: %d (fd %d)\n", bindport, sockfd);
return ret;
}
_WS_listen_fd = sockfd;
TRACE("<< bind, interposing on port: %d (fd %d)\n", envport, sockfd);
return ret;
}
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
{
int fd, ret, envfd;
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "accept");
TRACE("<< accept(%d, _, _) called\n", sockfd);
fd = (int) func(sockfd, addr, addrlen);
if (_WS_listen_fd == -1) {
TRACE("<< accept: not interposing\n");
return fd;
}
if (_WS_listen_fd != sockfd) {
TRACE("<< accept: not interposing on fd %d\n", sockfd);
return fd;
}
if (_WS_connections[fd]) {
RET_ERROR(EINVAL, "already interposing on fd %d\n", fd);
} else {
/* It's a port we're interposing on so allocate memory for it */
if (_WS_nfds >= WS_MAX_FDS) {
RET_ERROR(ENOMEM, "Too many interposer fds\n");
}
if (! (_WS_connections[fd] = malloc(sizeof(_WS_connection)))) {
RET_ERROR(ENOMEM, "Could not allocate interposer memory\n");
}
_WS_connections[fd]->rcarry_cnt = 0;
_WS_connections[fd]->rcarry[0] = '\0';
_WS_connections[fd]->newframe = 1;
/* Add to search list for select/pselect */
_WS_fds[_WS_nfds] = fd;
_WS_nfds++;
ret = _WS_handshake(fd);
if (ret < 0) {
free(_WS_connections[fd]);
_WS_connections[fd] = NULL;
errno = EPROTO;
TRACE("<< accept(%d, _, _): ret %d\n", sockfd, ret);
return ret;
}
MSG("interposing on fd %d (allocated memory)\n", fd);
}
return fd;
}
int close(int fd)
{
int i;
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "close");
if (_WS_connections[fd]) {
TRACE(">> close(%d)\n", fd);
free(_WS_connections[fd]);
_WS_connections[fd] = NULL;
/* Remove from the search list for select/pselect */
for (i = 0; i < _WS_nfds; i++) {
if (_WS_fds[i] == fd) {
break;
}
}
if (_WS_nfds - i - 1 > 0) {
memmove(_WS_fds + i, _WS_fds + i + 1, _WS_nfds - i - 1);
}
_WS_nfds--;
MSG("finished interposing on fd %d (freed memory)\n", fd);
TRACE("<< close(%d)\n", fd);
}
return (int) func(fd);
}
ssize_t read(int fd, void *buf, size_t count)
{
//TRACE("read(%d, _, %d) called\n", fd, count);
return (ssize_t) _WS_recv(0, fd, buf, count, 0);
}
ssize_t write(int fd, const void *buf, size_t count)
{
//TRACE("write(%d, _, %d) called\n", fd, count);
return (ssize_t) _WS_send(0, fd, buf, count, 0);
}
ssize_t recv(int sockfd, void *buf, size_t len, int flags)
{
//TRACE("recv(%d, _, %d, %d) called\n", sockfd, len, flags);
return (ssize_t) _WS_recv(1, sockfd, buf, len, flags);
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
{
//TRACE("send(%d, _, %d, %d) called\n", sockfd, len, flags);
return (ssize_t) _WS_send(1, sockfd, buf, len, flags);
}
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout)
{
//TRACE("select(%d, _, _, _, _) called\n", nfds);
return _WS_select(0, nfds, readfds, writefds, exceptfds,
(void *) timeout, NULL);
}
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout,
const sigset_t *sigmask)
{
TRACE("pselect(%d, _, _, _, _, _) called\n", nfds);
return _WS_select(1, nfds, readfds, writefds, exceptfds,
(void *) timeout, sigmask);
}
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
//TRACE("poll(_, %d, _) called\n", nfds);
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "poll");
return (int) func(fds, nfds, timeout);
}