diff --git a/include/h.h b/include/h.h index 9070c9e23..977523e3c 100644 --- a/include/h.h +++ b/include/h.h @@ -852,6 +852,7 @@ extern MODVAR void (*rpc_error)(Client *client, json_t *request, JsonRpcError er extern MODVAR void (*rpc_error_fmt)(Client *client, json_t *request, JsonRpcError error_code, FORMAT_STRING(const char *fmt), ...) __attribute__((format(printf,4,5))); extern MODVAR int (*websocket_handle_websocket)(Client *client, WebRequest *web, const char *readbuf2, int length2, int callback(Client *client, char *buf, int len)); extern MODVAR int (*websocket_create_packet)(int opcode, char **buf, int *len); +extern MODVAR int (*websocket_create_packet_ex)(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize); extern MODVAR int (*websocket_create_packet_simple)(int opcode, const char **buf, int *len); /* /Efuncs */ @@ -897,6 +898,7 @@ extern void rpc_error_default_handler(Client *client, json_t *request, JsonRpcEr extern void rpc_error_fmt_default_handler(Client *client, json_t *request, JsonRpcError error_code, const char *fmt, ...); extern int websocket_handle_websocket_default_handler(Client *client, WebRequest *web, const char *readbuf2, int length2, int callback(Client *client, char *buf, int len)); extern int websocket_create_packet_default_handler(int opcode, char **buf, int *len); +extern int websocket_create_packet_ex_default_handler(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize); extern int websocket_create_packet_simple_default_handler(int opcode, const char **buf, int *len); /* End of default handlers for efunctions */ diff --git a/include/modules.h b/include/modules.h index 74ecc678e..4af59ffa6 100644 --- a/include/modules.h +++ b/include/modules.h @@ -2538,6 +2538,7 @@ enum EfunctionType { EFUNC_RPC_ERROR_FMT, EFUNC_WEBSOCKET_HANDLE_WEBSOCKET, EFUNC_WEBSOCKET_CREATE_PACKET, + EFUNC_WEBSOCKET_CREATE_PACKET_EX, EFUNC_WEBSOCKET_CREATE_PACKET_SIMPLE, }; diff --git a/src/api-efunctions.c b/src/api-efunctions.c index 12a766c9a..65fdbab67 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -147,6 +147,7 @@ void (*rpc_error)(Client *client, json_t *request, JsonRpcError error_code, cons void (*rpc_error_fmt)(Client *client, json_t *request, JsonRpcError error_code, const char *fmt, ...); int (*websocket_handle_websocket)(Client *client, WebRequest *web, const char *readbuf2, int length2, int callback(Client *client, char *buf, int len)); int (*websocket_create_packet)(int opcode, char **buf, int *len); +int (*websocket_create_packet_ex)(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize); int (*websocket_create_packet_simple)(int opcode, const char **buf, int *len); Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)()) @@ -430,5 +431,6 @@ void efunctions_init(void) efunc_init_function(EFUNC_RPC_ERROR_FMT, rpc_error_fmt, rpc_error_fmt_default_handler); efunc_init_function(EFUNC_WEBSOCKET_HANDLE_WEBSOCKET, websocket_handle_websocket, websocket_handle_websocket_default_handler); efunc_init_function(EFUNC_WEBSOCKET_CREATE_PACKET, websocket_create_packet, websocket_create_packet_default_handler); + efunc_init_function(EFUNC_WEBSOCKET_CREATE_PACKET_EX, websocket_create_packet_ex, websocket_create_packet_ex_default_handler); efunc_init_function(EFUNC_WEBSOCKET_CREATE_PACKET_SIMPLE, websocket_create_packet_simple, websocket_create_packet_simple_default_handler); } diff --git a/src/misc.c b/src/misc.c index 840c69454..e126590c3 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1421,6 +1421,11 @@ int websocket_create_packet_default_handler(int opcode, char **buf, int *len) return -1; } +int websocket_create_packet_ex_default_handler(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize) +{ + return -1; +} + int websocket_create_packet_simple_default_handler(int opcode, const char **buf, int *len) { return -1; diff --git a/src/modules/rpc/rpc.c b/src/modules/rpc/rpc.c index 1f784ad4a..b75d3e93d 100644 --- a/src/modules/rpc/rpc.c +++ b/src/modules/rpc/rpc.c @@ -431,11 +431,16 @@ void rpc_sendto(Client *client, const char *buf, int len) if (MyConnect(client) && IsRPC(client) && WSU(client) && WSU(client)->handshake_completed) { /* Websocket */ - static char utf8buf[65535]; // TODO: dynamic!!! - char *newbuf = unrl_utf8_make_valid(buf, utf8buf, sizeof(utf8buf), 1); + int utf8bufsize = len*2 + 16; + char *utf8buf = safe_alloc(utf8bufsize); + char *newbuf = unrl_utf8_make_valid(buf, utf8buf, utf8bufsize, 1); int newlen = strlen(newbuf); - websocket_create_packet(WSOP_TEXT, &newbuf, &newlen); + int ws_sendbufsize = newlen + 64 + ((newlen / 1024) * 64); // some random magic + char *ws_sendbuf = safe_alloc(ws_sendbufsize); + websocket_create_packet_ex(WSOP_TEXT, &newbuf, &newlen, ws_sendbuf, ws_sendbufsize); dbuf_put(&client->local->sendQ, newbuf, newlen); + safe_free(ws_sendbuf); + safe_free(utf8buf); } else { /* Unix domain socket or HTTP */ dbuf_put(&client->local->sendQ, buf, len); diff --git a/src/modules/websocket_common.c b/src/modules/websocket_common.c index 1288149c7..c41e7fe3e 100644 --- a/src/modules/websocket_common.c +++ b/src/modules/websocket_common.c @@ -40,6 +40,7 @@ struct HTTPForwardedHeader /* Forward declarations - public functions */ int _websocket_handle_websocket(Client *client, WebRequest *web, const char *readbuf2, int length2, int callback(Client *client, char *buf, int len)); int _websocket_create_packet(int opcode, char **buf, int *len); +int _websocket_create_packet_ex(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize); int _websocket_create_packet_simple(int opcode, const char **buf, int *len); /* Forward declarations - other */ int websocket_handle_packet(Client *client, const char *readbuf, int length, int callback(Client *client, char *buf, int len)); @@ -57,6 +58,7 @@ MOD_TEST() MARK_AS_OFFICIAL_MODULE(modinfo); EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_HANDLE_WEBSOCKET, _websocket_handle_websocket); EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_CREATE_PACKET, _websocket_create_packet); + EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_CREATE_PACKET_EX, _websocket_create_packet_ex); EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_CREATE_PACKET_SIMPLE, _websocket_create_packet_simple); /* Init first, since we manage sockets */ @@ -289,7 +291,7 @@ int websocket_handle_packet_pong(Client *client, const char *buf, int len) return 0; } -/** Create a simple websocket packet that is ready to be send. +/** Create a simple websocket packet that is ready to be sent. * This is the simple version that is used ONLY for WSOP_PONG, * as it does not take \r\n into account. */ @@ -322,15 +324,18 @@ int _websocket_create_packet_simple(int opcode, const char **buf, int *len) } /** Create a websocket packet that is ready to be send. - * This is the more complex version that takes into account - * stripping off \r and \n, and possibly multi line due to - * labeled-response. It is used for WSOP_TEXT and WSOP_BINARY. + * This version takes into account stripping off \r and \n, + * and possibly multi line due to labeled-response. + * It is used for WSOP_TEXT and WSOP_BINARY. * The end result is one or more websocket frames, * all in a single packet *buf with size *len. + * + * This is the version that uses the specified buffer, + * it is used from the JSON-RPC code, + * and indirectly from websocket_create_packet(). */ -int _websocket_create_packet(int opcode, char **buf, int *len) +int _websocket_create_packet_ex(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize) { - static char sendbuf[WEBSOCKET_SEND_BUFFER_SIZE]; char *s = *buf; /* points to start of current line */ char *s2; /* used for searching of end of current line */ char *lastbyte = *buf + *len - 1; /* points to last byte in *buf that can be safely read */ @@ -359,10 +364,12 @@ int _websocket_create_packet(int opcode, char **buf, int *len) if (bytes_to_copy < 126) bytes_single_frame = 2 + bytes_to_copy; - else + else if (bytes_to_copy < 65536) bytes_single_frame = 4 + bytes_to_copy; + else + bytes_single_frame = 10 + bytes_to_copy; - if (bytes_in_sendbuf + bytes_single_frame > sizeof(sendbuf)) + if (bytes_in_sendbuf + bytes_single_frame > sendbufsize) { /* Overflow. This should never happen. */ unreal_log(ULOG_WARNING, "websocket", "BUG_WEBSOCKET_OVERFLOW", NULL, @@ -370,7 +377,7 @@ int _websocket_create_packet(int opcode, char **buf, int *len) "$bytes_in_sendbuf + $bytes_single_frame > $sendbuf_size", log_data_integer("bytes_in_sendbuf", bytes_in_sendbuf), log_data_integer("bytes_single_frame", bytes_single_frame), - log_data_integer("sendbuf_size", sizeof(sendbuf))); + log_data_integer("sendbuf_size", sendbufsize)); return -1; } @@ -382,12 +389,27 @@ int _websocket_create_packet(int opcode, char **buf, int *len) /* Short payload */ o[1] = (char)bytes_to_copy; memcpy(&o[2], s, bytes_to_copy); - } else { + } else + if (bytes_to_copy < 65536) + { /* Long payload */ o[1] = 126; o[2] = (char)((bytes_to_copy >> 8) & 0xFF); o[3] = (char)(bytes_to_copy & 0xFF); memcpy(&o[4], s, bytes_to_copy); + } else { + /* Longest payload */ + // XXX: yeah we don't support sending more than 4GB. + o[1] = 127; + o[2] = 0; + o[3] = 0; + o[4] = 0; + o[5] = 0; + o[6] = (char)((bytes_to_copy >> 24) & 0xFF); + o[7] = (char)((bytes_to_copy >> 16) & 0xFF); + o[8] = (char)((bytes_to_copy >> 8) & 0xFF); + o[9] = (char)(bytes_to_copy & 0xFF); + memcpy(&o[10], s, bytes_to_copy); } /* Advance destination pointer and counter */ @@ -403,6 +425,22 @@ int _websocket_create_packet(int opcode, char **buf, int *len) return 0; } +/** Create a websocket packet that is ready to be send. + * This version takes into account stripping off \r and \n, + * and possibly multi line due to labeled-response. + * It is used for WSOP_TEXT and WSOP_BINARY. + * The end result is one or more websocket frames, + * all in a single packet *buf with size *len. + * + * This is the version that uses a static sendbuf buffer, + * it is used from IRC websockets. + */ +int _websocket_create_packet(int opcode, char **buf, int *len) +{ + static char sendbuf[WEBSOCKET_SEND_BUFFER_SIZE]; + return _websocket_create_packet_ex(opcode, buf, len, sendbuf, sizeof(sendbuf)); +} + /** Create and send a WSOP_PONG frame */ int websocket_send_pong(Client *client, const char *buf, int len) {