Merge branch 'oranjuice-zmq' into development
This commit is contained in:
commit
3daece9439
|
@ -146,6 +146,8 @@ endif()
|
|||
# set(BSDI TRUE)
|
||||
|
||||
include_directories(src contrib/epee/include external "${CMAKE_BINARY_DIR}/version")
|
||||
include_directories(src/common/rapidjson/include/)
|
||||
include_directories(src/ipc/include)
|
||||
|
||||
if(APPLE)
|
||||
include_directories(SYSTEM /usr/include/malloc)
|
||||
|
|
|
@ -98,4 +98,8 @@ else()
|
|||
endif()
|
||||
endif()
|
||||
|
||||
set(NET_SKELETON_SRCS net_skeleton/net_skeleton.c)
|
||||
add_library(net_skeleton ${NET_SKELETON_SRCS})
|
||||
set(NET_SKELETON_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/libnet_skeleton.a" PARENT_SCOPE)
|
||||
|
||||
add_subdirectory(db_drivers)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
http://github.com/robmadole/jig-plugins@whitespace
|
||||
http://github.com/robmadole/jig-plugins@woops
|
|
@ -0,0 +1,15 @@
|
|||
Copyright (c) 2014 Cesanta Software Limited
|
||||
All rights reserved
|
||||
|
||||
This software is dual-licensed: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation. For the terms of this
|
||||
license, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
You are free to use this software under the terms of the GNU General
|
||||
Public License, 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.
|
||||
|
||||
Alternatively, you can license this software under a commercial
|
||||
license, as set out in <http://cesanta.com/>.
|
|
@ -0,0 +1,458 @@
|
|||
= Networking library for C/C++
|
||||
|
||||
:buildstatus-uri: https://www.codeship.io/projects/72674aa0-1cbd-0132-0050-4a361eed21f8
|
||||
:buildstatus-badge: https://www.codeship.io/projects/72674aa0-1cbd-0132-0050-4a361eed21f8/status?branch=master
|
||||
|
||||
ifdef::env-github[]
|
||||
image:{buildstatus-badge}[Build Status,link={buildstatus-uri}]
|
||||
endif::[]
|
||||
|
||||
image::https://drone.io/github.com/cesanta/net_skeleton/status.png[Build Status,link=https://drone.io/github.com/cesanta/net_skeleton/latest]
|
||||
|
||||
|
||||
Net Skeleton is a multi-protocol networking library written in C.
|
||||
It provides easy to use event-driven interface that allows to implement
|
||||
network protocols or scalable network applications with little effort.
|
||||
Net Skeleton releives developers from the burden of network programming
|
||||
complexity and let them concentrate on the logic, saving time and money.
|
||||
|
||||
Net Skeleton has built-in support for several protocols, like
|
||||
HTTP and Websocket, and is ideal for embedded environments. Net Skeleton
|
||||
has been designed as an open source platform for connecting devices and
|
||||
bringing them online.
|
||||
|
||||
== Features
|
||||
|
||||
* Cross-platform: works on Linux/UNIX, QNX, eCos, Windows, Android, iPhone, etc
|
||||
* Single-threaded, asynchronous, non-blocking core with simple event-based API
|
||||
* Builtin protocols:
|
||||
** plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way)
|
||||
** HTTP client, HTTP server
|
||||
** Websocket client, Websocket server
|
||||
** JSON-RPC client, JSON-RPC server
|
||||
* Tiny static and run-time footprint
|
||||
* Source code is both ISO C and ISO C++ compliant
|
||||
* Extensively tested and production-ready, trusted by many blue chip businesses
|
||||
|
||||
== Concept
|
||||
|
||||
Net Skeleton is a non-blocking, asyncronous event manager described by
|
||||
`struct ns_mgr` structure. That structure holds active connections.
|
||||
Connections could be either *listening*, *client* or *accepted*.
|
||||
Client connections are created by
|
||||
`ns_connect()` call. Listening connections are created by `ns_bind()` call.
|
||||
Accepted connections are those that incoming on a listening connection.
|
||||
Each connection is described by `struct ns_connection` structure, which has
|
||||
a number of fields like socket, event handler function, send/receive buffer,
|
||||
flags, et cetera.
|
||||
|
||||
`ns_mgr_poll()` should be called in an infinite event loop.
|
||||
`ns_mgr_poll()` iterates over all sockets, accepts new connections,
|
||||
sends and receives data, closes connections, and calls an event handler
|
||||
function for each of those events.
|
||||
|
||||
Each connection has send and receive buffer, `struct ns_connection::send_iobuf`
|
||||
and `struct ns_connection::recv_iobuf` respectively. When data arrives,
|
||||
Net Skeleton appends received data to the `recv_iobuf` and
|
||||
triggers `NS_RECV` event. User may send data back (`ns_send()` or
|
||||
`ns_printf()`), which appends data to the `send_iobuf`. When Net Skeleton
|
||||
successfully writes data to the socket, it discards it from `send_iobuf` and
|
||||
sends `NS_SEND` event. When connection is closed, `NS_CLOSE` event is sent.
|
||||
|
||||
image::http://cesanta.com/images/net_skeleton/iobuf.png[]
|
||||
|
||||
== Using Net Skeleton
|
||||
|
||||
1. Define an event handler function.
|
||||
2. Initialize mgr by calling `ns_mgr_init()`.
|
||||
3. Create *listening connections* with `ns_bind()` and/or *client connections*
|
||||
with `ns_connect()`. Note that many connections can be created within a
|
||||
single manager. Connections can be created at any time, including within
|
||||
an event handler function.
|
||||
4. Call `ns_mgr_poll()` in a loop.
|
||||
|
||||
[source,c]
|
||||
----
|
||||
#include "net_skeleton.h"
|
||||
|
||||
// This event handler implements TCP echo server
|
||||
static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) { // 1
|
||||
struct iobuf *io = &nc->recv_iobuf;
|
||||
|
||||
switch (ev) {
|
||||
case NS_RECV:
|
||||
ns_send(nc, io->buf, io->len); // Echo received data back
|
||||
iobuf_remove(io, io->len); // Discard data from recv buffer
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct ns_mgr mgr;
|
||||
ns_mgr_init(&mgr, NULL); // 2
|
||||
ns_bind(&mgr, "1234", ev_handler, NULL); // 3
|
||||
|
||||
// 4 - an event loop
|
||||
for (;;) {
|
||||
ns_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
|
||||
ns_mgr_free(&mgr);
|
||||
return 0;
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
Net Skeleton accepts incoming connections, reads and writes data, and
|
||||
calls specified event handler for each connection when appropriate. An
|
||||
event handler should examine received data, set connection flags if needed,
|
||||
and send data back to the client by `ns_send()` or `ns_printf()`. Here is a
|
||||
typical event flow for the accepted connection:
|
||||
`NS_ACCEPT` -> `NS_RECV` -> .... -> `NS_CLOSE`. Below is a complete list
|
||||
of events triggered by Net Skeleton:
|
||||
|
||||
NS_ACCEPT:: sent when new server connection is accepted by a
|
||||
listening connection. `void *ev_data` is `union socket_address`
|
||||
of the remote peer.
|
||||
NS_CONNECT:: sent when a new client connection created by `ns_connect()` either
|
||||
failed or succeeded. `void *ev_data` is `int *success`. If `success` is 0
|
||||
then connection has been established, otherwise it contains error code. Example
|
||||
code to check connection status:
|
||||
|
||||
[source,c]
|
||||
----
|
||||
static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
int connect_status;
|
||||
|
||||
switch (ev) {
|
||||
case NS_CONNECT:
|
||||
connect_status = * (int *) ev_data;
|
||||
if (connect_status == 0) {
|
||||
/* Success */
|
||||
} else {
|
||||
/* Error */
|
||||
printf("connect() error: %s\n", strerror(connect_status));
|
||||
}
|
||||
break;
|
||||
...
|
||||
----
|
||||
|
||||
NS_RECV:: New data is received and appended to the end of `recv_iobuf`.
|
||||
`void *ev_data` is `int *num_received_bytes`.
|
||||
|
||||
WARNING: Net Skeleton uses `realloc()` to expand receive buffer.
|
||||
It is user's responsibility to discard processed
|
||||
data from the beginning of receive buffer, note the `iobuf_remove()`
|
||||
call in the example above.
|
||||
|
||||
NS_SEND:: Net Skeleton has written data to the remote peer and discarded
|
||||
written data from the `send_iobuf`. `void *ev_data` is `int *num_sent_bytes`
|
||||
|
||||
NS_POLL:: Sent to all connections on each invocation of `ns_server_poll()`
|
||||
|
||||
An event handler can set `struct ns_connection::flags` attribute to control
|
||||
the behavior of the connection. Below is a list of connection flags:
|
||||
|
||||
* `NSF_FINISHED_SENDING_DATA` tells Net Skeleton that all data has been
|
||||
appended to the `send_iobuf`. As soon as Net Skeleton sends it to the
|
||||
socket, the connection will be closed.
|
||||
* `NSF_BUFFER_BUT_DONT_SEND` tells Net Skeleton to append data to the
|
||||
`send_iobuf` but hold on sending it, because the data will be modified
|
||||
later and then will be sent by clearing `NSF_BUFFER_BUT_DONT_SEND` flag.
|
||||
* `NSF_SSL_HANDSHAKE_DONE` SSL only, set when SSL handshake is done
|
||||
* `NSF_CONNECTING` set when connection is in connecting state after
|
||||
`ns_connect()` call but connect did not finish yet
|
||||
* `NSF_CLOSE_IMMEDIATELY` tells Net Skeleton to close the connection
|
||||
immediately, usually after some error
|
||||
* `NSF_LISTENING` set for all listening connections
|
||||
* `NSF_UDP` set if connection is UDP
|
||||
* `NSF_IS_WEBSOCKET` set by Net Skeleton if connection is a Websocket connection
|
||||
* `NSF_WEBSOCKET_NO_DEFRAG` should be set by a user if user wants to switch
|
||||
off automatic frame defragmentation
|
||||
* `NSF_USER_1`, `NSF_USER_2`, `NSF_USER_3`, `NSF_USER_4` could be
|
||||
used by a developer to store application-specific state
|
||||
|
||||
== Plain TCP/UDP/SSL API
|
||||
|
||||
CAUTION: Net skeleton manager instance is single threaded. It does not protect
|
||||
it's data structures by mutexes, therefore all functions that are dealing
|
||||
with particular event manager should be called from the same thread,
|
||||
with exception of `mg_broadcast()` function. It is fine to have different
|
||||
event managers handled by different threads.
|
||||
|
||||
=== Structures
|
||||
|
||||
- `struct ns_connection` Describes a connection between two peers
|
||||
- `struct ns_mgr` Container for a bunch of connections
|
||||
- `struct iobuf` Describes piece of data
|
||||
|
||||
=== Functions for net skeleton manager
|
||||
|
||||
void ns_mgr_init(struct ns_mgr *, void *user_data)::
|
||||
Initializes net skeleton manager.
|
||||
|
||||
void ns_mgr_free(struct ns_mgr *)::
|
||||
|
||||
De-initializes skeleton manager, closes and deallocates all active connections.
|
||||
|
||||
time_t ns_mgr_poll(struct ns_mgr *, int milliseconds)::
|
||||
|
||||
This function performs the actual IO, and must be called in a loop
|
||||
(an event loop). Returns number current timestamp.
|
||||
|
||||
void ns_broadcast(struct ns_mgr *, ns_event_handler_t cb, void *msg, size_t len)::
|
||||
|
||||
Must be called from a different thread. Passes a message of a given length to
|
||||
all connections. Skeleton manager has a socketpair, `struct ns_mgr::ctl`,
|
||||
where `ns_broadcast()` pushes the message.
|
||||
`ns_mgr_poll()` wakes up, reads a message from the socket pair, and calls
|
||||
specified callback for each connection. Thus the callback function executes
|
||||
in event manager thread. Note that `ns_broadcast()` is the only function
|
||||
that can be, and must be, called from a different thread.
|
||||
|
||||
void ns_next(struct ns_mgr *, struct ns_connection *)::
|
||||
|
||||
Iterates over all active connections. Returns next connection from the list
|
||||
of active connections, or `NULL` if there is no more connections. Below
|
||||
is the iteration idiom:
|
||||
[source,c]
|
||||
----
|
||||
for (c = ns_next(srv, NULL); c != NULL; c = ns_next(srv, c)) {
|
||||
// Do something with connection `c`
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
=== Functions for adding new connections
|
||||
|
||||
struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t sock, ns_event_handler_t ev_handler)::
|
||||
|
||||
Create a connection, associate it with the given socket and event handler, and
|
||||
add to the manager.
|
||||
|
||||
struct ns_connection *ns_connect(struct ns_mgr *server, const char *addr, ns_event_handler_t ev_handler)::
|
||||
|
||||
Connect to a remote host. If successful, `NS_CONNECT` event will be delivered
|
||||
to the new connection. `addr` format is the same as for the `ns_bind()` call,
|
||||
just an IP address becomes mandatory: `[PROTO://]HOST:PORT`
|
||||
`PROTO` could be `tcp://` or `udp://`. If `HOST` is not an IP
|
||||
address, Net Skeleton will resolve it - beware that standard blocking resolver
|
||||
will be used. It is a good practice to pre-resolve hosts beforehands and
|
||||
use only IP addresses to avoid blockin an IO thread.
|
||||
Returns: new client connection, or `NULL` on error.
|
||||
|
||||
struct ns_connection *ns_bind(struct ns_mgr *, const char *addr, ns_event_handler_t ev_handler)::
|
||||
|
||||
Start listening on the given port. `addr` could be a port number,
|
||||
e.g. `"3128"`, or IP address with a port number, e.g. `"127.0.0.1:3128"`.
|
||||
Also, a protocol prefix could be specified, valid prefixes are `tcp://` or
|
||||
`udp://`.
|
||||
|
||||
Note that for UDP listening connections, only `NS_RECV` and `NS_CLOSE`
|
||||
are triggered.
|
||||
|
||||
If IP address is specified, Net Skeleton binds to a specific interface only.
|
||||
Also, port could be `"0"`, in which case a random non-occupied port number
|
||||
will be chosen. Return value: a listening connection on success, or
|
||||
`NULL` on error.
|
||||
|
||||
const char *ns_set_ssl(struct ns_connection *nc, const char *cert, const char *ca_cert)::
|
||||
Enable SSL for a given connection. Connection must be TCP. For listening
|
||||
connection, `cert` is a path to a server certificate, and is mandatory.
|
||||
`ca_cert` if non-NULL, specifies CA certificate for client authentication,
|
||||
enables two-way SSL. For client connections, both `cert` and `ca_cert` are
|
||||
optional and can be set to NULL. All certificates
|
||||
must be in PEM format. PEM file for server certificate should contain
|
||||
both certificate and the private key concatenated together.
|
||||
Returns: NULL if there is no error, or error string if there was error.
|
||||
|
||||
Snippet below shows how to generate self-signed SSL certificate using OpenSSL:
|
||||
[source,sh]
|
||||
----
|
||||
openssl req -x509 -nodes -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365
|
||||
cat cert.pem key.pem > my_ssl_cert.pem
|
||||
----
|
||||
|
||||
=== Functions for sending data
|
||||
|
||||
int ns_send(struct ns_connection *, const void *buf, int len)::
|
||||
int ns_printf(struct ns_connection *, const char *fmt, ...)::
|
||||
int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap)::
|
||||
|
||||
These functions are for sending un-formatted and formatted data to the
|
||||
connection. Number of written bytes is returned. Note that these sending
|
||||
functions do not actually push data to the sockets, they just append data
|
||||
to the output buffer. The exception is UDP connections. For UDP, data is
|
||||
sent immediately, and returned value indicates an actual number of bytes
|
||||
sent to the socket.
|
||||
|
||||
=== Utility functions
|
||||
|
||||
void *ns_start_thread(void *(*thread_function)(void *), void *param)::
|
||||
Starts a new thread
|
||||
|
||||
int ns_socketpair2(sock_t [2], int proto)::
|
||||
Create a socket pair. `proto` can be either `SOCK_STREAM` or `SOCK_DGRAM`.
|
||||
Return 0 on failure, 1 on success.
|
||||
|
||||
void ns_set_close_on_exec(sock_t)::
|
||||
Set close-on-exec bit for a given socket.
|
||||
|
||||
void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags)::
|
||||
Converts socket's local or remote address into string. `flags` parameter
|
||||
is a bit mask that controls the behavior. If bit 2 is set (`flags & 4`) then
|
||||
the remote address is stringified, otherwise local address is stringified.
|
||||
If bit 0 is set, then IP
|
||||
address is printed. If bit 1 is set, then port number is printed. If both
|
||||
port number and IP address are printed, they are separated by `:`.
|
||||
|
||||
int ns_hexdump(const void *buf, int len, char *dst, int dst_len)::
|
||||
Takes a memory buffer `buf` of length `len` and creates a hex dump of that
|
||||
buffer in `dst`.
|
||||
|
||||
int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len)::
|
||||
Converts domain name into IP address. This is a blocking call. Returns 1
|
||||
on success, 0 on failure.
|
||||
|
||||
int ns_stat(const char *path, ns_stat_t *st)::
|
||||
Perform a 64-bit `stat()` call against given file. `path` should be
|
||||
UTF8 encoded. Return value is the same as for `stat()` syscall.
|
||||
|
||||
FILE *ns_fopen(const char *path, const char *mode)::
|
||||
Open given file and return a file stream. `path` and `mode` should be
|
||||
UTF8 encoded. Return value is the same as for `fopen()` call.
|
||||
|
||||
int ns_open(const char *path, int flag, int mode)::
|
||||
Open given file and return file descriptor. `path` should be UTF8 encoded.
|
||||
Return value is the same as for `open()` syscall.
|
||||
|
||||
=== HTTP/Websocket API
|
||||
|
||||
void ns_set_protocol_http_websocket(struct ns_connection *)::
|
||||
Attach built-in HTTP event handler to the given connection. User-defined
|
||||
event handler will receive following extra events:
|
||||
- NS_HTTP_REQUEST: HTTP request has arrived. Parsed HTTP request is passed as
|
||||
`struct http_message` through the handler's `void *ev_data` pointer.
|
||||
- NS_HTTP_REPLY: HTTP reply has arrived. Parsed HTTP reply is passed as
|
||||
`struct http_message` through the handler's `void *ev_data` pointer.
|
||||
- NS_WEBSOCKET_HANDSHAKE_REQUEST: server has received websocket handshake
|
||||
request. `ev_data` contains parsed HTTP request.
|
||||
- NS_WEBSOCKET_HANDSHAKE_DONE: server has completed Websocket handshake.
|
||||
`ev_data` is `NULL`.
|
||||
- NS_WEBSOCKET_FRAME: new websocket frame has arrived. `ev_data` is
|
||||
`struct websocket_message *`
|
||||
|
||||
void ns_send_websocket_handshake(struct ns_connection *nc, const char *uri, const char *extra_headers)::
|
||||
Sends websocket handshake to the server. `nc` must be a valid connection, connected to a server, `uri` is an URI on the server, `extra_headers` is
|
||||
extra HTTP headers to send or `NULL`.
|
||||
This function is to be used by websocket client.
|
||||
|
||||
void ns_send_websocket_frame(struct ns_connection *nc, int op, const void *data, size_t data_len)::
|
||||
Send websocket frame to the remote end. `op` specifies frame's type , one of:
|
||||
- WEBSOCKET_OP_CONTINUE
|
||||
- WEBSOCKET_OP_TEXT
|
||||
- WEBSOCKET_OP_BINARY
|
||||
- WEBSOCKET_OP_CLOSE
|
||||
- WEBSOCKET_OP_PING
|
||||
- WEBSOCKET_OP_PONG
|
||||
`data` and `data_len` contain frame data.
|
||||
|
||||
void ns_send_websocket_framev(struct ns_connection *nc, int op, const struct ns_str *frames, int num_frames);
|
||||
Send multiple websocket frames. Like `ns_send_websocket_frame()`, but sends
|
||||
multiple frames at once.
|
||||
|
||||
void ns_printf_websocket_frame(struct ns_connection *nc, int op, const char *fmt, ...)::
|
||||
Send websocket frame to the remote end. Like `ns_send_websocket_frame()`,
|
||||
but allows to create formatted message with `printf()`-like semantics.
|
||||
|
||||
struct ns_str *ns_get_http_header(struct http_message *, const char *)::
|
||||
Returns HTTP header if it is present in the HTTP message, or `NULL`.
|
||||
|
||||
int ns_parse_http(const char *s, int n, struct http_message *req)::
|
||||
Parses HTTP message. Return number of bytes parsed. If HTTP message is
|
||||
incomplete, `0` is returned. On parse error, negative number is returned.
|
||||
|
||||
int ns_get_http_var(const struct ns_str *buf, const char *name, char *dst, size_t dst_len)::
|
||||
Fetch an HTTP form variable `name` from a `buf` into a buffer specified by
|
||||
`dst`, `dst_len`. Destination is always zero-terminated. Return length
|
||||
of a fetched variable. If not found, 0 is returned. `buf` must be
|
||||
valid url-encoded buffer. If destination is too small, `-1` is returned.
|
||||
|
||||
void ns_serve_http(struct ns_connection *nc, struct http_message *request, struct ns_serve_http_opts options)::
|
||||
Serve given HTTP request according to the `options`.
|
||||
Example code snippet:
|
||||
|
||||
[source,c]
|
||||
.web_server.c
|
||||
----
|
||||
static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
struct ns_serve_http_opts opts = { .document_root = "/var/www" }; // C99 syntax
|
||||
|
||||
switch (ev) {
|
||||
case NS_HTTP_REQUEST:
|
||||
ns_serve_http(nc, hm, opts);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
=== JSON-RPC API
|
||||
|
||||
JSON-RPC module is implemented using
|
||||
https://github.com/cesanta/frozen[Frozen JSON parser/generator]. So for
|
||||
JSON-related functionality refer to Frozen documentation.
|
||||
|
||||
int ns_rpc_parse_reply(const char *buf, int len, struct json_token *toks, int max_toks, struct ns_rpc_reply *reply, struct ns_rpc_error *error)::
|
||||
Parse JSON-RPC reply contained in `buf`, `len` into JSON tokens array
|
||||
`toks`, `max_toks`. If buffer contains valid reply, `reply` structure is
|
||||
populated. The result of RPC call is located in `reply.result`. On error,
|
||||
`error` structure is populated. Returns: the result of calling
|
||||
`parse_json(buf, len, toks, max_toks)`.
|
||||
|
||||
int ns_rpc_create_request(char *buf, int len, const char *method, const char *id, const char *params_fmt, ...)::
|
||||
Create JSON-RPC request in a given buffer. Return length of the request, which
|
||||
can be larger then `len` that indicates an overflow.
|
||||
|
||||
int ns_rpc_create_reply(char *buf, int len, const struct ns_rpc_request *req, const char *result_fmt, ...)::
|
||||
Create JSON-RPC reply in a given buffer. Return length of the reply, which
|
||||
can be larger then `len` that indicates an overflow.
|
||||
|
||||
int ns_rpc_create_error(char *, int, struct ns_rpc_request *req, int, const char *, const char *, ...)::
|
||||
Create JSON-RPC error in a given buffer. Return length of the error, which
|
||||
can be larger then `len` that indicates an overflow.
|
||||
|
||||
int ns_rpc_create_std_error(char *, int, struct ns_rpc_request *, int code)::
|
||||
Create JSON-RPC error in a given buffer. Return length of the error, which
|
||||
can be larger then `len` that indicates an overflow. `code` could be one of:
|
||||
`JSON_RPC_PARSE_ERROR`, `JSON_RPC_INVALID_REQUEST_ERROR`,
|
||||
`JSON_RPC_METHOD_NOT_FOUND_ERROR`, `JSON_RPC_INVALID_PARAMS_ERROR`,
|
||||
`JSON_RPC_INTERNAL_ERROR`, `JSON_RPC_SERVER_ERROR`.
|
||||
|
||||
int ns_rpc_dispatch(const char *buf, int, char *dst, int dst_len, const char **methods, ns_rpc_handler_t *handlers)::
|
||||
Parses JSON-RPC request contained in `buf`, `len`. Then, dispatches the request
|
||||
to the correct handler method. Valid method names should be specified in NULL
|
||||
terminated array `methods`, and corresponding handlers in `handlers`.
|
||||
Result is put in `dst`, `dst_len`. Return: length of the result, which
|
||||
can be larger then `dst_len` that indicates an overflow.
|
||||
|
||||
== Examples
|
||||
|
||||
* link:examples/echo_server[examples/echo_server]:
|
||||
a simple TCP echo server. It accepts incoming connections
|
||||
and echoes back any data that it receives
|
||||
* link:examples/publish_subscribe[examples/publish_subscribe]:
|
||||
implements pubsub pattern for TCP communication
|
||||
* link:examples/netcat[examples/netcat]:
|
||||
an implementation of Netcat utility with traffic hexdump and SSL support
|
||||
|
||||
== License
|
||||
|
||||
Net Skeleton is released under
|
||||
http://www.gnu.org/licenses/old-licenses/gpl-2.0.html[GNU GPL v.2].
|
||||
Businesses have an option to get non-restrictive, royalty-free commercial
|
||||
license and professional support from http://cesanta.com[Cesanta Software].
|
|
@ -0,0 +1,15 @@
|
|||
# Copyright (c) 2014 Cesanta Software
|
||||
# All rights reserved
|
||||
|
||||
SUBDIRS = $(sort $(dir $(wildcard */)))
|
||||
X = $(SUBDIRS)
|
||||
|
||||
.PHONY: $(SUBDIRS)
|
||||
|
||||
all: $(SUBDIRS)
|
||||
|
||||
$(SUBDIRS):
|
||||
@$(MAKE) -C $@
|
||||
|
||||
clean:
|
||||
for d in $(SUBDIRS) ; do $(MAKE) -C $$d clean ; done
|
|
@ -0,0 +1,14 @@
|
|||
PROG = json_rpc_server
|
||||
SOURCES = $(PROG).c ../../net_skeleton.c
|
||||
CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA)
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||
|
||||
$(PROG).exe: $(SOURCES)
|
||||
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||
|
||||
clean:
|
||||
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* To test this server, do
|
||||
* $ curl -d '{"id":1,method:"sum",params:[22,33]}' 127.0.0.1:8000
|
||||
*/
|
||||
|
||||
#include "net_skeleton.h"
|
||||
|
||||
static const char *s_http_port = "8000";
|
||||
|
||||
static int rpc_sum(char *buf, int len, struct ns_rpc_request *req) {
|
||||
double sum = 0;
|
||||
int i;
|
||||
|
||||
if (req->params[0].type != JSON_TYPE_ARRAY) {
|
||||
return ns_rpc_create_std_error(buf, len, req,
|
||||
JSON_RPC_INVALID_PARAMS_ERROR);
|
||||
}
|
||||
|
||||
for (i = 0; i < req->params[0].num_desc; i++) {
|
||||
if (req->params[i + 1].type != JSON_TYPE_NUMBER) {
|
||||
return ns_rpc_create_std_error(buf, len, req,
|
||||
JSON_RPC_INVALID_PARAMS_ERROR);
|
||||
}
|
||||
sum += strtod(req->params[i + 1].ptr, NULL);
|
||||
}
|
||||
return ns_rpc_create_reply(buf, len, req, "f", sum);
|
||||
}
|
||||
|
||||
static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
static const char *methods[] = { "sum", NULL };
|
||||
static ns_rpc_handler_t handlers[] = { rpc_sum, NULL };
|
||||
char buf[100];
|
||||
|
||||
switch (ev) {
|
||||
case NS_HTTP_REQUEST:
|
||||
ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf),
|
||||
methods, handlers);
|
||||
ns_printf(nc, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
|
||||
"Content-Type: application/json\r\n\r\n%s",
|
||||
(int) strlen(buf), buf);
|
||||
nc->flags |= NSF_FINISHED_SENDING_DATA;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
nc = ns_bind(&mgr, s_http_port, ev_handler);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
|
||||
printf("Starting JSON-RPC server on port %s\n", s_http_port);
|
||||
for (;;) {
|
||||
ns_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
PROG = nc
|
||||
SOURCES = $(PROG).c ../../net_skeleton.c
|
||||
CFLAGS = -W -Wall -I../.. -pthread -DNS_ENABLE_SSL -lssl $(CFLAGS_EXTRA)
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||
|
||||
$(PROG).exe: $(SOURCES)
|
||||
cl $(SOURCES) /I../.. /DNS_ENABLE_SSL /MD /Fe$@
|
||||
|
||||
clean:
|
||||
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
Binary file not shown.
|
@ -0,0 +1,140 @@
|
|||
// Copyright (c) 2014 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// This software is dual-licensed: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation. For the terms of this
|
||||
// license, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// You are free to use this software under the terms of the GNU General
|
||||
// Public License, 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.
|
||||
//
|
||||
// Alternatively, you can license this software under a commercial
|
||||
// license, as set out in <http://cesanta.com/>.
|
||||
//
|
||||
// $Date: 2014-09-28 05:04:41 UTC $
|
||||
|
||||
// This file implements "netcat" utility with SSL and traffic hexdump.
|
||||
|
||||
#include "net_skeleton.h"
|
||||
|
||||
static int s_received_signal = 0;
|
||||
|
||||
static void signal_handler(int sig_num) {
|
||||
signal(sig_num, signal_handler);
|
||||
s_received_signal = sig_num;
|
||||
}
|
||||
|
||||
static void show_usage_and_exit(const char *prog_name) {
|
||||
fprintf(stderr, "%s\n", "Copyright (c) 2014 CESANTA SOFTWARE");
|
||||
fprintf(stderr, "%s\n", "Usage:");
|
||||
fprintf(stderr, " %s\n [-d debug_file] [-l] [tcp|ssl]://[ip:]port[:cert][:ca_cert]",
|
||||
prog_name);
|
||||
fprintf(stderr, "%s\n", "Examples:");
|
||||
fprintf(stderr, " %s\n -d hexdump.txt ssl://google.com:443", prog_name);
|
||||
fprintf(stderr, " %s\n -l ssl://443:ssl_cert.pem", prog_name);
|
||||
fprintf(stderr, " %s\n -l tcp://8080", prog_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void on_stdin_read(struct ns_connection *nc, int ev, void *p) {
|
||||
int ch = * (int *) p;
|
||||
|
||||
(void) ev;
|
||||
|
||||
if (ch < 0) {
|
||||
// EOF is received from stdin. Schedule the connection to close
|
||||
nc->flags |= NSF_FINISHED_SENDING_DATA;
|
||||
if (nc->send_iobuf.len <= 0) {
|
||||
nc->flags |= NSF_CLOSE_IMMEDIATELY;
|
||||
}
|
||||
} else {
|
||||
// A character is received from stdin. Send it to the connection.
|
||||
unsigned char c = (unsigned char) ch;
|
||||
ns_send(nc, &c, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void *stdio_thread_func(void *param) {
|
||||
struct ns_mgr *mgr = (struct ns_mgr *) param;
|
||||
int ch;
|
||||
|
||||
// Read stdin until EOF character by character, sending them to the mgr
|
||||
while ((ch = getchar()) != EOF) {
|
||||
ns_broadcast(mgr, on_stdin_read, &ch, sizeof(ch));
|
||||
}
|
||||
s_received_signal = 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ev_handler(struct ns_connection *nc, int ev, void *p) {
|
||||
(void) p;
|
||||
|
||||
switch (ev) {
|
||||
case NS_ACCEPT:
|
||||
case NS_CONNECT:
|
||||
ns_start_thread(stdio_thread_func, nc->mgr);
|
||||
break;
|
||||
|
||||
case NS_CLOSE:
|
||||
s_received_signal = 1;
|
||||
break;
|
||||
|
||||
case NS_RECV:
|
||||
fwrite(nc->recv_iobuf.buf, 1, nc->recv_iobuf.len, stdout);
|
||||
iobuf_remove(&nc->recv_iobuf, nc->recv_iobuf.len);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct ns_mgr mgr;
|
||||
int i, is_listening = 0;
|
||||
const char *address = NULL;
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
|
||||
// Parse command line options
|
||||
for (i = 1; i < argc && argv[i][0] == '-'; i++) {
|
||||
if (strcmp(argv[i], "-l") == 0) {
|
||||
is_listening = 1;
|
||||
} else if (strcmp(argv[i], "-d") == 0 && i + 1 < argc) {
|
||||
mgr.hexdump_file = argv[++i];
|
||||
} else {
|
||||
show_usage_and_exit(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (i + 1 == argc) {
|
||||
address = argv[i];
|
||||
} else {
|
||||
show_usage_and_exit(argv[0]);
|
||||
}
|
||||
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
if (is_listening) {
|
||||
if (ns_bind(&mgr, address, ev_handler) == NULL) {
|
||||
fprintf(stderr, "ns_bind(%s) failed\n", address);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (ns_connect(&mgr, address, ev_handler) == NULL) {
|
||||
fprintf(stderr, "ns_connect(%s) failed\n", address);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (s_received_signal == 0) {
|
||||
ns_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
PROG = publish_subscribe
|
||||
SOURCES = $(PROG).c ../../net_skeleton.c
|
||||
CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA)
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||
|
||||
$(PROG).exe: $(SOURCES)
|
||||
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||
|
||||
clean:
|
||||
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
Binary file not shown.
|
@ -0,0 +1,112 @@
|
|||
// Copyright (c) 2014 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// This software is dual-licensed: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation. For the terms of this
|
||||
// license, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// You are free to use this software under the terms of the GNU General
|
||||
// Public License, 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.
|
||||
//
|
||||
// Alternatively, you can license this software under a commercial
|
||||
// license, as set out in <http://cesanta.com/>.
|
||||
//
|
||||
// $Date: 2014-09-28 05:04:41 UTC $
|
||||
|
||||
#include "net_skeleton.h"
|
||||
|
||||
static void *stdin_thread(void *param) {
|
||||
int ch, sock = * (int *) param;
|
||||
while ((ch = getchar()) != EOF) {
|
||||
unsigned char c = (unsigned char) ch;
|
||||
send(sock, &c, 1, 0); // Forward all types characters to the socketpair
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void server_handler(struct ns_connection *nc, int ev, void *p) {
|
||||
(void) p;
|
||||
if (ev == NS_RECV) {
|
||||
// Push received message to all ncections
|
||||
struct iobuf *io = &nc->recv_iobuf;
|
||||
struct ns_connection *c;
|
||||
|
||||
for (c = ns_next(nc->mgr, NULL); c != NULL; c = ns_next(nc->mgr, c)) {
|
||||
ns_send(c, io->buf, io->len);
|
||||
}
|
||||
iobuf_remove(io, io->len);
|
||||
}
|
||||
}
|
||||
|
||||
static void client_handler(struct ns_connection *conn, int ev, void *p) {
|
||||
struct iobuf *io = &conn->recv_iobuf;
|
||||
(void) p;
|
||||
|
||||
if (ev == NS_CONNECT) {
|
||||
if (conn->flags & NSF_CLOSE_IMMEDIATELY) {
|
||||
printf("%s\n", "Error connecting to server!");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
printf("%s\n", "Connected to server. Type a message and press enter.");
|
||||
} else if (ev == NS_RECV) {
|
||||
if (conn->flags & NSF_USER_1) {
|
||||
// Received data from the stdin, forward it to the server
|
||||
struct ns_connection *c = (struct ns_connection *) conn->user_data;
|
||||
ns_send(c, io->buf, io->len);
|
||||
iobuf_remove(io, io->len);
|
||||
} else {
|
||||
// Received data from server connection, print it
|
||||
fwrite(io->buf, io->len, 1, stdout);
|
||||
iobuf_remove(io, io->len);
|
||||
}
|
||||
} else if (ev == NS_CLOSE) {
|
||||
// Connection has closed, most probably cause server has stopped
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct ns_mgr mgr;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: %s <port> <client|server>\n", argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (strcmp(argv[2], "client") == 0) {
|
||||
int fds[2];
|
||||
struct ns_connection *ioconn, *server_conn;
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
|
||||
// Connect to the pubsub server
|
||||
server_conn = ns_connect(&mgr, argv[1], client_handler);
|
||||
if (server_conn == NULL) {
|
||||
fprintf(stderr, "Cannot connect to port %s\n", argv[1]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Create a socketpair and give one end to the thread that reads stdin
|
||||
ns_socketpair(fds);
|
||||
ns_start_thread(stdin_thread, &fds[1]);
|
||||
|
||||
// The other end of a pair goes inside the server
|
||||
ioconn = ns_add_sock(&mgr, fds[0], client_handler);
|
||||
ioconn->flags |= NSF_USER_1; // Mark this so we know this is a stdin
|
||||
ioconn->user_data = server_conn;
|
||||
|
||||
} else {
|
||||
// Server code path
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
ns_bind(&mgr, argv[1], server_handler);
|
||||
printf("Starting pubsub server on port %s\n", argv[1]);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ns_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
PROG = restful_client
|
||||
SOURCES = $(PROG).c ../../net_skeleton.c
|
||||
CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA)
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||
|
||||
$(PROG).exe: $(SOURCES)
|
||||
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||
|
||||
clean:
|
||||
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
Binary file not shown.
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#include "net_skeleton.h"
|
||||
|
||||
static const char *s_target_address = "ajax.googleapis.com:80";
|
||||
static int s_exit = 0;
|
||||
|
||||
static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
int connect_status;
|
||||
|
||||
switch (ev) {
|
||||
case NS_CONNECT:
|
||||
connect_status = * (int *) ev_data;
|
||||
if (connect_status == 0) {
|
||||
printf("Connected to %s, sending request...\n", s_target_address);
|
||||
ns_printf(nc, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
|
||||
"/ajax/services/search/web?v=1.0&q=cesanta",
|
||||
"ajax.googleapis.com");
|
||||
} else {
|
||||
printf("Error connecting to %s: %s\n",
|
||||
s_target_address, strerror(connect_status));
|
||||
s_exit = 1;
|
||||
}
|
||||
break;
|
||||
case NS_HTTP_REPLY:
|
||||
printf("Got reply:\n%.*s\n", (int) hm->body.len, hm->body.p);
|
||||
nc->flags |= NSF_FINISHED_SENDING_DATA;
|
||||
s_exit = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
nc = ns_connect(&mgr, s_target_address, ev_handler);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
|
||||
printf("Starting RESTful client against %s\n", s_target_address);
|
||||
while (s_exit == 0) {
|
||||
ns_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
PROG = restful_server
|
||||
SOURCES = $(PROG).c ../../net_skeleton.c
|
||||
CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA)
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||
|
||||
$(PROG).exe: $(SOURCES)
|
||||
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||
|
||||
clean:
|
||||
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>RESTful API demo</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
* { outline: none; }
|
||||
body {
|
||||
background-color: #789; margin: 0;
|
||||
padding: 0; font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||
}
|
||||
div.content {
|
||||
width: 800px; margin: 2em auto; padding: 20px 50px;
|
||||
background-color: #fff; border-radius: 1em;
|
||||
}
|
||||
label { display: inline-block; min-width: 7em; }
|
||||
input { border: 1px solid #ccc; padding: 0.2em; }
|
||||
a:link, a:visited { color: #69c; text-decoration: none; }
|
||||
@media (max-width: 700px) {
|
||||
body { background-color: #fff; }
|
||||
div.content { width: auto; margin: 0 auto; padding: 1em; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script language="javascript" type="text/javascript">
|
||||
jQuery(function() {
|
||||
|
||||
$(document).on('keyup', '#n1, #n2', function() {
|
||||
$.ajax({
|
||||
url: '/api/v1/sum',
|
||||
method: 'POST',
|
||||
dataType: 'json',
|
||||
data: { n1: $('#n1').val(), n2: $('#n2').val() },
|
||||
success: function(json) {
|
||||
$('#result').html(json.result);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1>RESTful API demo.</h1>
|
||||
|
||||
<p>
|
||||
This page demonstrates how Net Skeleton could be used to implement
|
||||
RESTful APIs. Start typing numbers into the text fields below.
|
||||
Browser sends two numbers to <tt>/api/v1/sum</tt> URI.
|
||||
Web server calclulates the sum and returns the result.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<label>Number 1:</label> <input type="text" id="n1" />
|
||||
</div><div>
|
||||
<label>Number 2:</label> <input type="text" id="n2" />
|
||||
</div><div>
|
||||
<label>Result:</label> <span id="result"> </span>
|
||||
</div><div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,75 @@
|
|||
#include "json_rpc_http_server.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace RPC
|
||||
{
|
||||
Json_rpc_http_server::Json_rpc_http_server(const std::string &ip, const std::string &port)
|
||||
{
|
||||
m_ip = ip;
|
||||
m_port = port;
|
||||
m_is_running = false;
|
||||
m_method_count = 0;
|
||||
}
|
||||
|
||||
Json_rpc_http_server::~Json_rpc_http_server()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void Json_rpc_http_server::start()
|
||||
{
|
||||
m_is_running = true;
|
||||
server_thread = new boost::thread(poll);
|
||||
}
|
||||
|
||||
void Json_rpc_http_server::poll()
|
||||
{
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
nc = ns_bind(&mgr, s_http_port, ev_handler);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
while (m_is_running) {
|
||||
ns_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
void Json_rpc_http_server::stop()
|
||||
{
|
||||
m_is_running = false;
|
||||
server_thread->join();
|
||||
delete server_thread;
|
||||
ns_mgr_free(&mgr);
|
||||
}
|
||||
|
||||
void ev_handler(struct ns_connection *nc, int ev, void *ev_data)
|
||||
{
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
char buf[100];
|
||||
switch (ev) {
|
||||
case NS_HTTP_REQUEST:
|
||||
ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf),
|
||||
m_method_names, m_handlers);
|
||||
ns_printf(nc, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
|
||||
"Content-Type: application/json\r\n\r\n%s",
|
||||
(int) strlen(buf), buf);
|
||||
nc->flags |= NSF_FINISHED_SENDING_DATA;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool add_handler(const std::string &method_name,
|
||||
void (*hander)(char *buf, int len, struct ns_rpc_request *req))
|
||||
{
|
||||
if (m_method_count == MAX_METHODS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_method_names[m_method_count] = new char[method_name.length() + 1];
|
||||
strcpy(m_method_names[m_method_count], method_name.c_str());
|
||||
m_handlers[m_method_count] = hander;
|
||||
m_method_count++;
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#include "net_skeleton/net_skeleton.h"
|
||||
#include <boost/thread.hpp>
|
||||
#include <string>
|
||||
|
||||
#define MAX_METHODS
|
||||
|
||||
namespace RPC
|
||||
{
|
||||
class Json_rpc_http_server
|
||||
{
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
Boost::thread *server_thread;
|
||||
void ev_handler(struct ns_connection *nc, int ev, void *ev_data);
|
||||
void poll();
|
||||
std::string m_ip;
|
||||
std::string m_port;
|
||||
bool m_is_running;
|
||||
char *m_method_names[MAX_METHODS];
|
||||
ns_rpc_handler_t m_handlers[MAX_METHODS];
|
||||
int m_method_count;
|
||||
public:
|
||||
Json_rpc_http_server(const std::string &ip, const std::string &port);
|
||||
~Json_rpc_http_server();
|
||||
void start();
|
||||
void stop();
|
||||
bool add_handler(const std::string &method_name,
|
||||
void (*hander)(char *buf, int len, struct ns_rpc_request *req));
|
||||
};
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#include "net_skeleton.h"
|
||||
|
||||
static const char *s_http_port = "8000";
|
||||
static struct ns_serve_http_opts s_http_server_opts = { "." };
|
||||
|
||||
static void handle_sum_call(struct ns_connection *nc, struct http_message *hm) {
|
||||
char n1[100], n2[100];
|
||||
|
||||
/* Get form variables */
|
||||
ns_get_http_var(&hm->body, "n1", n1, sizeof(n1));
|
||||
ns_get_http_var(&hm->body, "n2", n2, sizeof(n2));
|
||||
|
||||
ns_printf(nc, "%s", "HTTP/1.0 200 OK\n\n");
|
||||
ns_printf(nc, "{ \"result\": %lf }", strtod(n1, NULL) + strtod(n2, NULL));
|
||||
}
|
||||
|
||||
static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
|
||||
switch (ev) {
|
||||
case NS_HTTP_REQUEST:
|
||||
if (ns_vcmp(&hm->uri, "/api/v1/sum") == 0) {
|
||||
handle_sum_call(nc, hm); /* Handle RESTful call */
|
||||
} else {
|
||||
ns_serve_http(nc, hm, s_http_server_opts); /* Serve static content */
|
||||
}
|
||||
nc->flags |= NSF_FINISHED_SENDING_DATA;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
nc = ns_bind(&mgr, s_http_port, ev_handler);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
|
||||
printf("Starting RESTful server on port %s\n", s_http_port);
|
||||
for (;;) {
|
||||
ns_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
PROG = echo_server
|
||||
SOURCES = $(PROG).c ../../net_skeleton.c
|
||||
CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA)
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||
|
||||
$(PROG).exe: $(SOURCES)
|
||||
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||
|
||||
clean:
|
||||
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
Binary file not shown.
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2014 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// This software is dual-licensed: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation. For the terms of this
|
||||
// license, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// You are free to use this software under the terms of the GNU General
|
||||
// Public License, 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.
|
||||
//
|
||||
// Alternatively, you can license this software under a commercial
|
||||
// license, as set out in <http://cesanta.com/>.
|
||||
//
|
||||
// $Date: 2014-09-28 05:04:41 UTC $
|
||||
|
||||
#include "net_skeleton.h"
|
||||
|
||||
static void ev_handler(struct ns_connection *nc, int ev, void *p) {
|
||||
struct iobuf *io = &nc->recv_iobuf;
|
||||
(void) p;
|
||||
|
||||
switch (ev) {
|
||||
case NS_RECV:
|
||||
ns_send(nc, io->buf, io->len); // Echo message back
|
||||
iobuf_remove(io, io->len); // Discard message from recv buffer
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct ns_mgr mgr;
|
||||
const char *port1 = "1234", *port2 = "127.0.0.1:17000";
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
ns_bind(&mgr, port1, ev_handler);
|
||||
ns_bind(&mgr, port2, ev_handler);
|
||||
|
||||
printf("Starting echo mgr on ports %s, %s\n", port1, port2);
|
||||
for (;;) {
|
||||
ns_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
# Copyright (c) 2014 Cesanta Software
|
||||
# All rights reserved
|
||||
|
||||
PROG = websocket_chat
|
||||
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
|
||||
SOURCES = $(PROG).c ../../net_skeleton.c
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
|
|
@ -0,0 +1,85 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>WebSocket Test</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color: #789; margin: 0;
|
||||
padding: 0; font: 14px Helvetica, Arial, sans-serif;
|
||||
}
|
||||
div.content {
|
||||
width: 800px; margin: 2em auto; padding: 20px 50px;
|
||||
background-color: #fff; border-radius: 1em;
|
||||
}
|
||||
#messages {
|
||||
border: 2px solid #fec; border-radius: 1em;
|
||||
height: 10em; overflow: scroll; padding: 0.5em 1em;
|
||||
}
|
||||
a:link, a:visited { color: #69c; text-decoration: none; }
|
||||
@media (max-width: 700px) {
|
||||
body { background-color: #fff; }
|
||||
div.content {
|
||||
width: auto; margin: 0 auto; border-radius: 0;
|
||||
padding: 1em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script language="javascript" type="text/javascript">
|
||||
|
||||
var rooms = [];
|
||||
var ws = new WebSocket('ws://' + location.host + '/ws');
|
||||
|
||||
if (!window.console) { window.console = { log: function() {} } };
|
||||
|
||||
ws.onopen = function(ev) { console.log(ev); };
|
||||
ws.onerror = function(ev) { console.log(ev); };
|
||||
ws.onclose = function(ev) { console.log(ev); };
|
||||
ws.onmessage = function(ev) {
|
||||
console.log(ev);
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = ev.data;
|
||||
document.getElementById('messages').appendChild(div);
|
||||
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
document.getElementById('send_button').onclick = function(ev) {
|
||||
var msg = document.getElementById('send_input').value;
|
||||
document.getElementById('send_input').value = '';
|
||||
ws.send(msg);
|
||||
};
|
||||
document.getElementById('send_input').onkeypress = function(ev) {
|
||||
if (ev.keyCode == 13 || ev.which == 13) {
|
||||
document.getElementById('send_button').click();
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1>Websocket PubSub Demonstration</h1>
|
||||
|
||||
<p>
|
||||
This page demonstrates how net skeleton could be used to implement
|
||||
<a href="http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">
|
||||
publish–subscribe pattern</a>. Open this page in several browser
|
||||
windows. Each window initiates persistent
|
||||
<a href="http://en.wikipedia.org/wiki/WebSocket">WebSocket</a>
|
||||
connection with the server, making each browser window a websocket client.
|
||||
Send messages, and see messages sent by other clients.
|
||||
</p>
|
||||
|
||||
<div id="messages">
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<input type="text" id="send_input" />
|
||||
<button id="send_button">Send Message</button>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#include "net_skeleton.h"
|
||||
|
||||
static int s_signal_received = 0;
|
||||
static const char *s_http_port = "8000";
|
||||
static struct ns_serve_http_opts s_http_server_opts = { "." };
|
||||
|
||||
static void signal_handler(int sig_num) {
|
||||
signal(sig_num, signal_handler); // Reinstantiate signal handler
|
||||
s_signal_received = sig_num;
|
||||
}
|
||||
|
||||
static int is_websocket(const struct ns_connection *nc) {
|
||||
return nc->flags & NSF_IS_WEBSOCKET;
|
||||
}
|
||||
|
||||
static void broadcast(struct ns_connection *nc, const char *msg, size_t len) {
|
||||
struct ns_connection *c;
|
||||
char buf[500];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%p %.*s", nc, (int) len, msg);
|
||||
for (c = ns_next(nc->mgr, NULL); c != NULL; c = ns_next(nc->mgr, c)) {
|
||||
ns_send_websocket_frame(c, WEBSOCKET_OP_TEXT, buf, strlen(buf));
|
||||
}
|
||||
}
|
||||
|
||||
static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
|
||||
switch (ev) {
|
||||
case NS_HTTP_REQUEST:
|
||||
/* Usual HTTP request - serve static files */
|
||||
ns_serve_http(nc, hm, s_http_server_opts);
|
||||
nc->flags |= NSF_FINISHED_SENDING_DATA;
|
||||
break;
|
||||
case NS_WEBSOCKET_HANDSHAKE_DONE:
|
||||
/* New websocket connection. Tell everybody. */
|
||||
broadcast(nc, "joined", 6);
|
||||
break;
|
||||
case NS_WEBSOCKET_FRAME:
|
||||
/* New websocket message. Tell everybody. */
|
||||
broadcast(nc, (char *) wm->data, wm->size);
|
||||
break;
|
||||
case NS_CLOSE:
|
||||
/* Disconnect. Tell everybody. */
|
||||
if (is_websocket(nc)) {
|
||||
broadcast(nc, "left", 4);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
|
||||
nc = ns_bind(&mgr, s_http_port, ev_handler);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
|
||||
printf("Started on port %s\n", s_http_port);
|
||||
while (s_signal_received == 0) {
|
||||
ns_mgr_poll(&mgr, 200);
|
||||
}
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
FR = ../../frozen
|
||||
HEADERS = skeleton.h $(FR)/frozen.h sha1.h util.h http.h json-rpc.h
|
||||
SOURCES = skeleton.c $(FR)/frozen.c http.c sha1.c util.c json-rpc.c
|
||||
|
||||
all: ../net_skeleton.c ../net_skeleton.h
|
||||
|
||||
../net_skeleton.h: Makefile $(HEADERS)
|
||||
$(MAKE) -s --no-print-directory merge_net_skeleton.h >$@
|
||||
|
||||
../net_skeleton.c: Makefile $(SOURCES)
|
||||
$(MAKE) -s --no-print-directory merge_net_skeleton.c >$@
|
||||
|
||||
merge_net_skeleton.h: Makefile $(HEADERS)
|
||||
@cat $(HEADERS)
|
||||
|
||||
merge_net_skeleton.c: Makefile $(SOURCES)
|
||||
@(echo '#include "net_skeleton.h"'; (cat $(SOURCES) | sed '/^#include "/d'))
|
|
@ -0,0 +1,485 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef NS_DISABLE_HTTP_WEBSOCKET
|
||||
|
||||
#include "net_skeleton.h"
|
||||
#include "sha1.h"
|
||||
#include "util.h"
|
||||
#include "http.h"
|
||||
|
||||
/*
|
||||
* Check whether full request is buffered. Return:
|
||||
* -1 if request is malformed
|
||||
* 0 if request is not yet fully buffered
|
||||
* >0 actual request length, including last \r\n\r\n
|
||||
*/
|
||||
static int get_request_len(const char *s, int buf_len) {
|
||||
const unsigned char *buf = (unsigned char *) s;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < buf_len; i++) {
|
||||
if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) {
|
||||
return -1;
|
||||
} else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') {
|
||||
return i + 2;
|
||||
} else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' &&
|
||||
buf[i + 2] == '\n') {
|
||||
return i + 3;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ns_parse_http(const char *s, int n, struct http_message *req) {
|
||||
const char *end;
|
||||
int len, i;
|
||||
|
||||
if ((len = get_request_len(s, n)) <= 0) return len;
|
||||
|
||||
memset(req, 0, sizeof(*req));
|
||||
req->message.p = s;
|
||||
req->body.p = s + len;
|
||||
req->message.len = req->body.len = (size_t) ~0;
|
||||
end = s + len;
|
||||
|
||||
/* Request is fully buffered. Skip leading whitespaces. */
|
||||
while (s < end && isspace(* (unsigned char *) s)) s++;
|
||||
|
||||
/* Parse request line: method, URI, proto */
|
||||
s = ns_skip(s, end, " ", &req->method);
|
||||
s = ns_skip(s, end, " ", &req->uri);
|
||||
s = ns_skip(s, end, "\r\n", &req->proto);
|
||||
if (req->uri.p <= req->method.p || req->proto.p <= req->uri.p) return -1;
|
||||
|
||||
for (i = 0; i < (int) ARRAY_SIZE(req->header_names); i++) {
|
||||
struct ns_str *k = &req->header_names[i], *v = &req->header_values[i];
|
||||
|
||||
s = ns_skip(s, end, ": ", k);
|
||||
s = ns_skip(s, end, "\r\n", v);
|
||||
|
||||
while (v->len > 0 && v->p[v->len - 1] == ' ') {
|
||||
v->len--; /* Trim trailing spaces in header value */
|
||||
}
|
||||
|
||||
if (k->len == 0 || v->len == 0) {
|
||||
k->p = v->p = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ns_ncasecmp(k->p, "Content-Length", 14)) {
|
||||
req->body.len = to64(v->p);
|
||||
req->message.len = len + req->body.len;
|
||||
}
|
||||
}
|
||||
|
||||
if (req->body.len == (size_t) ~0 && ns_vcasecmp(&req->method, "GET") == 0) {
|
||||
req->body.len = 0;
|
||||
req->message.len = len;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
struct ns_str *ns_get_http_header(struct http_message *hm, const char *name) {
|
||||
size_t i, len = strlen(name);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hm->header_names); i++) {
|
||||
struct ns_str *h = &hm->header_names[i], *v = &hm->header_values[i];
|
||||
if (h->p != NULL && h->len == len && !ns_ncasecmp(h->p, name, len)) return v;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int is_ws_fragment(unsigned char flags) {
|
||||
return (flags & 0x80) == 0 || (flags & 0x0f) == 0;
|
||||
}
|
||||
|
||||
static int is_ws_first_fragment(unsigned char flags) {
|
||||
return (flags & 0x80) == 0 && (flags & 0x0f) != 0;
|
||||
}
|
||||
|
||||
static int deliver_websocket_data(struct ns_connection *nc) {
|
||||
/* Using unsigned char *, cause of integer arithmetic below */
|
||||
uint64_t i, data_len = 0, frame_len = 0, buf_len = nc->recv_iobuf.len,
|
||||
len, mask_len = 0, header_len = 0;
|
||||
unsigned char *p = (unsigned char *) nc->recv_iobuf.buf,
|
||||
*buf = p, *e = p + buf_len;
|
||||
unsigned *sizep = (unsigned *) &p[1]; /* Size ptr for defragmented frames */
|
||||
int ok, reass = buf_len > 0 && is_ws_fragment(p[0]) &&
|
||||
!(nc->flags & NSF_WEBSOCKET_NO_DEFRAG);
|
||||
|
||||
/* If that's a continuation frame that must be reassembled, handle it */
|
||||
if (reass && !is_ws_first_fragment(p[0]) && buf_len >= 1 + sizeof(*sizep) &&
|
||||
buf_len >= 1 + sizeof(*sizep) + *sizep) {
|
||||
buf += 1 + sizeof(*sizep) + *sizep;
|
||||
buf_len -= 1 + sizeof(*sizep) + *sizep;
|
||||
}
|
||||
|
||||
if (buf_len >= 2) {
|
||||
len = buf[1] & 127;
|
||||
mask_len = buf[1] & 128 ? 4 : 0;
|
||||
if (len < 126 && buf_len >= mask_len) {
|
||||
data_len = len;
|
||||
header_len = 2 + mask_len;
|
||||
} else if (len == 126 && buf_len >= 4 + mask_len) {
|
||||
header_len = 4 + mask_len;
|
||||
data_len = ntohs(* (uint16_t *) &buf[2]);
|
||||
} else if (buf_len >= 10 + mask_len) {
|
||||
header_len = 10 + mask_len;
|
||||
data_len = (((uint64_t) ntohl(* (uint32_t *) &buf[2])) << 32) +
|
||||
ntohl(* (uint32_t *) &buf[6]);
|
||||
}
|
||||
}
|
||||
|
||||
frame_len = header_len + data_len;
|
||||
ok = frame_len > 0 && frame_len <= buf_len;
|
||||
|
||||
if (ok) {
|
||||
struct websocket_message wsm;
|
||||
|
||||
wsm.size = (size_t) data_len;
|
||||
wsm.data = buf + header_len;
|
||||
wsm.flags = buf[0];
|
||||
|
||||
/* Apply mask if necessary */
|
||||
if (mask_len > 0) {
|
||||
for (i = 0; i < data_len; i++) {
|
||||
buf[i + header_len] ^= (buf + header_len - mask_len)[i % 4];
|
||||
}
|
||||
}
|
||||
|
||||
if (reass) {
|
||||
/* On first fragmented frame, nullify size */
|
||||
if (is_ws_first_fragment(wsm.flags)) {
|
||||
iobuf_resize(&nc->recv_iobuf, nc->recv_iobuf.size + sizeof(*sizep));
|
||||
p[0] &= ~0x0f; /* Next frames will be treated as continuation */
|
||||
buf = p + 1 + sizeof(*sizep);
|
||||
*sizep = 0; /* TODO(lsm): fix. this can stomp over frame data */
|
||||
}
|
||||
|
||||
/* Append this frame to the reassembled buffer */
|
||||
memmove(buf, wsm.data, e - wsm.data);
|
||||
(*sizep) += wsm.size;
|
||||
nc->recv_iobuf.len -= wsm.data - buf;
|
||||
|
||||
/* On last fragmented frame - call user handler and remove data */
|
||||
if (wsm.flags & 0x80) {
|
||||
wsm.data = p + 1 + sizeof(*sizep);
|
||||
wsm.size = *sizep;
|
||||
nc->handler(nc, NS_WEBSOCKET_FRAME, &wsm);
|
||||
iobuf_remove(&nc->recv_iobuf, 1 + sizeof(*sizep) + *sizep);
|
||||
}
|
||||
} else {
|
||||
/* TODO(lsm): properly handle OOB control frames during defragmentation */
|
||||
nc->handler(nc, NS_WEBSOCKET_FRAME, &wsm); /* Call handler */
|
||||
iobuf_remove(&nc->recv_iobuf, (size_t) frame_len); /* Cleanup frame */
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void ns_send_ws_header(struct ns_connection *nc, int op, size_t len) {
|
||||
int header_len;
|
||||
unsigned char header[10];
|
||||
|
||||
header[0] = 0x80 + (op & 0x0f);
|
||||
if (len < 126) {
|
||||
header[1] = len;
|
||||
header_len = 2;
|
||||
} else if (len < 65535) {
|
||||
header[1] = 126;
|
||||
* (uint16_t *) &header[2] = htons((uint16_t) len);
|
||||
header_len = 4;
|
||||
} else {
|
||||
header[1] = 127;
|
||||
* (uint32_t *) &header[2] = htonl((uint32_t) ((uint64_t) len >> 32));
|
||||
* (uint32_t *) &header[6] = htonl((uint32_t) (len & 0xffffffff));
|
||||
header_len = 10;
|
||||
}
|
||||
ns_send(nc, header, header_len);
|
||||
}
|
||||
|
||||
void ns_send_websocket_frame(struct ns_connection *nc, int op,
|
||||
const void *data, size_t len) {
|
||||
ns_send_ws_header(nc, op, len);
|
||||
ns_send(nc, data, len);
|
||||
|
||||
if (op == WEBSOCKET_OP_CLOSE) {
|
||||
nc->flags |= NSF_FINISHED_SENDING_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
void ns_send_websocket_framev(struct ns_connection *nc, int op,
|
||||
const struct ns_str *strv, int strvcnt) {
|
||||
int i;
|
||||
int len = 0;
|
||||
for (i=0; i<strvcnt; i++) {
|
||||
len += strv[i].len;
|
||||
}
|
||||
|
||||
ns_send_ws_header(nc, op, len);
|
||||
|
||||
for (i=0; i<strvcnt; i++) {
|
||||
ns_send(nc, strv[i].p, strv[i].len);
|
||||
}
|
||||
|
||||
if (op == WEBSOCKET_OP_CLOSE) {
|
||||
nc->flags |= NSF_FINISHED_SENDING_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
void ns_printf_websocket_frame(struct ns_connection *nc, int op,
|
||||
const char *fmt, ...) {
|
||||
char mem[4192], *buf = mem;
|
||||
va_list ap;
|
||||
int len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
|
||||
ns_send_websocket_frame(nc, op, buf, len);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
if (buf != mem && buf != NULL) {
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void websocket_handler(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
nc->handler(nc, ev, ev_data);
|
||||
|
||||
switch (ev) {
|
||||
case NS_RECV:
|
||||
do { } while (deliver_websocket_data(nc));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ws_handshake(struct ns_connection *nc, const struct ns_str *key) {
|
||||
static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
char buf[500], sha[20], b64_sha[sizeof(sha) * 2];
|
||||
SHA1_CTX sha_ctx;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%.*s%s", (int) key->len, key->p, magic);
|
||||
|
||||
SHA1Init(&sha_ctx);
|
||||
SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf));
|
||||
SHA1Final((unsigned char *) sha, &sha_ctx);
|
||||
|
||||
ns_base64_encode((unsigned char *) sha, sizeof(sha), b64_sha);
|
||||
ns_printf(nc, "%s%s%s",
|
||||
"HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");
|
||||
}
|
||||
|
||||
static void http_handler(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct iobuf *io = &nc->recv_iobuf;
|
||||
struct http_message hm;
|
||||
struct ns_str *vec;
|
||||
int req_len;
|
||||
|
||||
/*
|
||||
* For HTTP messages without Content-Length, always send HTTP message
|
||||
* before NS_CLOSE message.
|
||||
*/
|
||||
if (ev == NS_CLOSE && io->len > 0 &&
|
||||
ns_parse_http(io->buf, io->len, &hm) > 0) {
|
||||
hm.body.len = io->buf + io->len - hm.body.p;
|
||||
nc->handler(nc, nc->listener ? NS_HTTP_REQUEST : NS_HTTP_REPLY, &hm);
|
||||
}
|
||||
|
||||
nc->handler(nc, ev, ev_data);
|
||||
|
||||
if (ev == NS_RECV) {
|
||||
req_len = ns_parse_http(io->buf, io->len, &hm);
|
||||
if (req_len < 0 || (req_len == 0 && io->len >= NS_MAX_HTTP_REQUEST_SIZE)) {
|
||||
nc->flags |= NSF_CLOSE_IMMEDIATELY;
|
||||
} else if (req_len == 0) {
|
||||
/* Do nothing, request is not yet fully buffered */
|
||||
} else if (nc->listener == NULL &&
|
||||
ns_get_http_header(&hm, "Sec-WebSocket-Accept")) {
|
||||
/* We're websocket client, got handshake response from server. */
|
||||
/* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */
|
||||
iobuf_remove(io, req_len);
|
||||
nc->proto_handler = websocket_handler;
|
||||
nc->flags |= NSF_IS_WEBSOCKET;
|
||||
nc->handler(nc, NS_WEBSOCKET_HANDSHAKE_DONE, NULL);
|
||||
websocket_handler(nc, NS_RECV, ev_data);
|
||||
} else if (nc->listener != NULL &&
|
||||
(vec = ns_get_http_header(&hm, "Sec-WebSocket-Key")) != NULL) {
|
||||
/* This is a websocket request. Switch protocol handlers. */
|
||||
iobuf_remove(io, req_len);
|
||||
nc->proto_handler = websocket_handler;
|
||||
nc->flags |= NSF_IS_WEBSOCKET;
|
||||
|
||||
/* Send handshake */
|
||||
nc->handler(nc, NS_WEBSOCKET_HANDSHAKE_REQUEST, &hm);
|
||||
if (!(nc->flags & NSF_CLOSE_IMMEDIATELY)) {
|
||||
if (nc->send_iobuf.len == 0) {
|
||||
ws_handshake(nc, vec);
|
||||
}
|
||||
nc->handler(nc, NS_WEBSOCKET_HANDSHAKE_DONE, NULL);
|
||||
websocket_handler(nc, NS_RECV, ev_data);
|
||||
}
|
||||
} else if (hm.message.len <= io->len) {
|
||||
/* Whole HTTP message is fully buffered, call event handler */
|
||||
nc->handler(nc, nc->listener ? NS_HTTP_REQUEST : NS_HTTP_REPLY, &hm);
|
||||
iobuf_remove(io, hm.message.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ns_set_protocol_http_websocket(struct ns_connection *nc) {
|
||||
nc->proto_handler = http_handler;
|
||||
}
|
||||
|
||||
void ns_send_websocket_handshake(struct ns_connection *nc, const char *uri,
|
||||
const char *extra_headers) {
|
||||
unsigned long random = (unsigned long) uri;
|
||||
char key[sizeof(random) * 2];
|
||||
|
||||
ns_base64_encode((unsigned char *) &random, sizeof(random), key);
|
||||
ns_printf(nc, "GET %s HTTP/1.1\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Sec-WebSocket-Key: %s\r\n"
|
||||
"%s\r\n",
|
||||
uri, key, extra_headers == NULL ? "" : extra_headers);
|
||||
}
|
||||
|
||||
void ns_send_http_file(struct ns_connection *nc, const char *path,
|
||||
ns_stat_t *st) {
|
||||
char buf[BUFSIZ];
|
||||
size_t n;
|
||||
FILE *fp;
|
||||
|
||||
if ((fp = fopen(path, "rb")) != NULL) {
|
||||
ns_printf(nc, "HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: %lu\r\n\r\n", (unsigned long) st->st_size);
|
||||
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
|
||||
ns_send(nc, buf, n);
|
||||
}
|
||||
fclose(fp);
|
||||
} else {
|
||||
ns_printf(nc, "%s", "HTTP/1.1 500 Server Error\r\n"
|
||||
"Content-Length: 0\r\n\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_double_dots(char *s) {
|
||||
char *p = s;
|
||||
|
||||
while (*s != '\0') {
|
||||
*p++ = *s++;
|
||||
if (s[-1] == '/' || s[-1] == '\\') {
|
||||
while (s[0] != '\0') {
|
||||
if (s[0] == '/' || s[0] == '\\') {
|
||||
s++;
|
||||
} else if (s[0] == '.' && s[1] == '.') {
|
||||
s += 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
int ns_url_decode(const char *src, int src_len, char *dst,
|
||||
int dst_len, int is_form_url_encoded) {
|
||||
int i, j, a, b;
|
||||
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
|
||||
|
||||
for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
|
||||
if (src[i] == '%' && i < src_len - 2 &&
|
||||
isxdigit(* (const unsigned char *) (src + i + 1)) &&
|
||||
isxdigit(* (const unsigned char *) (src + i + 2))) {
|
||||
a = tolower(* (const unsigned char *) (src + i + 1));
|
||||
b = tolower(* (const unsigned char *) (src + i + 2));
|
||||
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
|
||||
i += 2;
|
||||
} else if (is_form_url_encoded && src[i] == '+') {
|
||||
dst[j] = ' ';
|
||||
} else {
|
||||
dst[j] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
dst[j] = '\0'; /* Null-terminate the destination */
|
||||
|
||||
return i >= src_len ? j : -1;
|
||||
}
|
||||
|
||||
int ns_get_http_var(const struct ns_str *buf, const char *name,
|
||||
char *dst, size_t dst_len) {
|
||||
const char *p, *e, *s;
|
||||
size_t name_len;
|
||||
int len;
|
||||
|
||||
if (dst == NULL || dst_len == 0) {
|
||||
len = -2;
|
||||
} else if (buf->p == NULL || name == NULL || buf->len == 0) {
|
||||
len = -1;
|
||||
dst[0] = '\0';
|
||||
} else {
|
||||
name_len = strlen(name);
|
||||
e = buf->p + buf->len;
|
||||
len = -1;
|
||||
dst[0] = '\0';
|
||||
|
||||
for (p = buf->p; p + name_len < e; p++) {
|
||||
if ((p == buf->p || p[-1] == '&') && p[name_len] == '=' &&
|
||||
!ns_ncasecmp(name, p, name_len)) {
|
||||
p += name_len + 1;
|
||||
s = (const char *) memchr(p, '&', (size_t)(e - p));
|
||||
if (s == NULL) {
|
||||
s = e;
|
||||
}
|
||||
len = ns_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
|
||||
if (len == -1) {
|
||||
len = -2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void ns_serve_http(struct ns_connection *nc, struct http_message *hm,
|
||||
struct ns_serve_http_opts opts) {
|
||||
char path[NS_MAX_PATH];
|
||||
ns_stat_t st;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%.*s", opts.document_root,
|
||||
(int) hm->uri.len, hm->uri.p);
|
||||
remove_double_dots(path);
|
||||
|
||||
if (ns_stat(path, &st) != 0) {
|
||||
ns_printf(nc, "%s", "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n");
|
||||
} else if (S_ISDIR(st.st_mode)) {
|
||||
strncat(path, "/index.html", sizeof(path) - (strlen(path) + 1));
|
||||
if (ns_stat(path, &st) == 0) {
|
||||
ns_send_http_file(nc, path, &st);
|
||||
} else {
|
||||
ns_printf(nc, "%s", "HTTP/1.1 403 Access Denied\r\n"
|
||||
"Content-Length: 0\r\n\r\n");
|
||||
}
|
||||
} else {
|
||||
ns_send_http_file(nc, path, &st);
|
||||
}
|
||||
}
|
||||
#endif /* NS_DISABLE_HTTP_WEBSOCKET */
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef NS_HTTP_HEADER_DEFINED
|
||||
#define NS_HTTP_HEADER_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define NS_MAX_HTTP_HEADERS 40
|
||||
#define NS_MAX_HTTP_REQUEST_SIZE 8192
|
||||
#define NS_MAX_PATH 1024
|
||||
|
||||
struct http_message {
|
||||
struct ns_str message; /* Whole message: request line + headers + body */
|
||||
|
||||
/* HTTP Request line (or HTTP response line) */
|
||||
struct ns_str method; /* "GET" */
|
||||
struct ns_str uri; /* "/my_file.html" */
|
||||
struct ns_str proto; /* "HTTP/1.1" */
|
||||
|
||||
/* Headers */
|
||||
struct ns_str header_names[NS_MAX_HTTP_HEADERS];
|
||||
struct ns_str header_values[NS_MAX_HTTP_HEADERS];
|
||||
|
||||
/* Message body */
|
||||
struct ns_str body; /* Zero-length for requests with no body */
|
||||
};
|
||||
|
||||
struct websocket_message {
|
||||
unsigned char *data;
|
||||
size_t size;
|
||||
unsigned char flags;
|
||||
};
|
||||
|
||||
/* HTTP and websocket events. void *ev_data is described in a comment. */
|
||||
#define NS_HTTP_REQUEST 100 /* struct http_message * */
|
||||
#define NS_HTTP_REPLY 101 /* struct http_message * */
|
||||
|
||||
#define NS_WEBSOCKET_HANDSHAKE_REQUEST 111 /* NULL */
|
||||
#define NS_WEBSOCKET_HANDSHAKE_DONE 112 /* NULL */
|
||||
#define NS_WEBSOCKET_FRAME 113 /* struct websocket_message * */
|
||||
|
||||
void ns_set_protocol_http_websocket(struct ns_connection *);
|
||||
void ns_send_websocket_handshake(struct ns_connection *, const char *,
|
||||
const char *);
|
||||
void ns_send_websocket_frame(struct ns_connection *, int, const void *, size_t);
|
||||
void ns_send_websocket_framev(struct ns_connection *, int, const struct ns_str *, int);
|
||||
|
||||
void ns_printf_websocket_frame(struct ns_connection *, int, const char *, ...);
|
||||
|
||||
/* Websocket opcodes, from http://tools.ietf.org/html/rfc6455 */
|
||||
#define WEBSOCKET_OP_CONTINUE 0
|
||||
#define WEBSOCKET_OP_TEXT 1
|
||||
#define WEBSOCKET_OP_BINARY 2
|
||||
#define WEBSOCKET_OP_CLOSE 8
|
||||
#define WEBSOCKET_OP_PING 9
|
||||
#define WEBSOCKET_OP_PONG 10
|
||||
|
||||
/* Utility functions */
|
||||
struct ns_str *ns_get_http_header(struct http_message *, const char *);
|
||||
int ns_parse_http(const char *s, int n, struct http_message *req);
|
||||
int ns_get_http_var(const struct ns_str *, const char *, char *dst, size_t);
|
||||
|
||||
|
||||
struct ns_serve_http_opts {
|
||||
const char *document_root;
|
||||
};
|
||||
void ns_serve_http(struct ns_connection *, struct http_message *,
|
||||
struct ns_serve_http_opts);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* NS_HTTP_HEADER_DEFINED */
|
|
@ -0,0 +1,141 @@
|
|||
/* Copyright (c) 2014 Cesanta Software Limited */
|
||||
/* All rights reserved */
|
||||
|
||||
#ifndef NS_DISABLE_JSON_RPC
|
||||
|
||||
#include "net_skeleton.h"
|
||||
#include "json-rpc.h"
|
||||
|
||||
int ns_rpc_create_reply(char *buf, int len, const struct ns_rpc_request *req,
|
||||
const char *result_fmt, ...) {
|
||||
va_list ap;
|
||||
int n = 0;
|
||||
|
||||
n += json_emit(buf + n, len - n, "{s:s,s:V,s:",
|
||||
"jsonrpc", "2.0", "id",
|
||||
req->id == NULL ? "null" : req->id->ptr,
|
||||
req->id == NULL ? 4 : req->id->len,
|
||||
"result");
|
||||
va_start(ap, result_fmt);
|
||||
n += json_emit_va(buf + n, len - n, result_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
n += json_emit(buf + n, len - n, "}");
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int ns_rpc_create_request(char *buf, int len, const char *method,
|
||||
const char *id, const char *params_fmt, ...) {
|
||||
va_list ap;
|
||||
int n = 0;
|
||||
|
||||
n += json_emit(buf + n, len - n, "{s:s,s:s,s:s,s:",
|
||||
"jsonrpc", "2.0", "id", id, "method", method, "params");
|
||||
va_start(ap, params_fmt);
|
||||
n += json_emit_va(buf + n, len - n, params_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
n += json_emit(buf + n, len - n, "}");
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int ns_rpc_create_error(char *buf, int len, struct ns_rpc_request *req,
|
||||
int code, const char *message, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
int n = 0;
|
||||
|
||||
n += json_emit(buf + n, len - n, "{s:s,s:V,s:{s:i,s:s,s:",
|
||||
"jsonrpc", "2.0", "id",
|
||||
req->id == NULL ? "null" : req->id->ptr,
|
||||
req->id == NULL ? 4 : req->id->len,
|
||||
"error", "code", code,
|
||||
"message", message, "data");
|
||||
va_start(ap, fmt);
|
||||
n += json_emit_va(buf + n, len - n, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
n += json_emit(buf + n, len - n, "}}");
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int ns_rpc_create_std_error(char *buf, int len, struct ns_rpc_request *req,
|
||||
int code) {
|
||||
const char *message = NULL;
|
||||
|
||||
switch (code) {
|
||||
case JSON_RPC_PARSE_ERROR: message = "parse error"; break;
|
||||
case JSON_RPC_INVALID_REQUEST_ERROR: message = "invalid request"; break;
|
||||
case JSON_RPC_METHOD_NOT_FOUND_ERROR: message = "method not found"; break;
|
||||
case JSON_RPC_INVALID_PARAMS_ERROR: message = "invalid parameters"; break;
|
||||
case JSON_RPC_SERVER_ERROR: message = "server error"; break;
|
||||
default: message = "unspecified error"; break;
|
||||
}
|
||||
|
||||
return ns_rpc_create_error(buf, len, req, code, message, "N");
|
||||
}
|
||||
|
||||
int ns_rpc_dispatch(const char *buf, int len, char *dst, int dst_len,
|
||||
const char **methods, ns_rpc_handler_t *handlers) {
|
||||
struct json_token tokens[200];
|
||||
struct ns_rpc_request req;
|
||||
int i, n;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
n = parse_json(buf, len, tokens, sizeof(tokens) / sizeof(tokens[0]));
|
||||
if (n <= 0) {
|
||||
int err_code = (n == JSON_STRING_INVALID) ?
|
||||
JSON_RPC_PARSE_ERROR : JSON_RPC_SERVER_ERROR;
|
||||
return ns_rpc_create_std_error(dst, dst_len, &req, err_code);
|
||||
}
|
||||
|
||||
req.message = tokens;
|
||||
req.id = find_json_token(tokens, "id");
|
||||
req.method = find_json_token(tokens, "method");
|
||||
req.params = find_json_token(tokens, "params");
|
||||
|
||||
if (req.id == NULL || req.method == NULL) {
|
||||
return ns_rpc_create_std_error(dst, dst_len, &req,
|
||||
JSON_RPC_INVALID_REQUEST_ERROR);
|
||||
}
|
||||
|
||||
for (i = 0; methods[i] != NULL; i++) {
|
||||
int mlen = strlen(methods[i]);
|
||||
if (mlen == req.method->len &&
|
||||
memcmp(methods[i], req.method->ptr, mlen) == 0) break;
|
||||
}
|
||||
|
||||
if (methods[i] == NULL) {
|
||||
return ns_rpc_create_std_error(dst, dst_len, &req,
|
||||
JSON_RPC_METHOD_NOT_FOUND_ERROR);
|
||||
}
|
||||
|
||||
return handlers[i](dst, dst_len, &req);
|
||||
}
|
||||
|
||||
int ns_rpc_parse_reply(const char *buf, int len,
|
||||
struct json_token *toks, int max_toks,
|
||||
struct ns_rpc_reply *rep, struct ns_rpc_error *er) {
|
||||
int n = parse_json(buf, len, toks, max_toks);
|
||||
|
||||
memset(rep, 0, sizeof(*rep));
|
||||
memset(er, 0, sizeof(*er));
|
||||
|
||||
if (n > 0) {
|
||||
if ((rep->result = find_json_token(toks, "result")) != NULL) {
|
||||
rep->message = toks;
|
||||
rep->id = find_json_token(toks, "id");
|
||||
} else {
|
||||
er->message = toks;
|
||||
er->id = find_json_token(toks, "id");
|
||||
er->error_code = find_json_token(toks, "error.code");
|
||||
er->error_message = find_json_token(toks, "error.message");
|
||||
er->error_data = find_json_token(toks, "error.data");
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif /* NS_DISABLE_JSON_RPC */
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef NS_JSON_RPC_HEADER_DEFINED
|
||||
#define NS_JSON_RPC_HEADER_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* JSON-RPC standard error codes */
|
||||
#define JSON_RPC_PARSE_ERROR (-32700)
|
||||
#define JSON_RPC_INVALID_REQUEST_ERROR (-32600)
|
||||
#define JSON_RPC_METHOD_NOT_FOUND_ERROR (-32601)
|
||||
#define JSON_RPC_INVALID_PARAMS_ERROR (-32602)
|
||||
#define JSON_RPC_INTERNAL_ERROR (-32603)
|
||||
#define JSON_RPC_SERVER_ERROR (-32000)
|
||||
|
||||
struct ns_rpc_request {
|
||||
struct json_token *message; /* Whole RPC message */
|
||||
struct json_token *id; /* Message ID */
|
||||
struct json_token *method; /* Method name */
|
||||
struct json_token *params; /* Method params */
|
||||
};
|
||||
|
||||
struct ns_rpc_reply {
|
||||
struct json_token *message; /* Whole RPC message */
|
||||
struct json_token *id; /* Message ID */
|
||||
struct json_token *result; /* Remote call result */
|
||||
};
|
||||
|
||||
struct ns_rpc_error {
|
||||
struct json_token *message; /* Whole RPC message */
|
||||
struct json_token *id; /* Message ID */
|
||||
struct json_token *error_code; /* error.code */
|
||||
struct json_token *error_message; /* error.message */
|
||||
struct json_token *error_data; /* error.data, can be NULL */
|
||||
};
|
||||
|
||||
int ns_rpc_parse_request(const char *buf, int len, struct ns_rpc_request *req);
|
||||
|
||||
int ns_rpc_parse_reply(const char *buf, int len,
|
||||
struct json_token *toks, int max_toks,
|
||||
struct ns_rpc_reply *, struct ns_rpc_error *);
|
||||
|
||||
int ns_rpc_create_request(char *, int, const char *method,
|
||||
const char *id, const char *params_fmt, ...);
|
||||
|
||||
int ns_rpc_create_reply(char *, int, const struct ns_rpc_request *req,
|
||||
const char *result_fmt, ...);
|
||||
|
||||
int ns_rpc_create_error(char *, int, struct ns_rpc_request *req,
|
||||
int, const char *, const char *, ...);
|
||||
|
||||
int ns_rpc_create_std_error(char *, int, struct ns_rpc_request *, int code);
|
||||
|
||||
typedef int (*ns_rpc_handler_t)(char *buf, int len, struct ns_rpc_request *);
|
||||
int ns_rpc_dispatch(const char *buf, int, char *dst, int dst_len,
|
||||
const char **methods, ns_rpc_handler_t *handlers);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* NS_JSON_RPC_HEADER_DEFINED */
|
|
@ -0,0 +1,141 @@
|
|||
/* Copyright(c) By Steve Reid <steve@edmweb.com> */
|
||||
/* 100% Public Domain */
|
||||
|
||||
#ifndef NS_DISABLE_SHA1
|
||||
|
||||
#include <string.h>
|
||||
#include "sha1.h"
|
||||
|
||||
static int is_big_endian(void) {
|
||||
static const int n = 1;
|
||||
return ((char *) &n)[0] == 0;
|
||||
}
|
||||
|
||||
#define SHA1HANDSOFF
|
||||
#if defined(__sun)
|
||||
#include "solarisfixes.h"
|
||||
#endif
|
||||
|
||||
union char64long16 { unsigned char c[64]; uint32_t l[16]; };
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
static uint32_t blk0(union char64long16 *block, int i) {
|
||||
/* Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN */
|
||||
if (!is_big_endian()) {
|
||||
block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
|
||||
(rol(block->l[i], 8) & 0x00FF00FF);
|
||||
}
|
||||
return block->l[i];
|
||||
}
|
||||
|
||||
/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */
|
||||
#undef blk
|
||||
#undef R0
|
||||
#undef R1
|
||||
#undef R2
|
||||
#undef R3
|
||||
#undef R4
|
||||
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) {
|
||||
uint32_t a, b, c, d, e;
|
||||
union char64long16 block[1];
|
||||
|
||||
memcpy(block, buffer, 64);
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
/* Erase working structures. The order of operations is important,
|
||||
* used to ensure that compiler doesn't optimize those out. */
|
||||
memset(block, 0, sizeof(block));
|
||||
a = b = c = d = e = 0;
|
||||
(void) a; (void) b; (void) c; (void) d; (void) e;
|
||||
}
|
||||
|
||||
void SHA1Init(SHA1_CTX *context) {
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
void SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32_t len) {
|
||||
uint32_t i, j;
|
||||
|
||||
j = context->count[0];
|
||||
if ((context->count[0] += len << 3) < j)
|
||||
context->count[1]++;
|
||||
context->count[1] += (len>>29);
|
||||
j = (j >> 3) & 63;
|
||||
if ((j + len) > 63) {
|
||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1Transform(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64) {
|
||||
SHA1Transform(context->state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else i = 0;
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX *context) {
|
||||
unsigned i;
|
||||
unsigned char finalcount[8], c;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
c = 0200;
|
||||
SHA1Update(context, &c, 1);
|
||||
while ((context->count[0] & 504) != 448) {
|
||||
c = 0000;
|
||||
SHA1Update(context, &c, 1);
|
||||
}
|
||||
SHA1Update(context, finalcount, 8);
|
||||
for (i = 0; i < 20; i++) {
|
||||
digest[i] = (unsigned char)
|
||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
memset(context, '\0', sizeof(*context));
|
||||
memset(&finalcount, '\0', sizeof(finalcount));
|
||||
}
|
||||
#endif /* NS_DISABLE_SHA1 */
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#if !defined(NS_SHA1_HEADER_INCLUDED) && !defined(NS_DISABLE_SHA1)
|
||||
#define NS_SHA1_HEADER_INCLUDED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct {
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
unsigned char buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
void SHA1Init(SHA1_CTX *);
|
||||
void SHA1Update(SHA1_CTX *, const unsigned char *data, uint32_t len);
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* NS_SHA1_HEADER_INCLUDED */
|
|
@ -0,0 +1,957 @@
|
|||
/* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* This software is dual-licensed: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. For the terms of this
|
||||
* license, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* You are free to use this software under the terms of the GNU General
|
||||
* Public License, 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.
|
||||
*
|
||||
* Alternatively, you can license this software under a commercial
|
||||
* license, as set out in <http://cesanta.com/>.
|
||||
*
|
||||
* $Date: 2014-09-28 05:04:41 UTC $
|
||||
*/
|
||||
|
||||
#include "net_skeleton.h"
|
||||
|
||||
#ifndef NS_MALLOC
|
||||
#define NS_MALLOC malloc
|
||||
#endif
|
||||
|
||||
#ifndef NS_REALLOC
|
||||
#define NS_REALLOC realloc
|
||||
#endif
|
||||
|
||||
#ifndef NS_FREE
|
||||
#define NS_FREE free
|
||||
#endif
|
||||
|
||||
#define NS_UDP_RECEIVE_BUFFER_SIZE 2000
|
||||
#define NS_VPRINTF_BUFFER_SIZE 500
|
||||
|
||||
struct ctl_msg {
|
||||
ns_event_handler_t callback;
|
||||
char message[1024 * 8];
|
||||
};
|
||||
|
||||
void iobuf_resize(struct iobuf *io, size_t new_size) {
|
||||
char *p;
|
||||
if ((new_size > io->size || (new_size < io->size && new_size >= io->len)) &&
|
||||
(p = (char *) NS_REALLOC(io->buf, new_size)) != NULL) {
|
||||
io->size = new_size;
|
||||
io->buf = p;
|
||||
}
|
||||
}
|
||||
|
||||
void iobuf_init(struct iobuf *iobuf, size_t initial_size) {
|
||||
iobuf->len = iobuf->size = 0;
|
||||
iobuf->buf = NULL;
|
||||
iobuf_resize(iobuf, initial_size);
|
||||
}
|
||||
|
||||
void iobuf_free(struct iobuf *iobuf) {
|
||||
if (iobuf != NULL) {
|
||||
NS_FREE(iobuf->buf);
|
||||
iobuf_init(iobuf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
size_t iobuf_append(struct iobuf *io, const void *buf, size_t len) {
|
||||
char *p = NULL;
|
||||
|
||||
assert(io != NULL);
|
||||
assert(io->len <= io->size);
|
||||
|
||||
if (len <= 0) {
|
||||
} else if (io->len + len <= io->size) {
|
||||
memcpy(io->buf + io->len, buf, len);
|
||||
io->len += len;
|
||||
} else if ((p = (char *) NS_REALLOC(io->buf, io->len + len)) != NULL) {
|
||||
io->buf = p;
|
||||
memcpy(io->buf + io->len, buf, len);
|
||||
io->len += len;
|
||||
io->size = io->len;
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void iobuf_remove(struct iobuf *io, size_t n) {
|
||||
if (n > 0 && n <= io->len) {
|
||||
memmove(io->buf, io->buf + n, io->len - n);
|
||||
io->len -= n;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) {
|
||||
if (nc->flags & NSF_UDP) {
|
||||
long n = sendto(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin));
|
||||
DBG(("%p %d send %ld (%d %s)", nc, nc->sock, n, errno, strerror(errno)));
|
||||
return n < 0 ? 0 : n;
|
||||
} else {
|
||||
return iobuf_append(&nc->send_iobuf, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NS_DISABLE_THREADS
|
||||
void *ns_start_thread(void *(*f)(void *), void *p) {
|
||||
#ifdef _WIN32
|
||||
return (void *) _beginthread((void (__cdecl *)(void *)) f, 0, p);
|
||||
#else
|
||||
pthread_t thread_id = (pthread_t) 0;
|
||||
pthread_attr_t attr;
|
||||
|
||||
(void) pthread_attr_init(&attr);
|
||||
(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
#if defined(NS_STACK_SIZE) && NS_STACK_SIZE > 1
|
||||
(void) pthread_attr_setstacksize(&attr, NS_STACK_SIZE);
|
||||
#endif
|
||||
|
||||
pthread_create(&thread_id, &attr, f, p);
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
return (void *) thread_id;
|
||||
#endif
|
||||
}
|
||||
#endif /* NS_DISABLE_THREADS */
|
||||
|
||||
static void ns_add_conn(struct ns_mgr *mgr, struct ns_connection *c) {
|
||||
c->next = mgr->active_connections;
|
||||
mgr->active_connections = c;
|
||||
c->prev = NULL;
|
||||
if (c->next != NULL) c->next->prev = c;
|
||||
}
|
||||
|
||||
static void ns_remove_conn(struct ns_connection *conn) {
|
||||
if (conn->prev == NULL) conn->mgr->active_connections = conn->next;
|
||||
if (conn->prev) conn->prev->next = conn->next;
|
||||
if (conn->next) conn->next->prev = conn->prev;
|
||||
}
|
||||
|
||||
/* Print message to buffer. If buffer is large enough to hold the message,
|
||||
* return buffer. If buffer is to small, allocate large enough buffer on heap,
|
||||
* and return allocated buffer. */
|
||||
int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
|
||||
va_list ap_copy;
|
||||
int len;
|
||||
|
||||
va_copy(ap_copy, ap);
|
||||
len = vsnprintf(*buf, size, fmt, ap_copy);
|
||||
va_end(ap_copy);
|
||||
|
||||
if (len < 0) {
|
||||
/* eCos and Windows are not standard-compliant and return -1 when
|
||||
* the buffer is too small. Keep allocating larger buffers until we
|
||||
* succeed or out of memory. */
|
||||
*buf = NULL;
|
||||
while (len < 0) {
|
||||
NS_FREE(*buf);
|
||||
size *= 2;
|
||||
if ((*buf = (char *) NS_MALLOC(size)) == NULL) break;
|
||||
va_copy(ap_copy, ap);
|
||||
len = vsnprintf(*buf, size, fmt, ap_copy);
|
||||
va_end(ap_copy);
|
||||
}
|
||||
} else if (len > (int) size) {
|
||||
/* Standard-compliant code path. Allocate a buffer that is large enough. */
|
||||
if ((*buf = (char *) NS_MALLOC(len + 1)) == NULL) {
|
||||
len = -1;
|
||||
} else {
|
||||
va_copy(ap_copy, ap);
|
||||
len = vsnprintf(*buf, len + 1, fmt, ap_copy);
|
||||
va_end(ap_copy);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int ns_vprintf(struct ns_connection *nc, const char *fmt, va_list ap) {
|
||||
char mem[NS_VPRINTF_BUFFER_SIZE], *buf = mem;
|
||||
int len;
|
||||
|
||||
if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
|
||||
ns_out(nc, buf, len);
|
||||
}
|
||||
if (buf != mem && buf != NULL) {
|
||||
NS_FREE(buf);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int ns_printf(struct ns_connection *conn, const char *fmt, ...) {
|
||||
int len;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
len = ns_vprintf(conn, fmt, ap);
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
static void hexdump(struct ns_connection *nc, const char *path,
|
||||
int num_bytes, int ev) {
|
||||
const struct iobuf *io = ev == NS_SEND ? &nc->send_iobuf : &nc->recv_iobuf;
|
||||
FILE *fp;
|
||||
char *buf, src[60], dst[60];
|
||||
int buf_size = num_bytes * 5 + 100;
|
||||
|
||||
if ((fp = fopen(path, "a")) != NULL) {
|
||||
ns_sock_to_str(nc->sock, src, sizeof(src), 3);
|
||||
ns_sock_to_str(nc->sock, dst, sizeof(dst), 7);
|
||||
fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) time(NULL),
|
||||
nc->user_data, src,
|
||||
ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" :
|
||||
ev == NS_ACCEPT ? "<A" : ev == NS_CONNECT ? "C>" : "XX",
|
||||
dst, num_bytes);
|
||||
if (num_bytes > 0 && (buf = (char *) NS_MALLOC(buf_size)) != NULL) {
|
||||
ns_hexdump(io->buf + (ev == NS_SEND ? 0 : io->len) -
|
||||
(ev == NS_SEND ? 0 : num_bytes), num_bytes, buf, buf_size);
|
||||
fprintf(fp, "%s", buf);
|
||||
NS_FREE(buf);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
static void ns_call(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
if (nc->mgr->hexdump_file != NULL && ev != NS_POLL) {
|
||||
int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) ev_data : 0;
|
||||
hexdump(nc, nc->mgr->hexdump_file, len, ev);
|
||||
}
|
||||
|
||||
/*
|
||||
* If protocol handler is specified, call it. Otherwise, call user-specified
|
||||
* event handler.
|
||||
*/
|
||||
(nc->proto_handler ? nc->proto_handler : nc->handler)(nc, ev, ev_data);
|
||||
}
|
||||
|
||||
static void ns_destroy_conn(struct ns_connection *conn) {
|
||||
closesocket(conn->sock);
|
||||
iobuf_free(&conn->recv_iobuf);
|
||||
iobuf_free(&conn->send_iobuf);
|
||||
#ifdef NS_ENABLE_SSL
|
||||
if (conn->ssl != NULL) {
|
||||
SSL_free(conn->ssl);
|
||||
}
|
||||
if (conn->ssl_ctx != NULL) {
|
||||
SSL_CTX_free(conn->ssl_ctx);
|
||||
}
|
||||
#endif
|
||||
NS_FREE(conn);
|
||||
}
|
||||
|
||||
static void ns_close_conn(struct ns_connection *conn) {
|
||||
DBG(("%p %d", conn, conn->flags));
|
||||
ns_call(conn, NS_CLOSE, NULL);
|
||||
ns_remove_conn(conn);
|
||||
ns_destroy_conn(conn);
|
||||
}
|
||||
|
||||
void ns_set_close_on_exec(sock_t sock) {
|
||||
#ifdef _WIN32
|
||||
(void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
|
||||
#else
|
||||
fcntl(sock, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ns_set_non_blocking_mode(sock_t sock) {
|
||||
#ifdef _WIN32
|
||||
unsigned long on = 1;
|
||||
ioctlsocket(sock, FIONBIO, &on);
|
||||
#else
|
||||
int flags = fcntl(sock, F_GETFL, 0);
|
||||
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef NS_DISABLE_SOCKETPAIR
|
||||
int ns_socketpair2(sock_t sp[2], int sock_type) {
|
||||
union socket_address sa;
|
||||
sock_t sock;
|
||||
socklen_t len = sizeof(sa.sin);
|
||||
int ret = 0;
|
||||
|
||||
sock = sp[0] = sp[1] = INVALID_SOCKET;
|
||||
|
||||
(void) memset(&sa, 0, sizeof(sa));
|
||||
sa.sin.sin_family = AF_INET;
|
||||
sa.sin.sin_port = htons(0);
|
||||
sa.sin.sin_addr.s_addr = htonl(0x7f000001);
|
||||
|
||||
if ((sock = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) {
|
||||
} else if (bind(sock, &sa.sa, len) != 0) {
|
||||
} else if (sock_type == SOCK_STREAM && listen(sock, 1) != 0) {
|
||||
} else if (getsockname(sock, &sa.sa, &len) != 0) {
|
||||
} else if ((sp[0] = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) {
|
||||
} else if (connect(sp[0], &sa.sa, len) != 0) {
|
||||
} else if (sock_type == SOCK_DGRAM &&
|
||||
(getsockname(sp[0], &sa.sa, &len) != 0 ||
|
||||
connect(sock, &sa.sa, len) != 0)) {
|
||||
} else if ((sp[1] = (sock_type == SOCK_DGRAM ? sock :
|
||||
accept(sock, &sa.sa, &len))) == INVALID_SOCKET) {
|
||||
} else {
|
||||
ns_set_close_on_exec(sp[0]);
|
||||
ns_set_close_on_exec(sp[1]);
|
||||
if (sock_type == SOCK_STREAM) closesocket(sock);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
if (sp[0] != INVALID_SOCKET) closesocket(sp[0]);
|
||||
if (sp[1] != INVALID_SOCKET) closesocket(sp[1]);
|
||||
if (sock != INVALID_SOCKET) closesocket(sock);
|
||||
sock = sp[0] = sp[1] = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ns_socketpair(sock_t sp[2]) {
|
||||
return ns_socketpair2(sp, SOCK_STREAM);
|
||||
}
|
||||
#endif /* NS_DISABLE_SOCKETPAIR */
|
||||
|
||||
/* TODO(lsm): use non-blocking resolver */
|
||||
static int ns_resolve2(const char *host, struct in_addr *ina) {
|
||||
struct hostent *he;
|
||||
if ((he = gethostbyname(host)) == NULL) {
|
||||
DBG(("gethostbyname(%s) failed: %s", host, strerror(errno)));
|
||||
} else {
|
||||
memcpy(ina, he->h_addr_list[0], sizeof(*ina));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Resolve FDQN "host", store IP address in the "ip".
|
||||
* Return > 0 (IP address length) on success. */
|
||||
int ns_resolve(const char *host, char *buf, size_t n) {
|
||||
struct in_addr ad;
|
||||
return ns_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0;
|
||||
}
|
||||
|
||||
/* Address format: [PROTO://][IP_ADDRESS:]PORT[:CERT][:CA_CERT] */
|
||||
static int ns_parse_address(const char *str, union socket_address *sa, int *p) {
|
||||
unsigned int a, b, c, d, port = 0;
|
||||
int len = 0;
|
||||
char host[200];
|
||||
#ifdef NS_ENABLE_IPV6
|
||||
char buf[100];
|
||||
#endif
|
||||
|
||||
/* MacOS needs that. If we do not zero it, subsequent bind() will fail. */
|
||||
/* Also, all-zeroes in the socket address means binding to all addresses */
|
||||
/* for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). */
|
||||
memset(sa, 0, sizeof(*sa));
|
||||
sa->sin.sin_family = AF_INET;
|
||||
|
||||
*p = SOCK_STREAM;
|
||||
|
||||
if (memcmp(str, "udp://", 6) == 0) {
|
||||
str += 6;
|
||||
*p = SOCK_DGRAM;
|
||||
} else if (memcmp(str, "tcp://", 6) == 0) {
|
||||
str += 6;
|
||||
}
|
||||
|
||||
if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
|
||||
/* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */
|
||||
sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
|
||||
sa->sin.sin_port = htons((uint16_t) port);
|
||||
#ifdef NS_ENABLE_IPV6
|
||||
} else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 &&
|
||||
inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) {
|
||||
/* IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080 */
|
||||
sa->sin6.sin6_family = AF_INET6;
|
||||
sa->sin6.sin6_port = htons((uint16_t) port);
|
||||
#endif
|
||||
} else if (sscanf(str, "%199[^ :]:%u%n", host, &port, &len) == 2) {
|
||||
sa->sin.sin_port = htons((uint16_t) port);
|
||||
ns_resolve2(host, &sa->sin.sin_addr);
|
||||
} else if (sscanf(str, "%u%n", &port, &len) == 1) {
|
||||
/* If only port is specified, bind to IPv4, INADDR_ANY */
|
||||
sa->sin.sin_port = htons((uint16_t) port);
|
||||
}
|
||||
|
||||
return port < 0xffff && str[len] == '\0' ? len : 0;
|
||||
}
|
||||
|
||||
/* 'sa' must be an initialized address to bind to */
|
||||
static sock_t ns_open_listening_socket(union socket_address *sa, int proto) {
|
||||
socklen_t sa_len = (sa->sa.sa_family == AF_INET) ?
|
||||
sizeof(sa->sin) : sizeof(sa->sin6);
|
||||
sock_t sock = INVALID_SOCKET;
|
||||
int on = 1;
|
||||
|
||||
if ((sock = socket(sa->sa.sa_family, proto, 0)) != INVALID_SOCKET &&
|
||||
|
||||
#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE)
|
||||
/* http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx */
|
||||
!setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
||||
(void *) &on, sizeof(on)) &&
|
||||
#endif
|
||||
|
||||
#if 1 || !defined(_WIN32) || defined(SO_EXCLUSIVEADDRUSE)
|
||||
/*
|
||||
* SO_RESUSEADDR is not enabled on Windows because the semantics of
|
||||
* SO_REUSEADDR on UNIX and Windows is different. On Windows,
|
||||
* SO_REUSEADDR allows to bind a socket to a port without error even if
|
||||
* the port is already open by another program. This is not the behavior
|
||||
* SO_REUSEADDR was designed for, and leads to hard-to-track failure
|
||||
* scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless
|
||||
* SO_EXCLUSIVEADDRUSE is supported and set on a socket.
|
||||
*/
|
||||
!setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) &&
|
||||
#endif
|
||||
|
||||
!bind(sock, &sa->sa, sa_len) &&
|
||||
(proto == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) {
|
||||
ns_set_non_blocking_mode(sock);
|
||||
/* In case port was set to 0, get the real port number */
|
||||
(void) getsockname(sock, &sa->sa, &sa_len);
|
||||
} else if (sock != INVALID_SOCKET) {
|
||||
closesocket(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
#ifdef NS_ENABLE_SSL
|
||||
/* Certificate generation script is at */
|
||||
/* https://github.com/cesanta/net_skeleton/blob/master/scripts/gen_certs.sh */
|
||||
|
||||
static int ns_use_ca_cert(SSL_CTX *ctx, const char *cert) {
|
||||
if (ctx == NULL) {
|
||||
return -1;
|
||||
} else if (cert == NULL || cert[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
|
||||
return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? 0 : -2;
|
||||
}
|
||||
|
||||
static int ns_use_cert(SSL_CTX *ctx, const char *pem_file) {
|
||||
if (ctx == NULL) {
|
||||
return -1;
|
||||
} else if (pem_file == NULL || pem_file[0] == '\0') {
|
||||
return 0;
|
||||
} else if (SSL_CTX_use_certificate_file(ctx, pem_file, 1) == 0 ||
|
||||
SSL_CTX_use_PrivateKey_file(ctx, pem_file, 1) == 0) {
|
||||
return -2;
|
||||
} else {
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
SSL_CTX_use_certificate_chain_file(ctx, pem_file);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char *ns_set_ssl(struct ns_connection *nc, const char *cert,
|
||||
const char *ca_cert) {
|
||||
const char *result = NULL;
|
||||
|
||||
if ((nc->flags & NSF_LISTENING) &&
|
||||
(nc->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
|
||||
result = "SSL_CTX_new() failed";
|
||||
} else if (!(nc->flags & NSF_LISTENING) &&
|
||||
(nc->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
|
||||
result = "SSL_CTX_new() failed";
|
||||
} else if (ns_use_cert(nc->ssl_ctx, cert) != 0) {
|
||||
result = "Invalid ssl cert";
|
||||
} else if (ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0) {
|
||||
result = "Invalid CA cert";
|
||||
} else if (!(nc->flags & NSF_LISTENING) &&
|
||||
(nc->ssl = SSL_new(nc->ssl_ctx)) == NULL) {
|
||||
result = "SSL_new() failed";
|
||||
} else if (!(nc->flags & NSF_LISTENING)) {
|
||||
SSL_set_fd(nc->ssl, nc->sock);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ns_ssl_err(struct ns_connection *conn, int res) {
|
||||
int ssl_err = SSL_get_error(conn->ssl, res);
|
||||
if (ssl_err == SSL_ERROR_WANT_READ) conn->flags |= NSF_WANT_READ;
|
||||
if (ssl_err == SSL_ERROR_WANT_WRITE) conn->flags |= NSF_WANT_WRITE;
|
||||
return ssl_err;
|
||||
}
|
||||
#endif /* NS_ENABLE_SSL */
|
||||
|
||||
struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str,
|
||||
ns_event_handler_t callback) {
|
||||
union socket_address sa;
|
||||
struct ns_connection *nc = NULL;
|
||||
int proto;
|
||||
sock_t sock;
|
||||
|
||||
ns_parse_address(str, &sa, &proto);
|
||||
if ((sock = ns_open_listening_socket(&sa, proto)) == INVALID_SOCKET) {
|
||||
DBG(("Failed to open listener: %d", errno));
|
||||
} else if ((nc = ns_add_sock(srv, sock, callback)) == NULL) {
|
||||
DBG(("Failed to ns_add_sock"));
|
||||
closesocket(sock);
|
||||
} else {
|
||||
nc->sa = sa;
|
||||
nc->flags |= NSF_LISTENING;
|
||||
nc->handler = callback;
|
||||
|
||||
if (proto == SOCK_DGRAM) {
|
||||
nc->flags |= NSF_UDP;
|
||||
}
|
||||
|
||||
DBG(("%p sock %d/%d", nc, sock, proto));
|
||||
}
|
||||
|
||||
return nc;
|
||||
}
|
||||
|
||||
static struct ns_connection *accept_conn(struct ns_connection *ls) {
|
||||
struct ns_connection *c = NULL;
|
||||
union socket_address sa;
|
||||
socklen_t len = sizeof(sa);
|
||||
sock_t sock = INVALID_SOCKET;
|
||||
|
||||
/* NOTE(lsm): on Windows, sock is always > FD_SETSIZE */
|
||||
if ((sock = accept(ls->sock, &sa.sa, &len)) == INVALID_SOCKET) {
|
||||
} else if ((c = ns_add_sock(ls->mgr, sock, ls->handler)) == NULL) {
|
||||
closesocket(sock);
|
||||
#ifdef NS_ENABLE_SSL
|
||||
} else if (ls->ssl_ctx != NULL &&
|
||||
((c->ssl = SSL_new(ls->ssl_ctx)) == NULL ||
|
||||
SSL_set_fd(c->ssl, sock) != 1)) {
|
||||
DBG(("SSL error"));
|
||||
ns_close_conn(c);
|
||||
c = NULL;
|
||||
#endif
|
||||
} else {
|
||||
c->listener = ls;
|
||||
c->proto_data = ls->proto_data;
|
||||
c->proto_handler = ls->proto_handler;
|
||||
c->user_data = ls->user_data;
|
||||
ns_call(c, NS_ACCEPT, &sa);
|
||||
DBG(("%p %d %p %p", c, c->sock, c->ssl_ctx, c->ssl));
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static int ns_is_error(int n) {
|
||||
return n == 0 ||
|
||||
(n < 0 && errno != EINTR && errno != EINPROGRESS &&
|
||||
errno != EAGAIN && errno != EWOULDBLOCK
|
||||
#ifdef _WIN32
|
||||
&& WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags) {
|
||||
union socket_address sa;
|
||||
socklen_t slen = sizeof(sa);
|
||||
|
||||
if (buf != NULL && len > 0) {
|
||||
buf[0] = '\0';
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
if (flags & 4) {
|
||||
getpeername(sock, &sa.sa, &slen);
|
||||
} else {
|
||||
getsockname(sock, &sa.sa, &slen);
|
||||
}
|
||||
if (flags & 1) {
|
||||
#if defined(NS_ENABLE_IPV6)
|
||||
inet_ntop(sa.sa.sa_family, sa.sa.sa_family == AF_INET ?
|
||||
(void *) &sa.sin.sin_addr :
|
||||
(void *) &sa.sin6.sin6_addr, buf, len);
|
||||
#elif defined(_WIN32)
|
||||
/* Only Windoze Vista (and newer) have inet_ntop() */
|
||||
strncpy(buf, inet_ntoa(sa.sin.sin_addr), len);
|
||||
#else
|
||||
inet_ntop(sa.sa.sa_family, (void *) &sa.sin.sin_addr, buf,(socklen_t)len);
|
||||
#endif
|
||||
}
|
||||
if (flags & 2) {
|
||||
snprintf(buf + strlen(buf), len - (strlen(buf) + 1), "%s%d",
|
||||
flags & 1 ? ":" : "", (int) ntohs(sa.sin.sin_port));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ns_hexdump(const void *buf, int len, char *dst, int dst_len) {
|
||||
const unsigned char *p = (const unsigned char *) buf;
|
||||
char ascii[17] = "";
|
||||
int i, idx, n = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
idx = i % 16;
|
||||
if (idx == 0) {
|
||||
if (i > 0) n += snprintf(dst + n, dst_len - n, " %s\n", ascii);
|
||||
n += snprintf(dst + n, dst_len - n, "%04x ", i);
|
||||
}
|
||||
n += snprintf(dst + n, dst_len - n, " %02x", p[i]);
|
||||
ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i];
|
||||
ascii[idx + 1] = '\0';
|
||||
}
|
||||
|
||||
while (i++ % 16) n += snprintf(dst + n, dst_len - n, "%s", " ");
|
||||
n += snprintf(dst + n, dst_len - n, " %s\n\n", ascii);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void ns_read_from_socket(struct ns_connection *conn) {
|
||||
char buf[2048];
|
||||
int n = 0;
|
||||
|
||||
if (conn->flags & NSF_CONNECTING) {
|
||||
int ok = 1, ret;
|
||||
socklen_t len = sizeof(ok);
|
||||
|
||||
ret = getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &ok, &len);
|
||||
#ifdef NS_ENABLE_SSL
|
||||
if (ret == 0 && ok == 0 && conn->ssl != NULL) {
|
||||
int res = SSL_connect(conn->ssl);
|
||||
int ssl_err = ns_ssl_err(conn, res);
|
||||
if (res == 1) {
|
||||
conn->flags |= NSF_SSL_HANDSHAKE_DONE;
|
||||
} else if (ssl_err == SSL_ERROR_WANT_READ ||
|
||||
ssl_err == SSL_ERROR_WANT_WRITE) {
|
||||
return; /* Call us again */
|
||||
} else {
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
conn->flags &= ~NSF_CONNECTING;
|
||||
DBG(("%p ok=%d", conn, ok));
|
||||
if (ok != 0) {
|
||||
conn->flags |= NSF_CLOSE_IMMEDIATELY;
|
||||
}
|
||||
ns_call(conn, NS_CONNECT, &ok);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef NS_ENABLE_SSL
|
||||
if (conn->ssl != NULL) {
|
||||
if (conn->flags & NSF_SSL_HANDSHAKE_DONE) {
|
||||
/* SSL library may have more bytes ready to read then we ask to read.
|
||||
* Therefore, read in a loop until we read everything. Without the loop,
|
||||
* we skip to the next select() cycle which can just timeout. */
|
||||
while ((n = SSL_read(conn->ssl, buf, sizeof(buf))) > 0) {
|
||||
DBG(("%p %d <- %d bytes (SSL)", conn, conn->flags, n));
|
||||
iobuf_append(&conn->recv_iobuf, buf, n);
|
||||
ns_call(conn, NS_RECV, &n);
|
||||
}
|
||||
ns_ssl_err(conn, n);
|
||||
} else {
|
||||
int res = SSL_accept(conn->ssl);
|
||||
int ssl_err = ns_ssl_err(conn, res);
|
||||
if (res == 1) {
|
||||
conn->flags |= NSF_SSL_HANDSHAKE_DONE;
|
||||
} else if (ssl_err == SSL_ERROR_WANT_READ ||
|
||||
ssl_err == SSL_ERROR_WANT_WRITE) {
|
||||
return; /* Call us again */
|
||||
} else {
|
||||
conn->flags |= NSF_CLOSE_IMMEDIATELY;
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
while ((n = (int) recv(conn->sock, buf, sizeof(buf), 0)) > 0) {
|
||||
DBG(("%p %d <- %d bytes (PLAIN)", conn, conn->flags, n));
|
||||
iobuf_append(&conn->recv_iobuf, buf, n);
|
||||
ns_call(conn, NS_RECV, &n);
|
||||
}
|
||||
}
|
||||
|
||||
if (ns_is_error(n)) {
|
||||
conn->flags |= NSF_CLOSE_IMMEDIATELY;
|
||||
}
|
||||
}
|
||||
|
||||
static void ns_write_to_socket(struct ns_connection *conn) {
|
||||
struct iobuf *io = &conn->send_iobuf;
|
||||
int n = 0;
|
||||
|
||||
#ifdef NS_ENABLE_SSL
|
||||
if (conn->ssl != NULL) {
|
||||
n = SSL_write(conn->ssl, io->buf, io->len);
|
||||
if (n <= 0) {
|
||||
int ssl_err = ns_ssl_err(conn, n);
|
||||
if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) {
|
||||
return; /* Call us again */
|
||||
} else {
|
||||
conn->flags |= NSF_CLOSE_IMMEDIATELY;
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{ n = (int) send(conn->sock, io->buf, io->len, 0); }
|
||||
|
||||
DBG(("%p %d -> %d bytes", conn, conn->flags, n));
|
||||
|
||||
ns_call(conn, NS_SEND, &n);
|
||||
if (ns_is_error(n)) {
|
||||
conn->flags |= NSF_CLOSE_IMMEDIATELY;
|
||||
} else if (n > 0) {
|
||||
iobuf_remove(io, n);
|
||||
}
|
||||
}
|
||||
|
||||
int ns_send(struct ns_connection *conn, const void *buf, int len) {
|
||||
return (int) ns_out(conn, buf, len);
|
||||
}
|
||||
|
||||
static void ns_handle_udp(struct ns_connection *ls) {
|
||||
struct ns_connection nc;
|
||||
char buf[NS_UDP_RECEIVE_BUFFER_SIZE];
|
||||
int n;
|
||||
socklen_t s_len = sizeof(nc.sa);
|
||||
|
||||
memset(&nc, 0, sizeof(nc));
|
||||
n = recvfrom(ls->sock, buf, sizeof(buf), 0, &nc.sa.sa, &s_len);
|
||||
if (n <= 0) {
|
||||
DBG(("%p recvfrom: %s", ls, strerror(errno)));
|
||||
} else {
|
||||
nc.mgr = ls->mgr;
|
||||
nc.recv_iobuf.buf = buf;
|
||||
nc.recv_iobuf.len = nc.recv_iobuf.size = n;
|
||||
nc.sock = ls->sock;
|
||||
nc.handler = ls->handler;
|
||||
nc.user_data = ls->user_data;
|
||||
nc.proto_data = ls->proto_data;
|
||||
nc.proto_handler = ls->proto_handler;
|
||||
nc.mgr = ls->mgr;
|
||||
nc.listener = ls;
|
||||
nc.flags = NSF_UDP;
|
||||
DBG(("%p %d bytes received", ls, n));
|
||||
ns_call(&nc, NS_RECV, &n);
|
||||
}
|
||||
}
|
||||
|
||||
static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
|
||||
if (sock != INVALID_SOCKET) {
|
||||
FD_SET(sock, set);
|
||||
if (*max_fd == INVALID_SOCKET || sock > *max_fd) {
|
||||
*max_fd = sock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time_t ns_mgr_poll(struct ns_mgr *mgr, int milli) {
|
||||
struct ns_connection *nc, *tmp;
|
||||
struct timeval tv;
|
||||
fd_set read_set, write_set, err_set;
|
||||
sock_t max_fd = INVALID_SOCKET;
|
||||
time_t current_time = time(NULL);
|
||||
|
||||
FD_ZERO(&read_set);
|
||||
FD_ZERO(&write_set);
|
||||
FD_ZERO(&err_set);
|
||||
ns_add_to_set(mgr->ctl[1], &read_set, &max_fd);
|
||||
|
||||
for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
|
||||
tmp = nc->next;
|
||||
if (!(nc->flags & (NSF_LISTENING | NSF_CONNECTING))) {
|
||||
ns_call(nc, NS_POLL, ¤t_time);
|
||||
}
|
||||
if (!(nc->flags & NSF_WANT_WRITE)) {
|
||||
/*DBG(("%p read_set", nc)); */
|
||||
ns_add_to_set(nc->sock, &read_set, &max_fd);
|
||||
}
|
||||
if (((nc->flags & NSF_CONNECTING) && !(nc->flags & NSF_WANT_READ)) ||
|
||||
(nc->send_iobuf.len > 0 && !(nc->flags & NSF_CONNECTING) &&
|
||||
!(nc->flags & NSF_BUFFER_BUT_DONT_SEND))) {
|
||||
/*DBG(("%p write_set", nc)); */
|
||||
ns_add_to_set(nc->sock, &write_set, &max_fd);
|
||||
ns_add_to_set(nc->sock, &err_set, &max_fd);
|
||||
}
|
||||
if (nc->flags & NSF_CLOSE_IMMEDIATELY) {
|
||||
ns_close_conn(nc);
|
||||
}
|
||||
}
|
||||
|
||||
tv.tv_sec = milli / 1000;
|
||||
tv.tv_usec = (milli % 1000) * 1000;
|
||||
|
||||
if (select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv) > 0) {
|
||||
/* select() might have been waiting for a long time, reset current_time
|
||||
* now to prevent last_io_time being set to the past. */
|
||||
current_time = time(NULL);
|
||||
|
||||
/* Read wakeup messages */
|
||||
if (mgr->ctl[1] != INVALID_SOCKET &&
|
||||
FD_ISSET(mgr->ctl[1], &read_set)) {
|
||||
struct ctl_msg ctl_msg;
|
||||
int len = (int) recv(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
|
||||
send(mgr->ctl[1], ctl_msg.message, 1, 0);
|
||||
if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) {
|
||||
struct ns_connection *c;
|
||||
for (c = ns_next(mgr, NULL); c != NULL; c = ns_next(mgr, c)) {
|
||||
ctl_msg.callback(c, NS_POLL, ctl_msg.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
|
||||
tmp = nc->next;
|
||||
|
||||
/* Windows reports failed connect() requests in err_set */
|
||||
if (FD_ISSET(nc->sock, &err_set) && (nc->flags & NSF_CONNECTING)) {
|
||||
nc->last_io_time = current_time;
|
||||
ns_read_from_socket(nc);
|
||||
}
|
||||
|
||||
if (FD_ISSET(nc->sock, &read_set)) {
|
||||
nc->last_io_time = current_time;
|
||||
if (nc->flags & NSF_LISTENING) {
|
||||
if (nc->flags & NSF_UDP) {
|
||||
ns_handle_udp(nc);
|
||||
} else {
|
||||
/* We're not looping here, and accepting just one connection at
|
||||
* a time. The reason is that eCos does not respect non-blocking
|
||||
* flag on a listening socket and hangs in a loop. */
|
||||
accept_conn(nc);
|
||||
}
|
||||
} else {
|
||||
ns_read_from_socket(nc);
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(nc->sock, &write_set)) {
|
||||
nc->last_io_time = current_time;
|
||||
if (nc->flags & NSF_CONNECTING) {
|
||||
ns_read_from_socket(nc);
|
||||
} else if (!(nc->flags & NSF_BUFFER_BUT_DONT_SEND)) {
|
||||
ns_write_to_socket(nc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
|
||||
tmp = nc->next;
|
||||
if ((nc->flags & NSF_CLOSE_IMMEDIATELY) ||
|
||||
(nc->send_iobuf.len == 0 &&
|
||||
(nc->flags & NSF_FINISHED_SENDING_DATA))) {
|
||||
ns_close_conn(nc);
|
||||
}
|
||||
}
|
||||
|
||||
return current_time;
|
||||
}
|
||||
|
||||
struct ns_connection *ns_connect(struct ns_mgr *mgr, const char *address,
|
||||
ns_event_handler_t callback) {
|
||||
sock_t sock = INVALID_SOCKET;
|
||||
struct ns_connection *nc = NULL;
|
||||
union socket_address sa;
|
||||
int rc, proto;
|
||||
|
||||
ns_parse_address(address, &sa, &proto);
|
||||
if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) {
|
||||
return NULL;
|
||||
}
|
||||
ns_set_non_blocking_mode(sock);
|
||||
rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa.sa, sizeof(sa.sin));
|
||||
|
||||
if (rc != 0 && ns_is_error(rc)) {
|
||||
closesocket(sock);
|
||||
return NULL;
|
||||
} else if ((nc = ns_add_sock(mgr, sock, callback)) == NULL) {
|
||||
closesocket(sock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nc->sa = sa; /* Important, cause UDP conns will use sendto() */
|
||||
nc->flags = (proto == SOCK_DGRAM) ? NSF_UDP : NSF_CONNECTING;
|
||||
|
||||
return nc;
|
||||
}
|
||||
|
||||
struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock,
|
||||
ns_event_handler_t callback) {
|
||||
struct ns_connection *conn;
|
||||
if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) {
|
||||
memset(conn, 0, sizeof(*conn));
|
||||
ns_set_non_blocking_mode(sock);
|
||||
ns_set_close_on_exec(sock);
|
||||
conn->sock = sock;
|
||||
conn->handler = callback;
|
||||
conn->mgr = s;
|
||||
conn->last_io_time = time(NULL);
|
||||
ns_add_conn(s, conn);
|
||||
DBG(("%p %d", conn, sock));
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
struct ns_connection *ns_next(struct ns_mgr *s, struct ns_connection *conn) {
|
||||
return conn == NULL ? s->active_connections : conn->next;
|
||||
}
|
||||
|
||||
void ns_broadcast(struct ns_mgr *mgr, ns_event_handler_t cb,void *data, size_t len) {
|
||||
struct ctl_msg ctl_msg;
|
||||
if (mgr->ctl[0] != INVALID_SOCKET && data != NULL &&
|
||||
len < sizeof(ctl_msg.message)) {
|
||||
ctl_msg.callback = cb;
|
||||
memcpy(ctl_msg.message, data, len);
|
||||
send(mgr->ctl[0], (char *) &ctl_msg,
|
||||
offsetof(struct ctl_msg, message) + len, 0);
|
||||
recv(mgr->ctl[0], (char *) &len, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ns_mgr_init(struct ns_mgr *s, void *user_data) {
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
|
||||
s->user_data = user_data;
|
||||
|
||||
#ifdef _WIN32
|
||||
{ WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); }
|
||||
#else
|
||||
/* Ignore SIGPIPE signal, so if client cancels the request, it
|
||||
* won't kill the whole process. */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
#ifndef NS_DISABLE_SOCKETPAIR
|
||||
do {
|
||||
ns_socketpair2(s->ctl, SOCK_DGRAM);
|
||||
} while (s->ctl[0] == INVALID_SOCKET);
|
||||
#endif
|
||||
|
||||
#ifdef NS_ENABLE_SSL
|
||||
{static int init_done; if (!init_done) { SSL_library_init(); init_done++; }}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ns_mgr_free(struct ns_mgr *s) {
|
||||
struct ns_connection *conn, *tmp_conn;
|
||||
|
||||
DBG(("%p", s));
|
||||
if (s == NULL) return;
|
||||
/* Do one last poll, see https://github.com/cesanta/mongoose/issues/286 */
|
||||
ns_mgr_poll(s, 0);
|
||||
|
||||
if (s->ctl[0] != INVALID_SOCKET) closesocket(s->ctl[0]);
|
||||
if (s->ctl[1] != INVALID_SOCKET) closesocket(s->ctl[1]);
|
||||
s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
|
||||
|
||||
for (conn = s->active_connections; conn != NULL; conn = tmp_conn) {
|
||||
tmp_conn = conn->next;
|
||||
ns_close_conn(conn);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
* This software is dual-licensed: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. For the terms of this
|
||||
* license, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* You are free to use this software under the terms of the GNU General
|
||||
* Public License, 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.
|
||||
*
|
||||
* Alternatively, you can license this software under a commercial
|
||||
* license, as set out in <http://cesanta.com/>.
|
||||
*/
|
||||
|
||||
#ifndef NS_SKELETON_HEADER_INCLUDED
|
||||
#define NS_SKELETON_HEADER_INCLUDED
|
||||
|
||||
#define NS_SKELETON_VERSION "2.2.0"
|
||||
|
||||
#undef UNICODE /* Use ANSI WinAPI functions */
|
||||
#undef _UNICODE /* Use multibyte encoding on Windows */
|
||||
#define _MBCS /* Use multibyte encoding on Windows */
|
||||
#define _INTEGRAL_MAX_BITS 64 /* Enable _stati64() on Windows */
|
||||
#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */
|
||||
#undef WIN32_LEAN_AND_MEAN /* Let windows.h always include winsock2.h */
|
||||
#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */
|
||||
#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */
|
||||
#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */
|
||||
#ifndef _LARGEFILE_SOURCE
|
||||
#define _LARGEFILE_SOURCE /* Enable fseeko() and ftello() functions */
|
||||
#endif
|
||||
#define _FILE_OFFSET_BITS 64 /* Enable 64-bit file offsets */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable : 4127) /* FD_SET() emits warning, disable it */
|
||||
#pragma warning (disable : 4204) /* missing c99 support */
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifndef va_copy
|
||||
#ifdef __va_copy
|
||||
#define va_copy __va_copy
|
||||
#else
|
||||
#define va_copy(x,y) (x) = (y)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#ifndef EINPROGRESS
|
||||
#define EINPROGRESS WSAEINPROGRESS
|
||||
#endif
|
||||
#ifndef EWOULDBLOCK
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#endif
|
||||
#ifndef __func__
|
||||
#define STRX(x) #x
|
||||
#define STR(x) STRX(x)
|
||||
#define __func__ __FILE__ ":" STR(__LINE__)
|
||||
#endif
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define sleep(x) Sleep((x) * 1000)
|
||||
#define to64(x) _atoi64(x)
|
||||
typedef int socklen_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef SOCKET sock_t;
|
||||
#ifdef __MINGW32__
|
||||
typedef struct stat ns_stat_t;
|
||||
#else
|
||||
typedef struct _stati64 ns_stat_t;
|
||||
#endif
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
||||
#endif
|
||||
#else /* not _WIN32 */
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h> /* For inet_pton() when NS_ENABLE_IPV6 is defined */
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#define closesocket(x) close(x)
|
||||
#define __cdecl
|
||||
#define INVALID_SOCKET (-1)
|
||||
#ifdef __APPLE__
|
||||
int64_t strtoll(const char * str, char ** endptr, int base);
|
||||
#endif
|
||||
#define to64(x) strtoll(x, NULL, 10)
|
||||
typedef int sock_t;
|
||||
typedef struct stat ns_stat_t;
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#ifdef NS_ENABLE_DEBUG
|
||||
#define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \
|
||||
fflush(stdout); } while(0)
|
||||
#else
|
||||
#define DBG(x)
|
||||
#endif
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
|
||||
#endif
|
||||
|
||||
#ifdef NS_ENABLE_SSL
|
||||
#ifdef __APPLE__
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
#include <openssl/ssl.h>
|
||||
#else
|
||||
typedef void *SSL;
|
||||
typedef void *SSL_CTX;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
union socket_address {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_in sin;
|
||||
#ifdef NS_ENABLE_IPV6
|
||||
struct sockaddr_in6 sin6;
|
||||
#else
|
||||
struct sockaddr sin6;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Describes chunk of memory */
|
||||
struct ns_str {
|
||||
const char *p;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/* IO buffers interface */
|
||||
struct iobuf {
|
||||
char *buf;
|
||||
size_t len;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
void iobuf_init(struct iobuf *, size_t initial_size);
|
||||
void iobuf_free(struct iobuf *);
|
||||
size_t iobuf_append(struct iobuf *, const void *data, size_t data_size);
|
||||
void iobuf_remove(struct iobuf *, size_t data_size);
|
||||
void iobuf_resize(struct iobuf *, size_t new_size);
|
||||
|
||||
/* Callback function (event handler) prototype, must be defined by user. */
|
||||
/* Net skeleton will call event handler, passing events defined above. */
|
||||
struct ns_connection;
|
||||
typedef void (*ns_event_handler_t)(struct ns_connection *, int ev, void *);
|
||||
|
||||
/* Events. Meaning of event parameter (evp) is given in the comment. */
|
||||
#define NS_POLL 0 /* Sent to each connection on each call to ns_mgr_poll() */
|
||||
#define NS_ACCEPT 1 /* New connection accept()-ed. union socket_address *addr */
|
||||
#define NS_CONNECT 2 /* connect() succeeded or failed. int *success_status */
|
||||
#define NS_RECV 3 /* Data has benn received. int *num_bytes */
|
||||
#define NS_SEND 4 /* Data has been written to a socket. int *num_bytes */
|
||||
#define NS_CLOSE 5 /* Connection is closed. NULL */
|
||||
|
||||
struct ns_mgr {
|
||||
struct ns_connection *active_connections;
|
||||
const char *hexdump_file; /* Debug hexdump file path */
|
||||
sock_t ctl[2]; /* Socketpair for mg_wakeup() */
|
||||
void *user_data; /* User data */
|
||||
};
|
||||
|
||||
struct ns_connection {
|
||||
struct ns_connection *next, *prev; /* ns_mgr::active_connections linkage */
|
||||
struct ns_connection *listener; /* Set only for accept()-ed connections */
|
||||
struct ns_mgr *mgr;
|
||||
|
||||
sock_t sock; /* Socket */
|
||||
union socket_address sa; /* Peer address */
|
||||
struct iobuf recv_iobuf; /* Received data */
|
||||
struct iobuf send_iobuf; /* Data scheduled for sending */
|
||||
SSL *ssl;
|
||||
SSL_CTX *ssl_ctx;
|
||||
time_t last_io_time; /* Timestamp of the last socket IO */
|
||||
ns_event_handler_t proto_handler; /* Protocol-specific event handler */
|
||||
void *proto_data; /* Protocol-specific data */
|
||||
ns_event_handler_t handler; /* Event handler function */
|
||||
void *user_data; /* User-specific data */
|
||||
|
||||
unsigned long flags;
|
||||
#define NSF_FINISHED_SENDING_DATA (1 << 0)
|
||||
#define NSF_BUFFER_BUT_DONT_SEND (1 << 1)
|
||||
#define NSF_SSL_HANDSHAKE_DONE (1 << 2)
|
||||
#define NSF_CONNECTING (1 << 3)
|
||||
#define NSF_CLOSE_IMMEDIATELY (1 << 4)
|
||||
#define NSF_WANT_READ (1 << 5)
|
||||
#define NSF_WANT_WRITE (1 << 6) /* NOTE(lsm): proto-specific */
|
||||
#define NSF_LISTENING (1 << 7) /* NOTE(lsm): proto-specific */
|
||||
#define NSF_UDP (1 << 8)
|
||||
#define NSF_IS_WEBSOCKET (1 << 9) /* NOTE(lsm): proto-specific */
|
||||
#define NSF_WEBSOCKET_NO_DEFRAG (1 << 10) /* NOTE(lsm): proto-specific */
|
||||
|
||||
#define NSF_USER_1 (1 << 20)
|
||||
#define NSF_USER_2 (1 << 21)
|
||||
#define NSF_USER_3 (1 << 22)
|
||||
#define NSF_USER_4 (1 << 23)
|
||||
#define NSF_USER_5 (1 << 24)
|
||||
#define NSF_USER_6 (1 << 25)
|
||||
};
|
||||
|
||||
void ns_mgr_init(struct ns_mgr *, void *user_data);
|
||||
void ns_mgr_free(struct ns_mgr *);
|
||||
time_t ns_mgr_poll(struct ns_mgr *, int milli);
|
||||
void ns_broadcast(struct ns_mgr *, ns_event_handler_t, void *, size_t);
|
||||
|
||||
struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *);
|
||||
struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t, ns_event_handler_t);
|
||||
struct ns_connection *ns_bind(struct ns_mgr *, const char *, ns_event_handler_t);
|
||||
struct ns_connection *ns_connect(struct ns_mgr *, const char *, ns_event_handler_t);
|
||||
const char *ns_set_ssl(struct ns_connection *nc, const char *, const char *);
|
||||
|
||||
int ns_send(struct ns_connection *, const void *buf, int len);
|
||||
int ns_printf(struct ns_connection *, const char *fmt, ...);
|
||||
int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap);
|
||||
|
||||
/* Utility functions */
|
||||
void *ns_start_thread(void *(*f)(void *), void *p);
|
||||
int ns_socketpair(sock_t [2]);
|
||||
int ns_socketpair2(sock_t [2], int sock_type); /* SOCK_STREAM or SOCK_DGRAM */
|
||||
void ns_set_close_on_exec(sock_t);
|
||||
void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
|
||||
int ns_hexdump(const void *buf, int len, char *dst, int dst_len);
|
||||
int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
|
||||
int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* NS_SKELETON_HEADER_INCLUDED */
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#include "net_skeleton.h"
|
||||
#include "util.h"
|
||||
|
||||
const char *ns_skip(const char *s, const char *end,
|
||||
const char *delims, struct ns_str *v) {
|
||||
v->p = s;
|
||||
while (s < end && strchr(delims, * (unsigned char *) s) == NULL) s++;
|
||||
v->len = s - v->p;
|
||||
while (s < end && strchr(delims, * (unsigned char *) s) != NULL) s++;
|
||||
return s;
|
||||
}
|
||||
|
||||
static int lowercase(const char *s) {
|
||||
return tolower(* (const unsigned char *) s);
|
||||
}
|
||||
|
||||
int ns_ncasecmp(const char *s1, const char *s2, size_t len) {
|
||||
int diff = 0;
|
||||
|
||||
if (len > 0)
|
||||
do {
|
||||
diff = lowercase(s1++) - lowercase(s2++);
|
||||
} while (diff == 0 && s1[-1] != '\0' && --len > 0);
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
int ns_vcasecmp(const struct ns_str *str2, const char *str1) {
|
||||
size_t n1 = strlen(str1), n2 = str2->len;
|
||||
return n1 == n2 ? ns_ncasecmp(str1, str2->p, n1) : n1 > n2 ? 1 : -1;
|
||||
}
|
||||
|
||||
int ns_vcmp(const struct ns_str *str2, const char *str1) {
|
||||
size_t n1 = strlen(str1), n2 = str2->len;
|
||||
return n1 == n2 ? memcmp(str1, str2->p, n2) : n1 > n2 ? 1 : -1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
|
||||
char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p;
|
||||
|
||||
strncpy(buf, path, sizeof(buf));
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
|
||||
/* Trim trailing slashes. Leave backslash for paths like "X:\" */
|
||||
p = buf + strlen(buf) - 1;
|
||||
while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0';
|
||||
|
||||
/*
|
||||
* Convert to Unicode and back. If doubly-converted string does not
|
||||
* match the original, something is fishy, reject.
|
||||
*/
|
||||
memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
|
||||
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
|
||||
NULL, NULL);
|
||||
if (strcmp(buf, buf2) != 0) {
|
||||
wbuf[0] = L'\0';
|
||||
}
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
int ns_stat(const char *path, ns_stat_t *st) {
|
||||
#ifdef _WIN32
|
||||
wchar_t wpath[MAX_PATH_SIZE];
|
||||
to_wchar(path, wpath, ARRAY_SIZE(wpath));
|
||||
DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st)));
|
||||
return _wstati64(wpath, st);
|
||||
#else
|
||||
return stat(path, st);
|
||||
#endif
|
||||
}
|
||||
|
||||
FILE *ns_fopen(const char *path, const char *mode) {
|
||||
#ifdef _WIN32
|
||||
wchar_t wpath[MAX_PATH_SIZE], wmode[10];
|
||||
to_wchar(path, wpath, ARRAY_SIZE(wpath));
|
||||
to_wchar(mode, wmode, ARRAY_SIZE(wmode));
|
||||
return _wfopen(wpath, wmode);
|
||||
#else
|
||||
return fopen(path, mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ns_open(const char *path, int flag, int mode) {
|
||||
#ifdef _WIN32
|
||||
wchar_t wpath[MAX_PATH_SIZE];
|
||||
to_wchar(path, wpath, ARRAY_SIZE(wpath));
|
||||
return _wopen(wpath, flag, mode);
|
||||
#else
|
||||
return open(path, flag, mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ns_base64_encode(const unsigned char *src, int src_len, char *dst) {
|
||||
static const char *b64 =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
int i, j, a, b, c;
|
||||
|
||||
for (i = j = 0; i < src_len; i += 3) {
|
||||
a = src[i];
|
||||
b = i + 1 >= src_len ? 0 : src[i + 1];
|
||||
c = i + 2 >= src_len ? 0 : src[i + 2];
|
||||
|
||||
dst[j++] = b64[a >> 2];
|
||||
dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
|
||||
if (i + 1 < src_len) {
|
||||
dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
|
||||
}
|
||||
if (i + 2 < src_len) {
|
||||
dst[j++] = b64[c & 63];
|
||||
}
|
||||
}
|
||||
while (j % 4 != 0) {
|
||||
dst[j++] = '=';
|
||||
}
|
||||
dst[j++] = '\0';
|
||||
}
|
||||
|
||||
/* Convert one byte of encoded base64 input stream to 6-bit chunk */
|
||||
static unsigned char from_b64(unsigned char ch) {
|
||||
/* Inverse lookup map */
|
||||
static const unsigned char tab[128] = {
|
||||
255, 255, 255, 255, 255, 255, 255, 255, /* 0 */
|
||||
255, 255, 255, 255, 255, 255, 255, 255, /* 8 */
|
||||
255, 255, 255, 255, 255, 255, 255, 255, /* 16 */
|
||||
255, 255, 255, 255, 255, 255, 255, 255, /* 24 */
|
||||
255, 255, 255, 255, 255, 255, 255, 255, /* 32 */
|
||||
255, 255, 255, 62, 255, 255, 255, 63, /* 40 */
|
||||
52, 53, 54, 55, 56, 57, 58, 59, /* 48 */
|
||||
60, 61, 255, 255, 255, 200, 255, 255, /* 56 '=' is 200, on index 61 */
|
||||
255, 0, 1, 2, 3, 4, 5, 6, /* 64 */
|
||||
7, 8, 9, 10, 11, 12, 13, 14, /* 72 */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, /* 80 */
|
||||
23, 24, 25, 255, 255, 255, 255, 255, /* 88 */
|
||||
255, 26, 27, 28, 29, 30, 31, 32, /* 96 */
|
||||
33, 34, 35, 36, 37, 38, 39, 40, /* 104 */
|
||||
41, 42, 43, 44, 45, 46, 47, 48, /* 112 */
|
||||
49, 50, 51, 255, 255, 255, 255, 255, /* 120 */
|
||||
};
|
||||
return tab[ch & 127];
|
||||
}
|
||||
|
||||
void ns_base64_decode(const unsigned char *s, int len, char *dst) {
|
||||
unsigned char a, b, c, d;
|
||||
while (len >= 4 &&
|
||||
(a = from_b64(s[0])) != 255 &&
|
||||
(b = from_b64(s[1])) != 255 &&
|
||||
(c = from_b64(s[2])) != 255 &&
|
||||
(d = from_b64(s[3])) != 255) {
|
||||
if (a == 200 || b == 200) break; /* '=' can't be there */
|
||||
*dst++ = a << 2 | b >> 4;
|
||||
if (c == 200) break;
|
||||
*dst++ = b << 4 | c >> 2;
|
||||
if (d == 200) break;
|
||||
*dst++ = c << 6 | d;
|
||||
s += 4;
|
||||
len -=4;
|
||||
}
|
||||
*dst = 0;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef NS_UTIL_HEADER_DEFINED
|
||||
#define NS_UTIL_HEADER_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifndef MAX_PATH_SIZE
|
||||
#define MAX_PATH_SIZE 500
|
||||
#endif
|
||||
|
||||
const char *ns_skip(const char *, const char *, const char *, struct ns_str *);
|
||||
int ns_ncasecmp(const char *s1, const char *s2, size_t len);
|
||||
int ns_vcmp(const struct ns_str *str2, const char *str1);
|
||||
int ns_vcasecmp(const struct ns_str *str2, const char *str1);
|
||||
void ns_base64_decode(const unsigned char *s, int len, char *dst);
|
||||
void ns_base64_encode(const unsigned char *src, int src_len, char *dst);
|
||||
int ns_stat(const char *path, ns_stat_t *st);
|
||||
FILE *ns_fopen(const char *path, const char *mode);
|
||||
int ns_open(const char *path, int flag, int mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* NS_UTIL_HEADER_DEFINED */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,537 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
* This software is dual-licensed: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. For the terms of this
|
||||
* license, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* You are free to use this software under the terms of the GNU General
|
||||
* Public License, 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.
|
||||
*
|
||||
* Alternatively, you can license this software under a commercial
|
||||
* license, as set out in <http://cesanta.com/>.
|
||||
*/
|
||||
|
||||
#ifndef NS_SKELETON_HEADER_INCLUDED
|
||||
#define NS_SKELETON_HEADER_INCLUDED
|
||||
|
||||
#define NS_SKELETON_VERSION "2.2.0"
|
||||
|
||||
#undef UNICODE /* Use ANSI WinAPI functions */
|
||||
#undef _UNICODE /* Use multibyte encoding on Windows */
|
||||
#define _MBCS /* Use multibyte encoding on Windows */
|
||||
#define _INTEGRAL_MAX_BITS 64 /* Enable _stati64() on Windows */
|
||||
#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */
|
||||
#undef WIN32_LEAN_AND_MEAN /* Let windows.h always include winsock2.h */
|
||||
#ifndef _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */
|
||||
#endif
|
||||
#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */
|
||||
#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */
|
||||
#ifndef _LARGEFILE_SOURCE
|
||||
#define _LARGEFILE_SOURCE /* Enable fseeko() and ftello() functions */
|
||||
#endif
|
||||
#define _FILE_OFFSET_BITS 64 /* Enable 64-bit file offsets */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable : 4127) /* FD_SET() emits warning, disable it */
|
||||
#pragma warning (disable : 4204) /* missing c99 support */
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifndef va_copy
|
||||
#ifdef __va_copy
|
||||
#define va_copy __va_copy
|
||||
#else
|
||||
#define va_copy(x,y) (x) = (y)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#ifndef EINPROGRESS
|
||||
#define EINPROGRESS WSAEINPROGRESS
|
||||
#endif
|
||||
#ifndef EWOULDBLOCK
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#endif
|
||||
#ifndef __func__
|
||||
#define STRX(x) #x
|
||||
#define STR(x) STRX(x)
|
||||
#define __func__ __FILE__ ":" STR(__LINE__)
|
||||
#endif
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define sleep(x) Sleep((x) * 1000)
|
||||
#define to64(x) _atoi64(x)
|
||||
typedef int socklen_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef SOCKET sock_t;
|
||||
#ifdef __MINGW32__
|
||||
typedef struct stat ns_stat_t;
|
||||
#else
|
||||
typedef struct _stati64 ns_stat_t;
|
||||
#endif
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
||||
#endif
|
||||
#else /* not _WIN32 */
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h> /* For inet_pton() when NS_ENABLE_IPV6 is defined */
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#ifndef closesocket
|
||||
#define closesocket(x) close(x)
|
||||
#endif
|
||||
#define __cdecl
|
||||
#ifndef INVALID_SOCKET
|
||||
#define INVALID_SOCKET (-1)
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
int64_t strtoll(const char * str, char ** endptr, int base);
|
||||
#endif
|
||||
#define to64(x) strtoll(x, NULL, 10)
|
||||
typedef int sock_t;
|
||||
typedef struct stat ns_stat_t;
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#ifdef NS_ENABLE_DEBUG
|
||||
#define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \
|
||||
fflush(stdout); } while(0)
|
||||
#else
|
||||
#define DBG(x)
|
||||
#endif
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
|
||||
#endif
|
||||
|
||||
#ifdef NS_ENABLE_SSL
|
||||
#ifdef __APPLE__
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
#include <openssl/ssl.h>
|
||||
#else
|
||||
typedef void *SSL;
|
||||
typedef void *SSL_CTX;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
union socket_address {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_in sin;
|
||||
#ifdef NS_ENABLE_IPV6
|
||||
struct sockaddr_in6 sin6;
|
||||
#else
|
||||
struct sockaddr sin6;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Describes chunk of memory */
|
||||
struct ns_str {
|
||||
const char *p;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/* IO buffers interface */
|
||||
struct iobuf {
|
||||
char *buf;
|
||||
size_t len;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
void iobuf_init(struct iobuf *, size_t initial_size);
|
||||
void iobuf_free(struct iobuf *);
|
||||
size_t iobuf_append(struct iobuf *, const void *data, size_t data_size);
|
||||
void iobuf_remove(struct iobuf *, size_t data_size);
|
||||
void iobuf_resize(struct iobuf *, size_t new_size);
|
||||
|
||||
/* Callback function (event handler) prototype, must be defined by user. */
|
||||
/* Net skeleton will call event handler, passing events defined above. */
|
||||
struct ns_connection;
|
||||
typedef void (*ns_event_handler_t)(struct ns_connection *, int ev, void *);
|
||||
|
||||
/* Events. Meaning of event parameter (evp) is given in the comment. */
|
||||
#define NS_POLL 0 /* Sent to each connection on each call to ns_mgr_poll() */
|
||||
#define NS_ACCEPT 1 /* New connection accept()-ed. union socket_address *addr */
|
||||
#define NS_CONNECT 2 /* connect() succeeded or failed. int *success_status */
|
||||
#define NS_RECV 3 /* Data has benn received. int *num_bytes */
|
||||
#define NS_SEND 4 /* Data has been written to a socket. int *num_bytes */
|
||||
#define NS_CLOSE 5 /* Connection is closed. NULL */
|
||||
|
||||
struct ns_mgr {
|
||||
struct ns_connection *active_connections;
|
||||
const char *hexdump_file; /* Debug hexdump file path */
|
||||
sock_t ctl[2]; /* Socketpair for mg_wakeup() */
|
||||
void *user_data; /* User data */
|
||||
};
|
||||
|
||||
struct ns_connection {
|
||||
struct ns_connection *next, *prev; /* ns_mgr::active_connections linkage */
|
||||
struct ns_connection *listener; /* Set only for accept()-ed connections */
|
||||
struct ns_mgr *mgr;
|
||||
|
||||
sock_t sock; /* Socket */
|
||||
union socket_address sa; /* Peer address */
|
||||
struct iobuf recv_iobuf; /* Received data */
|
||||
struct iobuf send_iobuf; /* Data scheduled for sending */
|
||||
SSL *ssl;
|
||||
SSL_CTX *ssl_ctx;
|
||||
time_t last_io_time; /* Timestamp of the last socket IO */
|
||||
ns_event_handler_t proto_handler; /* Protocol-specific event handler */
|
||||
void *proto_data; /* Protocol-specific data */
|
||||
ns_event_handler_t handler; /* Event handler function */
|
||||
void *user_data; /* User-specific data */
|
||||
|
||||
unsigned long flags;
|
||||
#define NSF_FINISHED_SENDING_DATA (1 << 0)
|
||||
#define NSF_BUFFER_BUT_DONT_SEND (1 << 1)
|
||||
#define NSF_SSL_HANDSHAKE_DONE (1 << 2)
|
||||
#define NSF_CONNECTING (1 << 3)
|
||||
#define NSF_CLOSE_IMMEDIATELY (1 << 4)
|
||||
#define NSF_WANT_READ (1 << 5)
|
||||
#define NSF_WANT_WRITE (1 << 6) /* NOTE(lsm): proto-specific */
|
||||
#define NSF_LISTENING (1 << 7) /* NOTE(lsm): proto-specific */
|
||||
#define NSF_UDP (1 << 8)
|
||||
#define NSF_IS_WEBSOCKET (1 << 9) /* NOTE(lsm): proto-specific */
|
||||
#define NSF_WEBSOCKET_NO_DEFRAG (1 << 10) /* NOTE(lsm): proto-specific */
|
||||
|
||||
#define NSF_USER_1 (1 << 20)
|
||||
#define NSF_USER_2 (1 << 21)
|
||||
#define NSF_USER_3 (1 << 22)
|
||||
#define NSF_USER_4 (1 << 23)
|
||||
#define NSF_USER_5 (1 << 24)
|
||||
#define NSF_USER_6 (1 << 25)
|
||||
};
|
||||
|
||||
void ns_mgr_init(struct ns_mgr *, void *user_data);
|
||||
void ns_mgr_free(struct ns_mgr *);
|
||||
time_t ns_mgr_poll(struct ns_mgr *, int milli);
|
||||
void ns_broadcast(struct ns_mgr *, ns_event_handler_t, void *, size_t);
|
||||
|
||||
struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *);
|
||||
struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t, ns_event_handler_t);
|
||||
struct ns_connection *ns_bind(struct ns_mgr *, const char *, ns_event_handler_t);
|
||||
struct ns_connection *ns_connect(struct ns_mgr *, const char *, ns_event_handler_t);
|
||||
const char *ns_set_ssl(struct ns_connection *nc, const char *, const char *);
|
||||
|
||||
int ns_send(struct ns_connection *, const void *buf, int len);
|
||||
int ns_printf(struct ns_connection *, const char *fmt, ...);
|
||||
int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap);
|
||||
|
||||
/* Utility functions */
|
||||
void *ns_start_thread(void *(*f)(void *), void *p);
|
||||
int ns_socketpair(sock_t [2]);
|
||||
int ns_socketpair2(sock_t [2], int sock_type); /* SOCK_STREAM or SOCK_DGRAM */
|
||||
void ns_set_close_on_exec(sock_t);
|
||||
void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
|
||||
int ns_hexdump(const void *buf, int len, char *dst, int dst_len);
|
||||
int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
|
||||
int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* NS_SKELETON_HEADER_INCLUDED */
|
||||
/*
|
||||
* Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
* Copyright (c) 2013 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* This library is dual-licensed: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. For the terms of this
|
||||
* license, see <http: *www.gnu.org/licenses/>.
|
||||
*
|
||||
* You are free to use this library under the terms of the GNU General
|
||||
* Public License, 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.
|
||||
*
|
||||
* Alternatively, you can license this library under a commercial
|
||||
* license, as set out in <http://cesanta.com/products.html>.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_HEADER_INCLUDED
|
||||
#define FROZEN_HEADER_INCLUDED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
enum json_type {
|
||||
JSON_TYPE_EOF = 0, /* End of parsed tokens marker */
|
||||
JSON_TYPE_STRING = 1,
|
||||
JSON_TYPE_NUMBER = 2,
|
||||
JSON_TYPE_OBJECT = 3,
|
||||
JSON_TYPE_TRUE = 4,
|
||||
JSON_TYPE_FALSE = 5,
|
||||
JSON_TYPE_NULL = 6,
|
||||
JSON_TYPE_ARRAY = 7
|
||||
};
|
||||
|
||||
struct json_token {
|
||||
const char *ptr; /* Points to the beginning of the token */
|
||||
int len; /* Token length */
|
||||
int num_desc; /* For arrays and object, total number of descendants */
|
||||
enum json_type type; /* Type of the token, possible values above */
|
||||
};
|
||||
|
||||
/* Error codes */
|
||||
#define JSON_STRING_INVALID -1
|
||||
#define JSON_STRING_INCOMPLETE -2
|
||||
#define JSON_TOKEN_ARRAY_TOO_SMALL -3
|
||||
|
||||
int parse_json(const char *json_string, int json_string_length,
|
||||
struct json_token *tokens_array, int size_of_tokens_array);
|
||||
struct json_token *parse_json2(const char *json_string, int string_length);
|
||||
struct json_token *find_json_token(struct json_token *toks, const char *path);
|
||||
|
||||
int json_emit_long(char *buf, int buf_len, long value);
|
||||
int json_emit_double(char *buf, int buf_len, double value);
|
||||
int json_emit_quoted_str(char *buf, int buf_len, const char *str, int len);
|
||||
int json_emit_unquoted_str(char *buf, int buf_len, const char *str, int len);
|
||||
int json_emit(char *buf, int buf_len, const char *fmt, ...);
|
||||
int json_emit_va(char *buf, int buf_len, const char *fmt, va_list);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* FROZEN_HEADER_INCLUDED */
|
||||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#if !defined(NS_SHA1_HEADER_INCLUDED) && !defined(NS_DISABLE_SHA1)
|
||||
#define NS_SHA1_HEADER_INCLUDED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct {
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
unsigned char buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
void SHA1Init(SHA1_CTX *);
|
||||
void SHA1Update(SHA1_CTX *, const unsigned char *data, uint32_t len);
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* NS_SHA1_HEADER_INCLUDED */
|
||||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef NS_UTIL_HEADER_DEFINED
|
||||
#define NS_UTIL_HEADER_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifndef MAX_PATH_SIZE
|
||||
#define MAX_PATH_SIZE 500
|
||||
#endif
|
||||
|
||||
const char *ns_skip(const char *, const char *, const char *, struct ns_str *);
|
||||
int ns_ncasecmp(const char *s1, const char *s2, size_t len);
|
||||
int ns_vcmp(const struct ns_str *str2, const char *str1);
|
||||
int ns_vcasecmp(const struct ns_str *str2, const char *str1);
|
||||
void ns_base64_decode(const unsigned char *s, int len, char *dst);
|
||||
void ns_base64_encode(const unsigned char *src, int src_len, char *dst);
|
||||
int ns_stat(const char *path, ns_stat_t *st);
|
||||
FILE *ns_fopen(const char *path, const char *mode);
|
||||
int ns_open(const char *path, int flag, int mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* NS_UTIL_HEADER_DEFINED */
|
||||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef NS_HTTP_HEADER_DEFINED
|
||||
#define NS_HTTP_HEADER_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define NS_MAX_HTTP_HEADERS 40
|
||||
#define NS_MAX_HTTP_REQUEST_SIZE 8192
|
||||
#define NS_MAX_PATH 1024
|
||||
|
||||
struct http_message {
|
||||
struct ns_str message; /* Whole message: request line + headers + body */
|
||||
|
||||
/* HTTP Request line (or HTTP response line) */
|
||||
struct ns_str method; /* "GET" */
|
||||
struct ns_str uri; /* "/my_file.html" */
|
||||
struct ns_str proto; /* "HTTP/1.1" */
|
||||
|
||||
/* Headers */
|
||||
struct ns_str header_names[NS_MAX_HTTP_HEADERS];
|
||||
struct ns_str header_values[NS_MAX_HTTP_HEADERS];
|
||||
|
||||
/* Message body */
|
||||
struct ns_str body; /* Zero-length for requests with no body */
|
||||
};
|
||||
|
||||
struct websocket_message {
|
||||
unsigned char *data;
|
||||
size_t size;
|
||||
unsigned char flags;
|
||||
};
|
||||
|
||||
/* HTTP and websocket events. void *ev_data is described in a comment. */
|
||||
#define NS_HTTP_REQUEST 100 /* struct http_message * */
|
||||
#define NS_HTTP_REPLY 101 /* struct http_message * */
|
||||
|
||||
#define NS_WEBSOCKET_HANDSHAKE_REQUEST 111 /* NULL */
|
||||
#define NS_WEBSOCKET_HANDSHAKE_DONE 112 /* NULL */
|
||||
#define NS_WEBSOCKET_FRAME 113 /* struct websocket_message * */
|
||||
|
||||
void ns_set_protocol_http_websocket(struct ns_connection *);
|
||||
void ns_send_websocket_handshake(struct ns_connection *, const char *,
|
||||
const char *);
|
||||
void ns_send_websocket_frame(struct ns_connection *, int, const void *, size_t);
|
||||
void ns_send_websocket_framev(struct ns_connection *, int, const struct ns_str *, int);
|
||||
|
||||
void ns_printf_websocket_frame(struct ns_connection *, int, const char *, ...);
|
||||
|
||||
/* Websocket opcodes, from http://tools.ietf.org/html/rfc6455 */
|
||||
#define WEBSOCKET_OP_CONTINUE 0
|
||||
#define WEBSOCKET_OP_TEXT 1
|
||||
#define WEBSOCKET_OP_BINARY 2
|
||||
#define WEBSOCKET_OP_CLOSE 8
|
||||
#define WEBSOCKET_OP_PING 9
|
||||
#define WEBSOCKET_OP_PONG 10
|
||||
|
||||
/* Utility functions */
|
||||
struct ns_str *ns_get_http_header(struct http_message *, const char *);
|
||||
int ns_parse_http(const char *s, int n, struct http_message *req);
|
||||
int ns_get_http_var(const struct ns_str *, const char *, char *dst, size_t);
|
||||
|
||||
|
||||
struct ns_serve_http_opts {
|
||||
const char *document_root;
|
||||
};
|
||||
void ns_serve_http(struct ns_connection *, struct http_message *,
|
||||
struct ns_serve_http_opts);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* NS_HTTP_HEADER_DEFINED */
|
||||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef NS_JSON_RPC_HEADER_DEFINED
|
||||
#define NS_JSON_RPC_HEADER_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* JSON-RPC standard error codes */
|
||||
#define JSON_RPC_PARSE_ERROR (-32700)
|
||||
#define JSON_RPC_INVALID_REQUEST_ERROR (-32600)
|
||||
#define JSON_RPC_METHOD_NOT_FOUND_ERROR (-32601)
|
||||
#define JSON_RPC_INVALID_PARAMS_ERROR (-32602)
|
||||
#define JSON_RPC_INTERNAL_ERROR (-32603)
|
||||
#define JSON_RPC_SERVER_ERROR (-32000)
|
||||
|
||||
struct ns_rpc_request {
|
||||
struct json_token *message; /* Whole RPC message */
|
||||
struct json_token *id; /* Message ID */
|
||||
struct json_token *method; /* Method name */
|
||||
struct json_token *params; /* Method params */
|
||||
};
|
||||
|
||||
struct ns_rpc_reply {
|
||||
struct json_token *message; /* Whole RPC message */
|
||||
struct json_token *id; /* Message ID */
|
||||
struct json_token *result; /* Remote call result */
|
||||
};
|
||||
|
||||
struct ns_rpc_error {
|
||||
struct json_token *message; /* Whole RPC message */
|
||||
struct json_token *id; /* Message ID */
|
||||
struct json_token *error_code; /* error.code */
|
||||
struct json_token *error_message; /* error.message */
|
||||
struct json_token *error_data; /* error.data, can be NULL */
|
||||
};
|
||||
|
||||
int ns_rpc_parse_request(const char *buf, int len, struct ns_rpc_request *req);
|
||||
|
||||
int ns_rpc_parse_reply(const char *buf, int len,
|
||||
struct json_token *toks, int max_toks,
|
||||
struct ns_rpc_reply *, struct ns_rpc_error *);
|
||||
|
||||
int ns_rpc_create_request(char *, int, const char *method,
|
||||
const char *id, const char *params_fmt, ...);
|
||||
|
||||
int ns_rpc_create_reply(char *, int, const struct ns_rpc_request *req,
|
||||
const char *result_fmt, ...);
|
||||
|
||||
int ns_rpc_create_error(char *, int, struct ns_rpc_request *req,
|
||||
int, const char *, const char *, ...);
|
||||
|
||||
int ns_rpc_create_std_error(char *, int, struct ns_rpc_request *, int code);
|
||||
|
||||
typedef int (*ns_rpc_handler_t)(char *buf, int len, struct ns_rpc_request *);
|
||||
int ns_rpc_dispatch(const char *buf, int, char *dst, int dst_len,
|
||||
const char **methods, ns_rpc_handler_t *handlers);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* NS_JSON_RPC_HEADER_DEFINED */
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# This script embeds net_skeleton directly into the source file.
|
||||
# The source file must have a placeholder for the net_skeleton code,
|
||||
# two following lines:
|
||||
|
||||
# // net_skeleton start
|
||||
# // net_skeleton end
|
||||
#
|
||||
# Net skeleton code will be inserted between those two lines.
|
||||
|
||||
if ! test -f "$1" ; then
|
||||
echo "Usage: $0 <source_file>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
D=`dirname $0`
|
||||
|
||||
F1=$D/../net_skeleton.h
|
||||
F2=$D/../net_skeleton.c
|
||||
|
||||
sed '/#include "net_skeleton.h"/d' $F2 > /tmp/$$
|
||||
F2=/tmp/$$
|
||||
|
||||
A='\/\/ net_skeleton start'
|
||||
B='\/\/ net_skeleton end'
|
||||
|
||||
sed -i .$$.bak -e "/$A/,/$B/ { /$A/{ n; r $F1" -e "r $F2" -e "}; /$B/!d; }" "$1"
|
|
@ -0,0 +1,40 @@
|
|||
#!/bin/sh
|
||||
# This script generates self-signed certificates: CA, client and server.
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
# Change these if needed
|
||||
BITS=2048
|
||||
DAYS=3650
|
||||
|
||||
if test -z "$1" ; then
|
||||
echo "Usage: ./$0 <domain_name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MY_DOMAIN="$1"
|
||||
|
||||
CAS="/CN=$MY_DOMAIN/O=$MY_DOMAIN/C=IE/L=Dublin"
|
||||
SUBJ=${SUBJ:="/CN=$MY_DOMAIN/O=$MY_DOMAIN/C=IE/L=Galway"}
|
||||
SERIAL=$(date +%s)
|
||||
|
||||
echo $SERIAL > ca.srl
|
||||
openssl genrsa -out ca.key $BITS
|
||||
openssl req -new -x509 -key ca.key -out ca.crt -nodes -days $DAYS -subj "$CAS"
|
||||
cat ca.key ca.crt > ca.pem
|
||||
|
||||
openssl genrsa -out server.key $BITS
|
||||
openssl req -key server.key -new -out server.req -days $DAYS -subj "$SUBJ"
|
||||
openssl x509 -req -in server.req -CA ca.pem -CAkey ca.pem -out server.crt -days $DAYS
|
||||
cat server.key server.crt > server.pem
|
||||
|
||||
openssl genrsa -out client.key $BITS
|
||||
openssl req -new -key client.key -out client.req -days $DAYS -subj "$SUBJ"
|
||||
openssl x509 -req -in client.req -CA ca.pem -CAkey ca.pem -out client.crt -days $DAYS
|
||||
cat client.key client.crt > client.pem
|
||||
|
||||
rm ca.{crt,key,srl} client.{crt,key,req} server.{crt,key,req}
|
||||
mv ca.pem ${MY_DOMAIN}_ca.pem
|
||||
mv client.pem ${MY_DOMAIN}_client.pem
|
||||
mv server.pem ${MY_DOMAIN}_server.pem
|
|
@ -0,0 +1,10 @@
|
|||
unit_test
|
||||
unit_test_ansi
|
||||
unit_test_c99
|
||||
unit_test.c.gcov
|
||||
unit_test.gcno
|
||||
unit_test.gcda
|
||||
*.gcov
|
||||
unit_test.dSYM
|
||||
net_skeleton.gcno
|
||||
net_skeleton.gcda
|
|
@ -0,0 +1,46 @@
|
|||
PROG = unit_test
|
||||
PROF = -fprofile-arcs -ftest-coverage -g -O0
|
||||
CFLAGS = -W -Wall -Werror -I.. -DNS_ENABLE_SSL -DNS_ENABLE_IPV6 -pthread $(PROF) $(CFLAGS_EXTRA)
|
||||
PEDANTIC=$(shell gcc --version 2>/dev/null | grep -q clang && echo -pedantic)
|
||||
SOURCES = $(PROG).c ../net_skeleton.c
|
||||
|
||||
# http://crossgcc.rts-software.org/doku.php?id=compiling_for_win32
|
||||
MINGW_GCC=/usr/local/gcc-4.8.0-qt-4.8.4-for-mingw32/win32-gcc/bin/i586-mingw32-gcc
|
||||
|
||||
all: clean ismerged $(PROG)_ansi $(PROG)_c99 $(PROG)
|
||||
|
||||
.PHONY: clean clean_coverage $(PROG) $(PROG)_ansi $(PROG)_c99 $(PROG).exe $(PROG)_mingw.exe
|
||||
|
||||
ismerged:
|
||||
$(MAKE) -C ../modules -s --no-print-directory merge_net_skeleton.c | diff ../net_skeleton.c -
|
||||
$(MAKE) -C ../modules -s --no-print-directory merge_net_skeleton.h | diff ../net_skeleton.h -
|
||||
|
||||
$(PROG): Makefile
|
||||
$(MAKE) clean_coverage
|
||||
g++ -x c++ $(SOURCES) -o $(PROG) $(CFLAGS) -lssl && ./$(PROG)
|
||||
gcov -b $(PROG).c
|
||||
|
||||
$(PROG)_ansi: Makefile
|
||||
$(MAKE) clean_coverage
|
||||
gcc $(PEDANTIC) -ansi $(SOURCES) -o $(PROG)_ansi $(CFLAGS) -lssl && ./$(PROG)_ansi
|
||||
gcov -b $(PROG).c
|
||||
|
||||
$(PROG)_c99: Makefile
|
||||
$(MAKE) clean_coverage
|
||||
gcc $(PEDANTIC) -std=c99 $(SOURCES) -o $(PROG)_c99 $(CFLAGS) -lssl && ./$(PROG)_c99
|
||||
|
||||
$(PROG)_mingw.exe: Makefile
|
||||
$(MAKE) clean_coverage
|
||||
$(MINGW_GCC) $(SOURCES) -o $(PROG)_mingw.exe -W -Wall -Werror -I..
|
||||
|
||||
$(PROG).exe:
|
||||
wine cl $(SOURCES) /MD /I.. /Zi $(CFLAGS_EXTRA) && echo "Compiled OK\n"
|
||||
|
||||
win: $(PROG).exe
|
||||
wine $(PROG).exe
|
||||
|
||||
clean: clean_coverage
|
||||
rm -rf $(PROG) $(PROG)_ansi $(PROG)_c99 *.txt *.exe *.obj *.o a.out *.pdb *.opt
|
||||
|
||||
clean_coverage:
|
||||
rm -rf *.gc* *.dSYM
|
|
@ -0,0 +1,49 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAv3/TSSi5hZDwMKG43eqe+GzR1lRMXVYt9I1Mr987v1DT99xR
|
||||
Dcpfo/3aj6B/V/G67oPz+zbVZN/ZPvvA1Z82T7ixcBFsGIXgEWzxUm1UCUf51ftl
|
||||
MlOaf24cdyegi0y8hRdkWLoC7w0vuMfrgR6cmpbI2LSDSMaXXX2qDoofQsFUYaJN
|
||||
Nn3uqRK0ixs/jzbzbAT9q2BWYwySUX4VEgADpmi0FyANDjEhmdktxQW9l6IGGzF8
|
||||
M9mA053hIgZwo+9qf9X3nfNUTWMvisMQtxm0mRYgvD53Oix08VLs6bodNTVOLQoc
|
||||
H0uH3CTs+H3Z0CkcZaAJe/kwCLFhls9ee3M0nQIDAQABAoIBAQCsADPWUi3QOg6C
|
||||
n79cE5AVsigHSlAMxYshTIjErs0LWZ4J0mk66bpdoXTd7Fp0szojYYGS8f1ZTXXj
|
||||
jFv3g7lUgZ9d+UgN/rDy9dcLIgeJDozoFZUfTthF/LC0lXMtqw7ou8n1p51a+Y0T
|
||||
ev2cS9J9R1G+0uPYSgdKgcRsqsLJQS4fu5CAk9d0aeTTl009uxcn9yfTUjwOaR5J
|
||||
PuNmunAEvhE/DGSkt5oNXo7t8Q2L3mYSM0MwKdDFqoQdZAV6TMTv22Mjb6SxOOnJ
|
||||
r5gNK2BmM6oNPWvzY0PoI0LcLgFNDWIMqIq4mg73MdzszakaNRDlOYtLAuKbTF3Q
|
||||
SDq8OkZBAoGBAOn6B5jBxSa+5GcIIeGqtiRhDMExyblt3Gk2gaw6UIZpvVDXPWWm
|
||||
r0tkGJrYecmqesN7DGmmdkyx8KONF+yzYLxSsIEGNesvVYe6PXTDZYYI57As4Z4W
|
||||
DFlCDt2FaKuMXxyOlUCiXg94z8IJBJ2ldCmmG34gBSvuFe6V5x4XE3crAoGBANGG
|
||||
P7AWw6uygfjog6B2dFT6n+9UhpyJlqwfPi5eD9V5JXtWlH6xWi3dRfuYAIafg95I
|
||||
W8/OZGHrj44gNCgYjvZHud+H3NPJPZ7lftoay5KeShBAa/pCd67OMxp1SvvONYcp
|
||||
7TSwm5s+hOJvQOpw2wg0cXnfrxGKpGLOFaRddp9XAoGAFdeXefUs2G8dl1i1AQIU
|
||||
utSsgiSJtlvBJblG5bMT7VhVqgRN4P1sg9c2TM5EoETf7PvBruMxS/uYgUwcnaYp
|
||||
M6tser7/rZLfoyoJrqrHAXo3VsT50u4v/O0jwh5AJTOXdW0CFeSSb1NR4cVBvw3B
|
||||
CFpPWrjWgsFZHsqzpqV01b0CgYEAkDft4pDowmgumlvBLlQaotuX9q6hsWHrOjKP
|
||||
JG9OSswGhq0DrWj5/5PNNe5cfk2SARChUZpo8hWoTFXSUL8GuHKKeFgWIhjkt1iU
|
||||
RiAne5ZEuIb/S9UweDwqZM3TfRtlMNIlGh1uHh+cbBfUAQsJWM5wRUk4QcTCfdgI
|
||||
gYhrvCUCgYBB6u8Q49RjrTBxWK8bcZOjVhYNrd3xrCunFVMt2QAXGGrRaXpqUMnp
|
||||
xNUmGe9vGux+s0TRguZcLEX3vX+wFyBfFKwZY9hSU7PFY/da8echpu37JasKvAov
|
||||
5+5XWI5RgF+SFVk+Q7St2BlSJa/vBAH8vtrX9Dt/hN/VSo2mAPAyMQ==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDjjCCAnagAwIBAgIJAIOEuwkahzkOMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNV
|
||||
BAMTAm5zMQswCQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1Ymxp
|
||||
bjAeFw0xNDA4MzAxOTA3NDNaFw0yNDA4MjcxOTA3NDNaMDgxCzAJBgNVBAMTAm5z
|
||||
MQswCQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjCCASIw
|
||||
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9/00kouYWQ8DChuN3qnvhs0dZU
|
||||
TF1WLfSNTK/fO79Q0/fcUQ3KX6P92o+gf1fxuu6D8/s21WTf2T77wNWfNk+4sXAR
|
||||
bBiF4BFs8VJtVAlH+dX7ZTJTmn9uHHcnoItMvIUXZFi6Au8NL7jH64EenJqWyNi0
|
||||
g0jGl119qg6KH0LBVGGiTTZ97qkStIsbP48282wE/atgVmMMklF+FRIAA6ZotBcg
|
||||
DQ4xIZnZLcUFvZeiBhsxfDPZgNOd4SIGcKPvan/V953zVE1jL4rDELcZtJkWILw+
|
||||
dzosdPFS7Om6HTU1Ti0KHB9Lh9wk7Ph92dApHGWgCXv5MAixYZbPXntzNJ0CAwEA
|
||||
AaOBmjCBlzAdBgNVHQ4EFgQUsz/nOHpjMkV8pk9dFpy41batoTcwaAYDVR0jBGEw
|
||||
X4AUsz/nOHpjMkV8pk9dFpy41batoTehPKQ6MDgxCzAJBgNVBAMTAm5zMQswCQYD
|
||||
VQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpboIJAIOEuwkahzkO
|
||||
MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEDOtAl7bgAXgcL3HRlV
|
||||
H71tkUaok589PIqsTE4d8s8tFBZ92CyWD8ZPU46HbbyJXMFoFxiN7PvCzOBlgoZM
|
||||
r80HJWZc9tSlqK0NIbIyk1aeM06+F8qB+8/vw/spIkdYzDv3avwyOrc6fFnEzbwz
|
||||
5BFFrF2G9JajRKAP5snAV9iM8I2TD4l+w75MXXl7/DBEohdMBsTeDrrXj4q4sgoB
|
||||
L/yLeCoK6inkMTU5DwcGbiqvNnZA+9T654qlAlKjPMObGGPphK5/QKcOnV7Qtdju
|
||||
DHzDsDimdVbz9G1cxXs/AI/35GD7IDTdNTtmBhkf4/tsQ7Ua80xpIowb1fFUHmo1
|
||||
UAo=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,45 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAwV5xaK7ez2/TX7vSgJ0a3YbZj2l1VQ2rMzqO1Id01xlWbF/U
|
||||
rebwhAdVtWcT9R6RaBTPDGaILkV38u77M2BxIHX4MSnR6WezoA2bGMgvt3+tq2N6
|
||||
q+xkj57vwBEqedBjscVtFkoWtsX8pKwtNlMB1NvTa8p5+BNsvpvzaDX+51+FotId
|
||||
wvieQfQYgFg36HpOtOyyIV31rZ/5+qtoce8gU6wApHxmovTnQPoduNM6fOUJCHDd
|
||||
Lz90EeBREtoTVgoWcKvQoCEwJQSBmeDZgkA8Q1OYmbYoS12tIyi8rTkseRj5BvPH
|
||||
iXfNmHFKliAjvlsml5qI44I9DoagPubTf6qR5wIDAQABAoIBACZ6VZTgH0Ql22jU
|
||||
ZhnjqUHloIsyEAABvUxvXZaa8bwPtavREfAc4UVUdFCpl0YSdBrC8URlbrnOZwT3
|
||||
WxMpILm139JgoP2R/iNeMbunsh8QkA1nuTRW0NfnZ4vPnqUou33XbFKgIY7zLMfT
|
||||
3xdNQzMJHzP20Xh03RG81J2rCPMfLScTRo2XxcSxmhhS/p2WLk6pnmMHiNgYGGwX
|
||||
gcdK5lIVjMMNxgcltC30x90v0o0GDRM8/+wua+/vfn8rr3iudv9IHzL8xIzpi6NY
|
||||
CXJ8Kxd6Jtgsr3Boj5i6Mqi3Q/Trxt+rIA4bKAFXxwcp4+GmRIJtQFFiTWXpLCPC
|
||||
tLT4CHECgYEA7iCbrGjWHJ4QsUWUGrGlw1/sQ0SIv9BdZm8RydHzpRVtQOi+YOuU
|
||||
i6raVaXWzUBKgKcs/htVjAMTiePs/yhlU/MGXivz6uTX/nrD7ISJImmK2K50hgUe
|
||||
+UBnFKmBMVaNxD9RFWPJkfmNXfW7nBkqSa9CxlBcYPuOcPtZDqRl+gkCgYEAz+HX
|
||||
8wh3SHKb1cAI+o4caclpUTpGa9/zW4k+7gOh72WCKaqxTNvBvNyZGdXc9t5ToDSf
|
||||
xxsDXWG10lcHBIGLj4QBEoSWp9I43lid5swY3mCo7CjTl+1l03IfDNaC6CYQFp5p
|
||||
ZnKlsQUwR38t/uiyZpnnicCAZjqIfJbeQ5jD6G8CgYB8ufmwQa08ihJmN/KOVNRl
|
||||
VF31EfWquqHhYHXpxx2eL23tXLszGtHQoioASIANPAqJ/oaTho+1aXsXc5oUP/1r
|
||||
DlUciFsXgswb0APFY9pMewmt2xrPg+koVvJnIS25QQO6cguvb3gKDLNeLrMY3RmI
|
||||
RNNt+nOYnMqMJSsNf1CmuQKBgQCiCZxWaCbyZcNqncFh7BvhqYlaM15o/6ulkhln
|
||||
VZWIEUugRtjk2/bry9fa94TBORNeMSbKABhjVaJwTj2+GWw7dd2QHaGBNq/1QIX0
|
||||
POq1jAqf6kLkjbttUes6CosHgYPQ3bGylXLpxO2ZDV1A8Qj+SMDd8xsilEWHN+IQ
|
||||
NqeeKQKBgQDe4c7VVG+WvRRKshTh8+tjzc9nXKE2AWgwnw729SMFZO/WqX2FPp2C
|
||||
7C99XJTVBsCBy8VzuyaojeTKkag0YL3v6UTZYUeyu0YTHGQ33WVPaqdCAo840nmG
|
||||
ttwHVqshB9c67HHiYOOFt1VmT3xW6x6yympUyRqR0L+BZ1wOS3h2vQ==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC6DCCAdACBRQJQlZlMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNVBAMTAm5zMQsw
|
||||
CQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA4
|
||||
MzAxOTA3NDRaFw0yNDA4MjcxOTA3NDRaMDgxCzAJBgNVBAMTAm5zMQswCQYDVQQK
|
||||
EwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAMFecWiu3s9v01+70oCdGt2G2Y9pdVUNqzM6jtSH
|
||||
dNcZVmxf1K3m8IQHVbVnE/UekWgUzwxmiC5Fd/Lu+zNgcSB1+DEp0elns6ANmxjI
|
||||
L7d/ratjeqvsZI+e78ARKnnQY7HFbRZKFrbF/KSsLTZTAdTb02vKefgTbL6b82g1
|
||||
/udfhaLSHcL4nkH0GIBYN+h6TrTssiFd9a2f+fqraHHvIFOsAKR8ZqL050D6HbjT
|
||||
OnzlCQhw3S8/dBHgURLaE1YKFnCr0KAhMCUEgZng2YJAPENTmJm2KEtdrSMovK05
|
||||
LHkY+Qbzx4l3zZhxSpYgI75bJpeaiOOCPQ6GoD7m03+qkecCAwEAATANBgkqhkiG
|
||||
9w0BAQUFAAOCAQEAJ+wZ/IgAF5LIu0yOfJlaFRJLunKHZENigiVjYvkTdM7NI3O2
|
||||
1AZGY4O8H5Fs3YT5ZY3vas/n6IwWTk3o/JSPXojMFo82XkbI1k2cm3oLtwgEGN3p
|
||||
s5yFsjZE3H7fQJ9wHIzESBPHFY6dwwgMsNENuAM2zkwFpbAkisKhjK+EyUCXauok
|
||||
7zJY6RVPMaNojsje4iE/SBtSOnK/9WDBAgpCznHrSChJmKs4FsU7ZTO+Dg+0vQln
|
||||
l8/yBcEGAFe0GA2D9NvZKH5IoNmitvtU9zdNDK4dzC3Q+C28IjW5jE8peDFtdGs1
|
||||
P0u4kRxmb4UH1DchgoWlZjL2lSFScJ7L4xY2aQ==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,45 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA1mONQ0hAXOL9lb15Pz4fqXXNHREsF3a7/NoMJdQDclx0+a32
|
||||
MhuHcO6R7Fhsc0mZMuzbmAFLMmIIgXPMKQBZLoA12yCBlZPyKFoWUhFrLa3gUjO6
|
||||
CZlBKqkUVEACpVrQ41ihapeeUHa0uryt3tXwMn2/853yzi1uciGYi4ULTy3yTE/n
|
||||
qRIVJLiBDSC9WNFLg26f/W4YRW7tANOk2b/W/Ws9B/n7vNDgHG7Lpd38YTpFhhXT
|
||||
n3xlt/VcczkQhW79Moh6/lY6sLg6H15EjHKHeTn8t9BRm+qYi/CvC258YF/Qz/qK
|
||||
agSsLT/3FrQ6+aQgg/Eyao0IWAql49PQNxuwPQIDAQABAoIBAQC5y3S1BnyhAyb5
|
||||
Ckd1g4U0+x5TPnqTqxanvuAgOGj0RyQo7ZYbPrhWKqrTxJ3YG8Rk2dhFF3nvo/3z
|
||||
EkOwlNi07++8g6NJ2flW9xu469eSsslg8+saPnK3Yeh4SzD/1ICLRlg9ZECTQwzF
|
||||
eJbGM2oCl/AuVIgEHmNFDdCBuT9f0b7j3/Z3aK3lKzqzBYQgZ5fd8UxT+Kn4oAuS
|
||||
cLr3lQT1s6xZOAYn7O2GvXEC+yMMbvm0a97MdwSpQez1WcE9YxtCgAWwn5EmSXlh
|
||||
296iLtbaM1wgYOykJUOUoSgijf8pUfotk4Zj/y1KPHXePgAlyGCtE1zasiYb5K+5
|
||||
LuajD++BAoGBAPpKWLNzQBwQLiFaJgt6bLOxlEUR+EnjdFePDPJtyCCCiKJiKO5c
|
||||
Z5s/FT1JDQawouhjQwYqT48hbGBPjWRHkSkzB7+cg6FVSKkQRYX2TsSFvN+KCu32
|
||||
oSgDV9cFo68v1csoZIQ41TtHC82db4OTv9MPUe3Glujnep1TOTwspAM1AoGBANtH
|
||||
i+HWKOxOm7f/R2VX1ys9UjkAK+msac512XWSLAzBs7NFnB7iJ7m3Bh3ydb1ZiTgW
|
||||
l6bIdoT8TLPYNIXJ6uohhxPU5h3v81PHqIuJMBtmHCQjq3nxeH9mOsfjOFvS1cQa
|
||||
At45F9pK/5sQpOkkaBGSv8jXUFIKBEDBErourVHpAoGAK0gSAK4sZu3xXDkfnRqF
|
||||
k6lgr3UFD5nys3V8UqvjUKPiBtqco2N9Ux5ciOWKCB8hfLg1jephKaoo+JqpI68w
|
||||
jgRSEbN6G7sIvpueuiS2yEssNyfC7hWZFrdFSFykSpYmDWSlxSuizAZkJyFTeFhj
|
||||
cpcSnuCZlhr5XB1ZJ2u8zQUCgYEAke5QgpCDFZjO+ynR+vj1gppBwRuDHfUXSUaW
|
||||
3S7VT/wNOq6F0uvRYkASuxVkFAqlToWCkYVxktlRtpKZibwyMXT0r1cNejj5Z/VF
|
||||
Du/S6zkOW2K9uN7hwW9oiSSHmlx61RI2fGvkmus0pp7yERKgi6ltJx1cH+z4nZug
|
||||
efWcdRkCgYBy+XdmsxgNZOunlSC6VZiD0Ve/VFrCtKPWUivKDAZZPKl0T/1tbTwb
|
||||
I/N4zTF82jx88rDz+6jN5nOy9qbSR5TeCy6WlBesTvXm49awr5jSK3WkcLgmO+JI
|
||||
Zr2ozCBhUG6RvVsUPp2kXEsmwZMV/e9faFAlIXeJhKum6hZmfOgodg==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC6DCCAdACBRQJQlZkMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNVBAMTAm5zMQsw
|
||||
CQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA4
|
||||
MzAxOTA3NDNaFw0yNDA4MjcxOTA3NDNaMDgxCzAJBgNVBAMTAm5zMQswCQYDVQQK
|
||||
EwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBANZjjUNIQFzi/ZW9eT8+H6l1zR0RLBd2u/zaDCXU
|
||||
A3JcdPmt9jIbh3DukexYbHNJmTLs25gBSzJiCIFzzCkAWS6ANdsggZWT8ihaFlIR
|
||||
ay2t4FIzugmZQSqpFFRAAqVa0ONYoWqXnlB2tLq8rd7V8DJ9v/Od8s4tbnIhmIuF
|
||||
C08t8kxP56kSFSS4gQ0gvVjRS4Nun/1uGEVu7QDTpNm/1v1rPQf5+7zQ4Bxuy6Xd
|
||||
/GE6RYYV0598Zbf1XHM5EIVu/TKIev5WOrC4Oh9eRIxyh3k5/LfQUZvqmIvwrwtu
|
||||
fGBf0M/6imoErC0/9xa0OvmkIIPxMmqNCFgKpePT0DcbsD0CAwEAATANBgkqhkiG
|
||||
9w0BAQUFAAOCAQEAoVXK97WA24tp3JyPBJKr28gFSUtOBNDPdY8atWaqw7PwUIIM
|
||||
qhs3BTag96tgSoaISRwRphz2LM1Cl+QlItYXySAnxPKrUsA0S6DlxnA6Hq3s2wTR
|
||||
6yIT7oDUDKcWkVQcQmuNGdfxCvZXkCih9lnQn++xHcuVn9mZmjXW2xk42ljDTZCp
|
||||
CM29betpcmuho6sFXsBhY7WjQWg7UpRZat0bOwleS4fsePebMKrnr/6cq4bVw59U
|
||||
XvhSFBlLoGMYteJ82fOYH6pUO1hiPr6ww5d819LPcJEcRpcxCdQZqIq680Kp7+GY
|
||||
0wkyOYr0gkNwWVP7IUZ0FExaQ/s54g71Kd0OgA==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,558 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
* This software is dual-licensed: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. For the terms of this
|
||||
* license, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* You are free to use this software under the terms of the GNU General
|
||||
* Public License, 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.
|
||||
*
|
||||
* Alternatively, you can license this software under a commercial
|
||||
* license, as set out in <http://cesanta.com/>.
|
||||
*/
|
||||
|
||||
#include "net_skeleton.h"
|
||||
|
||||
#define FAIL(str, line) do { \
|
||||
printf("%s:%d:1 [%s]\n", __FILE__, line, str); \
|
||||
return str; \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT(expr) do { \
|
||||
static_num_tests++; \
|
||||
if (!(expr)) FAIL(#expr, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#define RUN_TEST(test) do { const char *msg = test(); \
|
||||
if (msg) return msg; } while (0)
|
||||
|
||||
#define HTTP_PORT "45772"
|
||||
#define LOOPBACK_IP "127.0.0.1"
|
||||
#define LISTENING_ADDR LOOPBACK_IP ":" HTTP_PORT
|
||||
|
||||
static int static_num_tests = 0;
|
||||
|
||||
static const char *test_iobuf(void) {
|
||||
struct iobuf io;
|
||||
|
||||
iobuf_init(&io, 0);
|
||||
ASSERT(io.buf == NULL && io.len == 0 && io.size == 0);
|
||||
iobuf_free(&io);
|
||||
ASSERT(io.buf == NULL && io.len == 0 && io.size == 0);
|
||||
|
||||
iobuf_init(&io, 10);
|
||||
ASSERT(io.buf != NULL && io.len == 0 && io.size == 10);
|
||||
iobuf_free(&io);
|
||||
ASSERT(io.buf == NULL && io.len == 0 && io.size == 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void poll_mgr(struct ns_mgr *mgr, int num_iterations) {
|
||||
while (num_iterations-- > 0) {
|
||||
ns_mgr_poll(mgr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void eh1(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct iobuf *io = &nc->recv_iobuf;
|
||||
|
||||
switch (ev) {
|
||||
case NS_CONNECT:
|
||||
ns_printf(nc, "%d %s there", * (int *) ev_data, "hi");
|
||||
break;
|
||||
case NS_RECV:
|
||||
if (nc->listener != NULL) {
|
||||
ns_printf(nc, "%d", (int) io->len);
|
||||
iobuf_remove(io, io->len);
|
||||
} else if (io->len == 2 && memcmp(io->buf, "10", 2) == 0) {
|
||||
sprintf((char *) nc->user_data, "%s", "ok!");
|
||||
nc->flags |= NSF_CLOSE_IMMEDIATELY;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define S_PEM "server.pem"
|
||||
#define C_PEM "client.pem"
|
||||
#define CA_PEM "ca.pem"
|
||||
|
||||
static const char *test_mgr_with_ssl(int use_ssl) {
|
||||
char addr[100] = "127.0.0.1:0", ip[sizeof(addr)], buf[100] = "";
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
int port, port2;
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
/* mgr.hexdump_file = "/dev/stdout"; */
|
||||
|
||||
ASSERT((nc = ns_bind(&mgr, addr, eh1)) != NULL);
|
||||
port2 = htons(nc->sa.sin.sin_port);
|
||||
ASSERT(port2 > 0);
|
||||
if (use_ssl) {
|
||||
ASSERT(ns_set_ssl(nc, S_PEM, CA_PEM) == NULL);
|
||||
}
|
||||
|
||||
ns_sock_to_str(nc->sock, addr, sizeof(addr), 3);
|
||||
ASSERT(sscanf(addr, "%[^:]:%d", ip, &port) == 2);
|
||||
ASSERT(strcmp(ip, "127.0.0.1") == 0);
|
||||
ASSERT(port == port2);
|
||||
|
||||
ASSERT((nc = ns_connect(&mgr, addr, eh1)) != NULL);
|
||||
if (use_ssl) {
|
||||
ASSERT(ns_set_ssl(nc, C_PEM, CA_PEM) == NULL);
|
||||
}
|
||||
nc->user_data = buf;
|
||||
poll_mgr(&mgr, 50);
|
||||
|
||||
ASSERT(strcmp(buf, "ok!") == 0);
|
||||
|
||||
ns_mgr_free(&mgr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_mgr(void) {
|
||||
return test_mgr_with_ssl(0);
|
||||
}
|
||||
|
||||
#ifdef NS_ENABLE_SSL
|
||||
static const char *test_ssl(void) {
|
||||
return test_mgr_with_ssl(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *test_to64(void) {
|
||||
ASSERT(to64("0") == 0);
|
||||
ASSERT(to64("") == 0);
|
||||
ASSERT(to64("123") == 123);
|
||||
ASSERT(to64("-34") == -34);
|
||||
ASSERT(to64("3566626116") == 3566626116U);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static const char *test_parse_address(void) {
|
||||
static const char *valid[] = {
|
||||
"1", "1.2.3.4:1", "tcp://123", "udp://0.0.0.0:99", "ssl://17",
|
||||
"ssl://900:a.pem:b.pem", "ssl://1.2.3.4:9000:aa.pem",
|
||||
#if defined(NS_ENABLE_IPV6)
|
||||
"udp://[::1]:123", "[3ffe:2a00:100:7031::1]:900",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
static const int protos[] = {SOCK_STREAM, SOCK_STREAM, SOCK_STREAM,
|
||||
SOCK_DGRAM, SOCK_STREAM, SOCK_STREAM, SOCK_STREAM, SOCK_DGRAM, SOCK_STREAM};
|
||||
static const int use_ssls[] = {0, 0, 0, 0, 1, 1, 1, 0, 0};
|
||||
static const char *invalid[] = {
|
||||
"99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p", "blah://12", NULL
|
||||
};
|
||||
union socket_address sa;
|
||||
char cert[100], ca[100];
|
||||
int i, proto, use_ssl;
|
||||
|
||||
for (i = 0; valid[i] != NULL; i++) {
|
||||
ASSERT(ns_parse_address(valid[i], &sa, &proto, &use_ssl, cert, ca) != 0);
|
||||
ASSERT(proto == protos[i]);
|
||||
ASSERT(use_ssl == use_ssls[i]);
|
||||
}
|
||||
|
||||
for (i = 0; invalid[i] != NULL; i++) {
|
||||
ASSERT(ns_parse_address(invalid[i], &sa, &proto, &use_ssl, cert, ca) == 0);
|
||||
}
|
||||
ASSERT(ns_parse_address("0", &sa, &proto, &use_ssl, cert, ca) != 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int avt(char **buf, size_t buf_size, const char *fmt, ...) {
|
||||
int result;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
result = ns_avprintf(buf, buf_size, fmt, ap);
|
||||
va_end(ap);
|
||||
return result;
|
||||
}
|
||||
|
||||
static const char *test_alloc_vprintf(void) {
|
||||
char buf[5], *p = buf;
|
||||
|
||||
ASSERT(avt(&p, sizeof(buf), "%d", 123) == 3);
|
||||
ASSERT(p == buf);
|
||||
ASSERT(strcmp(p, "123") == 0);
|
||||
|
||||
ASSERT(avt(&p, sizeof(buf), "%d", 123456789) == 9);
|
||||
ASSERT(p != buf);
|
||||
ASSERT(strcmp(p, "123456789") == 0);
|
||||
free(p);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_socketpair(void) {
|
||||
sock_t sp[2];
|
||||
static const char foo[] = "hi there";
|
||||
char buf[20];
|
||||
|
||||
ASSERT(ns_socketpair2(sp, SOCK_DGRAM) == 1);
|
||||
ASSERT(sizeof(foo) < sizeof(buf));
|
||||
|
||||
/* Send string in one direction */
|
||||
ASSERT(send(sp[0], foo, sizeof(foo), 0) == sizeof(foo));
|
||||
ASSERT(recv(sp[1], buf, sizeof(buf), 0) == sizeof(foo));
|
||||
ASSERT(strcmp(buf, "hi there") == 0);
|
||||
|
||||
/* Now in opposite direction */
|
||||
ASSERT(send(sp[1], foo, sizeof(foo), 0) == sizeof(foo));
|
||||
ASSERT(recv(sp[0], buf, sizeof(buf), 0) == sizeof(foo));
|
||||
ASSERT(strcmp(buf, "hi there") == 0);
|
||||
|
||||
closesocket(sp[0]);
|
||||
closesocket(sp[1]);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void eh2(struct ns_connection *nc, int ev, void *p) {
|
||||
(void) p;
|
||||
switch (ev) {
|
||||
case NS_RECV:
|
||||
strcpy((char *) nc->user_data, nc->recv_iobuf.buf);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void *thread_func(void *param) {
|
||||
sock_t sock = * (sock_t *) param;
|
||||
send(sock, ":-)", 4, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_thread(void) {
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
sock_t sp[2];
|
||||
char buf[20];
|
||||
|
||||
ASSERT(ns_socketpair(sp) == 1);
|
||||
ns_start_thread(thread_func, &sp[1]);
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
ASSERT((nc = ns_add_sock(&mgr, sp[0], eh2)) != NULL);
|
||||
nc->user_data = buf;
|
||||
poll_mgr(&mgr, 50);
|
||||
ASSERT(strcmp(buf, ":-)") == 0);
|
||||
ns_mgr_free(&mgr);
|
||||
closesocket(sp[1]);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void eh3(struct ns_connection *nc, int ev, void *p) {
|
||||
struct iobuf *io = &nc->recv_iobuf;
|
||||
(void) p;
|
||||
|
||||
if (ev == NS_RECV) {
|
||||
memcpy((char *) nc->mgr->user_data, io->buf, io->len);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *test_udp(void) {
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
const char *address = "udp://127.0.0.1:7878";
|
||||
char buf[20] = "";
|
||||
|
||||
ns_mgr_init(&mgr, buf);
|
||||
ASSERT(ns_bind(&mgr, address, eh3) != NULL);
|
||||
ASSERT((nc = ns_connect(&mgr, address, eh3)) != NULL);
|
||||
ns_printf(nc, "%s", "boo!");
|
||||
|
||||
{ int i; for (i = 0; i < 50; i++) ns_mgr_poll(&mgr, 1); }
|
||||
ASSERT(memcmp(buf, "boo!", 4) == 0);
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_parse_http_message(void) {
|
||||
static const char *a = "GET / HTTP/1.0\n\n";
|
||||
static const char *b = "GET /blah HTTP/1.0\r\nFoo: bar \r\n\r\n";
|
||||
static const char *c = "get b c\nz: k \nb: t\nvvv\n\n xx";
|
||||
static const char *d = "a b c\nContent-Length: 21 \nb: t\nvvv\n\n";
|
||||
struct ns_str *v;
|
||||
struct http_message req;
|
||||
|
||||
ASSERT(ns_parse_http("\b23", 3, &req) == -1);
|
||||
ASSERT(ns_parse_http("get\n\n", 5, &req) == -1);
|
||||
ASSERT(ns_parse_http(a, strlen(a) - 1, &req) == 0);
|
||||
ASSERT(ns_parse_http(a, strlen(a), &req) == (int) strlen(a));
|
||||
|
||||
ASSERT(ns_parse_http(b, strlen(b), &req) == (int) strlen(b));
|
||||
ASSERT(req.header_names[0].len == 3);
|
||||
ASSERT(req.header_values[0].len == 3);
|
||||
ASSERT(req.header_names[1].p == NULL);
|
||||
|
||||
ASSERT(ns_parse_http(c, strlen(c), &req) == (int) strlen(c) - 3);
|
||||
ASSERT(req.header_names[2].p == NULL);
|
||||
ASSERT(req.header_names[0].p != NULL);
|
||||
ASSERT(req.header_names[1].p != NULL);
|
||||
ASSERT(memcmp(req.header_values[1].p, "t", 1) == 0);
|
||||
ASSERT(req.header_names[1].len == 1);
|
||||
ASSERT(req.body.len == 0);
|
||||
|
||||
ASSERT(ns_parse_http(d, strlen(d), &req) == (int) strlen(d));
|
||||
ASSERT(req.body.len == 21);
|
||||
ASSERT(req.message.len == 21 + strlen(d));
|
||||
ASSERT(ns_get_http_header(&req, "foo") == NULL);
|
||||
ASSERT((v = ns_get_http_header(&req, "contENT-Length")) != NULL);
|
||||
ASSERT(v->len == 2 && memcmp(v->p, "21", 2) == 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void cb1(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
|
||||
if (ev == NS_HTTP_REQUEST) {
|
||||
ns_printf(nc, "HTTP/1.0 200 OK\n\n[%.*s %d]",
|
||||
(int) hm->uri.len, hm->uri.p, (int) hm->body.len);
|
||||
nc->flags |= NSF_FINISHED_SENDING_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
static void cb2(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
|
||||
if (ev == NS_HTTP_REPLY) {
|
||||
memcpy(nc->user_data, hm->body.p, hm->body.len);
|
||||
nc->flags |= NSF_CLOSE_IMMEDIATELY;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *test_http(void) {
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
const char *local_addr = "127.0.0.1:7777";
|
||||
char buf[20] = "";
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
ASSERT((nc = ns_bind(&mgr, local_addr, cb1)) != NULL);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
|
||||
/* Valid HTTP request. Pass test buffer to the callback. */
|
||||
ASSERT((nc = ns_connect(&mgr, local_addr, cb2)) != NULL);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
nc->user_data = buf;
|
||||
ns_printf(nc, "%s", "POST /foo HTTP/1.0\nContent-Length: 10\n\n"
|
||||
"0123456789");
|
||||
|
||||
/* Invalid HTTP request */
|
||||
ASSERT((nc = ns_connect(&mgr, local_addr, cb2)) != NULL);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
ns_printf(nc, "%s", "bl\x03\n\n");
|
||||
poll_mgr(&mgr, 50);
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
/* Check that test buffer has been filled by the callback properly. */
|
||||
ASSERT(strcmp(buf, "[/foo 10]") == 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void cb3(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
|
||||
if (ev == NS_WEBSOCKET_FRAME) {
|
||||
const char *reply = wm->size == 2 && !memcmp(wm->data, "hi", 2) ? "A": "B";
|
||||
ns_printf_websocket_frame(nc, WEBSOCKET_OP_TEXT, "%s", reply);
|
||||
}
|
||||
}
|
||||
|
||||
static void cb4(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
|
||||
if (ev == NS_WEBSOCKET_FRAME) {
|
||||
memcpy(nc->user_data, wm->data, wm->size);
|
||||
ns_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, NULL, 0);
|
||||
} else if (ev == NS_WEBSOCKET_HANDSHAKE_DONE) {
|
||||
/* Send "hi" to server. server must reply "A". */
|
||||
ns_printf_websocket_frame(nc, WEBSOCKET_OP_TEXT, "%s", "hi");
|
||||
}
|
||||
}
|
||||
|
||||
static const char *test_websocket(void) {
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
const char *local_addr = "127.0.0.1:7778";
|
||||
char buf[20] = "";
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
/* mgr.hexdump_file = "/dev/stdout"; */
|
||||
ASSERT((nc = ns_bind(&mgr, local_addr, cb3)) != NULL);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
|
||||
/* Websocket request */
|
||||
ASSERT((nc = ns_connect(&mgr, local_addr, cb4)) != NULL);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
nc->user_data = buf;
|
||||
ns_send_websocket_handshake(nc, "/ws", NULL);
|
||||
poll_mgr(&mgr, 50);
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
/* Check that test buffer has been filled by the callback properly. */
|
||||
ASSERT(strcmp(buf, "A") == 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int rpc_sum(char *buf, int len, struct ns_rpc_request *req) {
|
||||
double sum = 0;
|
||||
int i;
|
||||
|
||||
if (req->params[0].type != JSON_TYPE_ARRAY) {
|
||||
return ns_rpc_create_std_error(buf, len, req,
|
||||
JSON_RPC_INVALID_PARAMS_ERROR);
|
||||
}
|
||||
|
||||
for (i = 0; i < req->params[0].num_desc; i++) {
|
||||
if (req->params[i + 1].type != JSON_TYPE_NUMBER) {
|
||||
return ns_rpc_create_std_error(buf, len, req,
|
||||
JSON_RPC_INVALID_PARAMS_ERROR);
|
||||
}
|
||||
sum += strtod(req->params[i + 1].ptr, NULL);
|
||||
}
|
||||
return ns_rpc_create_reply(buf, len, req, "f", sum);
|
||||
}
|
||||
|
||||
static void rpc_server(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
static const char *methods[] = { "sum", NULL };
|
||||
static ns_rpc_handler_t handlers[] = { rpc_sum, NULL };
|
||||
char buf[100];
|
||||
|
||||
switch (ev) {
|
||||
case NS_HTTP_REQUEST:
|
||||
ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf),
|
||||
methods, handlers);
|
||||
ns_printf(nc, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
|
||||
"Content-Type: application/json\r\n\r\n%s",
|
||||
(int) strlen(buf), buf);
|
||||
nc->flags |= NSF_FINISHED_SENDING_DATA;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void rpc_client(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
struct ns_rpc_reply rpc_reply;
|
||||
struct ns_rpc_error rpc_error;
|
||||
struct json_token toks[20];
|
||||
char buf[100];
|
||||
|
||||
switch (ev) {
|
||||
case NS_CONNECT:
|
||||
ns_rpc_create_request(buf, sizeof(buf), "sum", "1", "[f,f,f]",
|
||||
1.0, 2.0, 13.0);
|
||||
ns_printf(nc, "POST / HTTP/1.0\r\nContent-Type: application/json\r\n"
|
||||
"Content-Length: %d\r\n\r\n%s", (int) strlen(buf), buf);
|
||||
case NS_HTTP_REPLY:
|
||||
ns_rpc_parse_reply(hm->body.p, hm->body.len,
|
||||
toks, sizeof(toks) / sizeof(toks[0]),
|
||||
&rpc_reply, &rpc_error);
|
||||
sprintf((char *) nc->user_data, "%.*s",
|
||||
rpc_reply.result == NULL ? 1 : rpc_reply.result->len,
|
||||
rpc_reply.result == NULL ? "?" : rpc_reply.result->ptr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *test_rpc(void) {
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
const char *local_addr = "127.0.0.1:7779";
|
||||
char buf[100] = "";
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
|
||||
ASSERT((nc = ns_bind(&mgr, local_addr, rpc_server)) != NULL);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
|
||||
ASSERT((nc = ns_connect(&mgr, local_addr, rpc_client)) != NULL);
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
nc->user_data = buf;
|
||||
|
||||
poll_mgr(&mgr, 50);
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
ASSERT(strcmp(buf, "16") == 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void cb5(struct ns_connection *nc, int ev, void *ev_data) {
|
||||
switch (ev) {
|
||||
case NS_CONNECT:
|
||||
sprintf((char *) nc->user_data, "%d", * (int *) ev_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *test_connect_fail(void) {
|
||||
struct ns_mgr mgr;
|
||||
struct ns_connection *nc;
|
||||
char buf[100] = "0";
|
||||
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
ASSERT((nc = ns_connect(&mgr, "127.0.0.1:33211", cb5)) != NULL);
|
||||
nc->user_data = buf;
|
||||
poll_mgr(&mgr, 50);
|
||||
ns_mgr_free(&mgr);
|
||||
|
||||
/* printf("failed connect status: [%s]\n", buf); */
|
||||
ASSERT(strcmp(buf, "0") != 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *run_all_tests(void) {
|
||||
RUN_TEST(test_iobuf);
|
||||
#if 0
|
||||
RUN_TEST(test_parse_address);
|
||||
#endif
|
||||
RUN_TEST(test_connect_fail);
|
||||
RUN_TEST(test_to64);
|
||||
RUN_TEST(test_alloc_vprintf);
|
||||
RUN_TEST(test_socketpair);
|
||||
RUN_TEST(test_thread);
|
||||
RUN_TEST(test_mgr);
|
||||
RUN_TEST(test_parse_http_message);
|
||||
RUN_TEST(test_http);
|
||||
RUN_TEST(test_websocket);
|
||||
RUN_TEST(test_rpc);
|
||||
#ifdef NS_ENABLE_SSL
|
||||
RUN_TEST(test_ssl);
|
||||
#endif
|
||||
RUN_TEST(test_udp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int __cdecl main(void) {
|
||||
const char *fail_msg = run_all_tests();
|
||||
printf("%s, tests run: %d\n", fail_msg ? "FAIL" : "PASS", static_num_tests);
|
||||
return fail_msg == NULL ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
|
@ -0,0 +1,595 @@
|
|||
/*
|
||||
Copyright (c) 2009-2011 250bpm s.r.o.
|
||||
Copyright (c) 2011 Botond Ballo
|
||||
Copyright (c) 2007-2009 iMatix Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __ZMQ_HPP_INCLUDED__
|
||||
#define __ZMQ_HPP_INCLUDED__
|
||||
|
||||
#include <zmq.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
// Detect whether the compiler supports C++11 rvalue references.
|
||||
#if (defined(__GNUC__) && (__GNUC__ > 4 || \
|
||||
(__GNUC__ == 4 && __GNUC_MINOR__ > 2)) && \
|
||||
defined(__GXX_EXPERIMENTAL_CXX0X__))
|
||||
#define ZMQ_HAS_RVALUE_REFS
|
||||
#define ZMQ_DELETED_FUNCTION = delete
|
||||
#elif defined(__clang__)
|
||||
#if __has_feature(cxx_rvalue_references)
|
||||
#define ZMQ_HAS_RVALUE_REFS
|
||||
#endif
|
||||
|
||||
#if __has_feature(cxx_deleted_functions)
|
||||
#define ZMQ_DELETED_FUNCTION = delete
|
||||
#else
|
||||
#define ZMQ_DELETED_FUNCTION
|
||||
#endif
|
||||
#elif defined(_MSC_VER) && (_MSC_VER >= 1600)
|
||||
#define ZMQ_HAS_RVALUE_REFS
|
||||
#define ZMQ_DELETED_FUNCTION
|
||||
#else
|
||||
#define ZMQ_DELETED_FUNCTION
|
||||
#endif
|
||||
|
||||
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 3, 0)
|
||||
#define ZMQ_NEW_MONITOR_EVENT_LAYOUT
|
||||
#endif
|
||||
|
||||
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0)
|
||||
#define ZMQ_HAS_PROXY_STEERABLE
|
||||
/* Socket event data */
|
||||
typedef struct {
|
||||
uint16_t event; // id of the event as bitfield
|
||||
int32_t value ; // value is either error code, fd or reconnect interval
|
||||
} zmq_event_t;
|
||||
#endif
|
||||
|
||||
// In order to prevent unused variable warnings when building in non-debug
|
||||
// mode use this macro to make assertions.
|
||||
#ifndef NDEBUG
|
||||
# define ZMQ_ASSERT(expression) assert(expression)
|
||||
#else
|
||||
# define ZMQ_ASSERT(expression) (void)(expression)
|
||||
#endif
|
||||
|
||||
namespace zmq
|
||||
{
|
||||
|
||||
typedef zmq_free_fn free_fn;
|
||||
typedef zmq_pollitem_t pollitem_t;
|
||||
|
||||
class error_t : public std::exception
|
||||
{
|
||||
public:
|
||||
|
||||
error_t () : errnum (zmq_errno ()) {}
|
||||
|
||||
virtual const char *what () const throw ()
|
||||
{
|
||||
return zmq_strerror (errnum);
|
||||
}
|
||||
|
||||
int num () const
|
||||
{
|
||||
return errnum;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
int errnum;
|
||||
};
|
||||
|
||||
inline int poll (zmq_pollitem_t *items_, int nitems_, long timeout_ = -1)
|
||||
{
|
||||
int rc = zmq_poll (items_, nitems_, timeout_);
|
||||
if (rc < 0)
|
||||
throw error_t ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
inline void proxy (void *frontend, void *backend, void *capture)
|
||||
{
|
||||
int rc = zmq_proxy (frontend, backend, capture);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
#ifdef ZMQ_HAS_PROXY_STEERABLE
|
||||
inline void proxy_steerable (void *frontend, void *backend, void *capture, void *control)
|
||||
{
|
||||
int rc = zmq_proxy_steerable (frontend, backend, capture, control);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void version (int *major_, int *minor_, int *patch_)
|
||||
{
|
||||
zmq_version (major_, minor_, patch_);
|
||||
}
|
||||
|
||||
class message_t
|
||||
{
|
||||
friend class socket_t;
|
||||
|
||||
public:
|
||||
|
||||
inline message_t ()
|
||||
{
|
||||
int rc = zmq_msg_init (&msg);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline explicit message_t (size_t size_)
|
||||
{
|
||||
int rc = zmq_msg_init_size (&msg, size_);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline message_t (void *data_, size_t size_, free_fn *ffn_,
|
||||
void *hint_ = NULL)
|
||||
{
|
||||
int rc = zmq_msg_init_data (&msg, data_, size_, ffn_, hint_);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
#ifdef ZMQ_HAS_RVALUE_REFS
|
||||
inline message_t (message_t &&rhs) : msg (rhs.msg)
|
||||
{
|
||||
int rc = zmq_msg_init (&rhs.msg);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline message_t &operator = (message_t &&rhs)
|
||||
{
|
||||
std::swap (msg, rhs.msg);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline ~message_t ()
|
||||
{
|
||||
int rc = zmq_msg_close (&msg);
|
||||
ZMQ_ASSERT (rc == 0);
|
||||
}
|
||||
|
||||
inline void rebuild ()
|
||||
{
|
||||
int rc = zmq_msg_close (&msg);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
rc = zmq_msg_init (&msg);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline void rebuild (size_t size_)
|
||||
{
|
||||
int rc = zmq_msg_close (&msg);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
rc = zmq_msg_init_size (&msg, size_);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline void rebuild (void *data_, size_t size_, free_fn *ffn_,
|
||||
void *hint_ = NULL)
|
||||
{
|
||||
int rc = zmq_msg_close (&msg);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
rc = zmq_msg_init_data (&msg, data_, size_, ffn_, hint_);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline void move (message_t *msg_)
|
||||
{
|
||||
int rc = zmq_msg_move (&msg, &(msg_->msg));
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline void copy (message_t *msg_)
|
||||
{
|
||||
int rc = zmq_msg_copy (&msg, &(msg_->msg));
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline bool more ()
|
||||
{
|
||||
int rc = zmq_msg_more (&msg);
|
||||
return rc != 0;
|
||||
}
|
||||
|
||||
inline void *data ()
|
||||
{
|
||||
return zmq_msg_data (&msg);
|
||||
}
|
||||
|
||||
inline const void* data () const
|
||||
{
|
||||
return zmq_msg_data (const_cast<zmq_msg_t*>(&msg));
|
||||
}
|
||||
|
||||
inline size_t size () const
|
||||
{
|
||||
return zmq_msg_size (const_cast<zmq_msg_t*>(&msg));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// The underlying message
|
||||
zmq_msg_t msg;
|
||||
|
||||
// Disable implicit message copying, so that users won't use shared
|
||||
// messages (less efficient) without being aware of the fact.
|
||||
message_t (const message_t&);
|
||||
void operator = (const message_t&);
|
||||
};
|
||||
|
||||
class context_t
|
||||
{
|
||||
friend class socket_t;
|
||||
|
||||
public:
|
||||
inline context_t ()
|
||||
{
|
||||
ptr = zmq_ctx_new ();
|
||||
if (ptr == NULL)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
|
||||
inline explicit context_t (int io_threads_, int max_sockets_ = ZMQ_MAX_SOCKETS_DFLT)
|
||||
{
|
||||
ptr = zmq_ctx_new ();
|
||||
if (ptr == NULL)
|
||||
throw error_t ();
|
||||
|
||||
int rc = zmq_ctx_set (ptr, ZMQ_IO_THREADS, io_threads_);
|
||||
ZMQ_ASSERT (rc == 0);
|
||||
|
||||
rc = zmq_ctx_set (ptr, ZMQ_MAX_SOCKETS, max_sockets_);
|
||||
ZMQ_ASSERT (rc == 0);
|
||||
}
|
||||
|
||||
#ifdef ZMQ_HAS_RVALUE_REFS
|
||||
inline context_t (context_t &&rhs) : ptr (rhs.ptr)
|
||||
{
|
||||
rhs.ptr = NULL;
|
||||
}
|
||||
inline context_t &operator = (context_t &&rhs)
|
||||
{
|
||||
std::swap (ptr, rhs.ptr);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline ~context_t ()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
inline void close()
|
||||
{
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
int rc = zmq_ctx_destroy (ptr);
|
||||
ZMQ_ASSERT (rc == 0);
|
||||
ptr = NULL;
|
||||
}
|
||||
|
||||
// Be careful with this, it's probably only useful for
|
||||
// using the C api together with an existing C++ api.
|
||||
// Normally you should never need to use this.
|
||||
inline operator void* ()
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void *ptr;
|
||||
|
||||
context_t (const context_t&);
|
||||
void operator = (const context_t&);
|
||||
};
|
||||
|
||||
class socket_t
|
||||
{
|
||||
friend class monitor_t;
|
||||
public:
|
||||
|
||||
inline socket_t (context_t &context_, int type_)
|
||||
{
|
||||
ctxptr = context_.ptr;
|
||||
ptr = zmq_socket (context_.ptr, type_);
|
||||
if (ptr == NULL)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
#ifdef ZMQ_HAS_RVALUE_REFS
|
||||
inline socket_t(socket_t&& rhs) : ptr(rhs.ptr)
|
||||
{
|
||||
rhs.ptr = NULL;
|
||||
}
|
||||
inline socket_t& operator=(socket_t&& rhs)
|
||||
{
|
||||
std::swap(ptr, rhs.ptr);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline ~socket_t ()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
inline operator void* ()
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
|
||||
inline void close()
|
||||
{
|
||||
if(ptr == NULL)
|
||||
// already closed
|
||||
return ;
|
||||
int rc = zmq_close (ptr);
|
||||
ZMQ_ASSERT (rc == 0);
|
||||
ptr = 0 ;
|
||||
}
|
||||
|
||||
inline void setsockopt (int option_, const void *optval_,
|
||||
size_t optvallen_)
|
||||
{
|
||||
int rc = zmq_setsockopt (ptr, option_, optval_, optvallen_);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline void getsockopt (int option_, void *optval_,
|
||||
size_t *optvallen_)
|
||||
{
|
||||
int rc = zmq_getsockopt (ptr, option_, optval_, optvallen_);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline void bind (const char *addr_)
|
||||
{
|
||||
int rc = zmq_bind (ptr, addr_);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline void unbind (const char *addr_)
|
||||
{
|
||||
int rc = zmq_unbind (ptr, addr_);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline void connect (const char *addr_)
|
||||
{
|
||||
int rc = zmq_connect (ptr, addr_);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline void disconnect (const char *addr_)
|
||||
{
|
||||
int rc = zmq_disconnect (ptr, addr_);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline bool connected()
|
||||
{
|
||||
return(ptr != NULL);
|
||||
}
|
||||
|
||||
inline size_t send (const void *buf_, size_t len_, int flags_ = 0)
|
||||
{
|
||||
int nbytes = zmq_send (ptr, buf_, len_, flags_);
|
||||
if (nbytes >= 0)
|
||||
return (size_t) nbytes;
|
||||
if (zmq_errno () == EAGAIN)
|
||||
return 0;
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline bool send (message_t &msg_, int flags_ = 0)
|
||||
{
|
||||
int nbytes = zmq_msg_send (&(msg_.msg), ptr, flags_);
|
||||
if (nbytes >= 0)
|
||||
return true;
|
||||
if (zmq_errno () == EAGAIN)
|
||||
return false;
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline size_t recv (void *buf_, size_t len_, int flags_ = 0)
|
||||
{
|
||||
int nbytes = zmq_recv (ptr, buf_, len_, flags_);
|
||||
if (nbytes >= 0)
|
||||
return (size_t) nbytes;
|
||||
if (zmq_errno () == EAGAIN)
|
||||
return 0;
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
inline bool recv (message_t *msg_, int flags_ = 0)
|
||||
{
|
||||
int nbytes = zmq_msg_recv (&(msg_->msg), ptr, flags_);
|
||||
if (nbytes >= 0)
|
||||
return true;
|
||||
if (zmq_errno () == EAGAIN)
|
||||
return false;
|
||||
throw error_t ();
|
||||
}
|
||||
|
||||
private:
|
||||
void *ptr;
|
||||
void *ctxptr;
|
||||
|
||||
socket_t (const socket_t&) ZMQ_DELETED_FUNCTION;
|
||||
void operator = (const socket_t&) ZMQ_DELETED_FUNCTION;
|
||||
};
|
||||
|
||||
class monitor_t
|
||||
{
|
||||
public:
|
||||
monitor_t() : socketPtr(NULL) {}
|
||||
virtual ~monitor_t() {}
|
||||
|
||||
void monitor(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL)
|
||||
{
|
||||
int rc = zmq_socket_monitor(socket.ptr, addr_, events);
|
||||
if (rc != 0)
|
||||
throw error_t ();
|
||||
|
||||
socketPtr = socket.ptr;
|
||||
void *s = zmq_socket (socket.ctxptr, ZMQ_PAIR);
|
||||
assert (s);
|
||||
|
||||
rc = zmq_connect (s, addr_);
|
||||
assert (rc == 0);
|
||||
|
||||
on_monitor_started();
|
||||
|
||||
while (true) {
|
||||
zmq_msg_t eventMsg;
|
||||
zmq_msg_init (&eventMsg);
|
||||
rc = zmq_recvmsg (s, &eventMsg, 0);
|
||||
if (rc == -1 && zmq_errno() == ETERM)
|
||||
break;
|
||||
assert (rc != -1);
|
||||
#if ZMQ_VERSION_MAJOR >= 4
|
||||
const char* data = static_cast<const char*>(zmq_msg_data(&eventMsg));
|
||||
zmq_event_t msgEvent;
|
||||
msgEvent.event = *(uint16_t*)data; data += sizeof(uint16_t);
|
||||
msgEvent.value = *(int32_t*)data;
|
||||
zmq_event_t* event = &msgEvent;
|
||||
#else
|
||||
zmq_event_t* event = static_cast<zmq_event_t*>(zmq_msg_data(&eventMsg));
|
||||
#endif
|
||||
|
||||
#ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT
|
||||
zmq_msg_t addrMsg;
|
||||
zmq_msg_init (&addrMsg);
|
||||
rc = zmq_recvmsg (s, &addrMsg, 0);
|
||||
if (rc == -1 && zmq_errno() == ETERM)
|
||||
break;
|
||||
assert (rc != -1);
|
||||
const char* str = static_cast<const char*>(zmq_msg_data (&addrMsg));
|
||||
std::string address(str, str + zmq_msg_size(&addrMsg));
|
||||
zmq_msg_close (&addrMsg);
|
||||
#else
|
||||
// Bit of a hack, but all events in the zmq_event_t union have the same layout so this will work for all event types.
|
||||
std::string address = event->data.connected.addr;
|
||||
#endif
|
||||
|
||||
#ifdef ZMQ_EVENT_MONITOR_STOPPED
|
||||
if (event->event == ZMQ_EVENT_MONITOR_STOPPED)
|
||||
break;
|
||||
#endif
|
||||
|
||||
switch (event->event) {
|
||||
case ZMQ_EVENT_CONNECTED:
|
||||
on_event_connected(*event, address.c_str());
|
||||
break;
|
||||
case ZMQ_EVENT_CONNECT_DELAYED:
|
||||
on_event_connect_delayed(*event, address.c_str());
|
||||
break;
|
||||
case ZMQ_EVENT_CONNECT_RETRIED:
|
||||
on_event_connect_retried(*event, address.c_str());
|
||||
break;
|
||||
case ZMQ_EVENT_LISTENING:
|
||||
on_event_listening(*event, address.c_str());
|
||||
break;
|
||||
case ZMQ_EVENT_BIND_FAILED:
|
||||
on_event_bind_failed(*event, address.c_str());
|
||||
break;
|
||||
case ZMQ_EVENT_ACCEPTED:
|
||||
on_event_accepted(*event, address.c_str());
|
||||
break;
|
||||
case ZMQ_EVENT_ACCEPT_FAILED:
|
||||
on_event_accept_failed(*event, address.c_str());
|
||||
break;
|
||||
case ZMQ_EVENT_CLOSED:
|
||||
on_event_closed(*event, address.c_str());
|
||||
break;
|
||||
case ZMQ_EVENT_CLOSE_FAILED:
|
||||
on_event_close_failed(*event, address.c_str());
|
||||
break;
|
||||
case ZMQ_EVENT_DISCONNECTED:
|
||||
on_event_disconnected(*event, address.c_str());
|
||||
break;
|
||||
default:
|
||||
on_event_unknown(*event, address.c_str());
|
||||
break;
|
||||
}
|
||||
zmq_msg_close (&eventMsg);
|
||||
}
|
||||
zmq_close (s);
|
||||
socketPtr = NULL;
|
||||
}
|
||||
|
||||
#ifdef ZMQ_EVENT_MONITOR_STOPPED
|
||||
void abort()
|
||||
{
|
||||
if (socketPtr)
|
||||
zmq_socket_monitor(socketPtr, NULL, 0);
|
||||
}
|
||||
#endif
|
||||
virtual void on_monitor_started() {}
|
||||
virtual void on_event_connected(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; }
|
||||
virtual void on_event_connect_delayed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; }
|
||||
virtual void on_event_connect_retried(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; }
|
||||
virtual void on_event_listening(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; }
|
||||
virtual void on_event_bind_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; }
|
||||
virtual void on_event_accepted(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; }
|
||||
virtual void on_event_accept_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; }
|
||||
virtual void on_event_closed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; }
|
||||
virtual void on_event_close_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; }
|
||||
virtual void on_event_disconnected(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; }
|
||||
virtual void on_event_unknown(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; }
|
||||
private:
|
||||
void* socketPtr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -51,7 +51,6 @@ function (bitmonero_add_executable name)
|
|||
source_group("${name}"
|
||||
FILES
|
||||
${ARGN})
|
||||
|
||||
add_executable("${name}"
|
||||
${ARGN})
|
||||
target_link_libraries("${name}"
|
||||
|
@ -86,11 +85,15 @@ function (bitmonero_add_library name)
|
|||
FOLDER "libs")
|
||||
endfunction ()
|
||||
|
||||
find_library(ZMQ_LIB zmq)
|
||||
find_library(CZMQ_LIB czmq)
|
||||
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(crypto)
|
||||
add_subdirectory(cryptonote_core)
|
||||
add_subdirectory(blockchain_db)
|
||||
add_subdirectory(mnemonics)
|
||||
add_subdirectory(ipc)
|
||||
add_subdirectory(rpc)
|
||||
add_subdirectory(wallet)
|
||||
add_subdirectory(p2p)
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 8307f0f4c9035bd63853bdf9e1b951e8c0e37b62
|
|
@ -85,6 +85,7 @@ bitmonero_add_executable(daemon
|
|||
target_link_libraries(daemon
|
||||
LINK_PRIVATE
|
||||
rpc
|
||||
server_ipc
|
||||
blockchain_db
|
||||
cryptonote_core
|
||||
crypto
|
||||
|
@ -101,8 +102,12 @@ target_link_libraries(daemon
|
|||
${Boost_THREAD_LIBRARY}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${UPNP_LIBRARIES}
|
||||
${EXTRA_LIBRARIES})
|
||||
add_dependencies(daemon version)
|
||||
${EXTRA_LIBRARIES}
|
||||
${NET_SKELETON_LIBRARY}
|
||||
${ZMQ_LIB}
|
||||
${CZMQ_LIB})
|
||||
add_dependencies(daemon
|
||||
version)
|
||||
set_property(TARGET daemon
|
||||
PROPERTY
|
||||
OUTPUT_NAME "bitmonerod")
|
||||
|
|
|
@ -34,11 +34,11 @@
|
|||
#include "daemon/core.h"
|
||||
#include "daemon/p2p.h"
|
||||
#include "daemon/protocol.h"
|
||||
#include "daemon/rpc.h"
|
||||
#include "daemon/command_server.h"
|
||||
#include "misc_log_ex.h"
|
||||
#include "version.h"
|
||||
#include "../../contrib/epee/include/syncobj.h"
|
||||
#include "daemon_ipc_handlers.h"
|
||||
|
||||
using namespace epee;
|
||||
|
||||
|
@ -56,7 +56,7 @@ private:
|
|||
public:
|
||||
t_core core;
|
||||
t_p2p p2p;
|
||||
t_rpc rpc;
|
||||
bool testnet_mode;
|
||||
|
||||
t_internals(
|
||||
boost::program_options::variables_map const & vm
|
||||
|
@ -64,11 +64,11 @@ public:
|
|||
: core{vm}
|
||||
, protocol{vm, core}
|
||||
, p2p{vm, protocol}
|
||||
, rpc{vm, core, p2p}
|
||||
{
|
||||
// Handle circular dependencies
|
||||
protocol.set_p2p_endpoint(p2p.get());
|
||||
core.set_protocol(protocol.get());
|
||||
testnet_mode = command_line::get_arg(vm, daemon_args::arg_testnet_on);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -76,7 +76,6 @@ void t_daemon::init_options(boost::program_options::options_description & option
|
|||
{
|
||||
t_core::init_options(option_spec);
|
||||
t_p2p::init_options(option_spec);
|
||||
t_rpc::init_options(option_spec);
|
||||
}
|
||||
|
||||
t_daemon::t_daemon(
|
||||
|
@ -119,24 +118,19 @@ bool t_daemon::run(bool interactive)
|
|||
try
|
||||
{
|
||||
mp_internals->core.run();
|
||||
mp_internals->rpc.run();
|
||||
|
||||
daemonize::t_command_server* rpc_commands;
|
||||
|
||||
if (interactive)
|
||||
{
|
||||
rpc_commands = new daemonize::t_command_server(0, 0, false, mp_internals->rpc.get_server());
|
||||
rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this));
|
||||
IPC::Daemon::init(mp_internals->core.get(), mp_internals->p2p.get(), mp_internals->testnet_mode);
|
||||
}
|
||||
|
||||
mp_internals->p2p.run(); // blocks until p2p goes down
|
||||
|
||||
if (interactive)
|
||||
{
|
||||
rpc_commands->stop_handling();
|
||||
IPC::Daemon::stop();
|
||||
}
|
||||
|
||||
mp_internals->rpc.stop();
|
||||
LOG_PRINT("Node stopped.", LOG_LEVEL_0);
|
||||
return true;
|
||||
}
|
||||
|
@ -159,8 +153,8 @@ void t_daemon::stop()
|
|||
throw std::runtime_error{"Can't stop stopped daemon"};
|
||||
}
|
||||
mp_internals->p2p.stop();
|
||||
mp_internals->rpc.stop();
|
||||
mp_internals.reset(nullptr); // Ensure resources are cleaned up before we return
|
||||
IPC::Daemon::stop();
|
||||
}
|
||||
|
||||
void t_daemon::stop_p2p()
|
||||
|
|
|
@ -98,6 +98,11 @@ int main(int argc, char const * argv[])
|
|||
// Hidden options
|
||||
command_line::add_arg(hidden_options, daemon_args::arg_command);
|
||||
|
||||
// Legacy RPC options (in hidden section, not sure if best place)
|
||||
command_line::add_arg(hidden_options, cryptonote::core_rpc_server::arg_rpc_bind_ip);
|
||||
command_line::add_arg(hidden_options, cryptonote::core_rpc_server::arg_rpc_bind_port);
|
||||
command_line::add_arg(hidden_options, cryptonote::core_rpc_server::arg_testnet_rpc_bind_port);
|
||||
|
||||
visible_options.add(core_settings);
|
||||
all_options.add(visible_options);
|
||||
all_options.add(hidden_options);
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
# Copyright (c) 2014-2015, The Monero Project
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are
|
||||
# permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
# conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
# of conditions and the following disclaimer in the documentation and/or other
|
||||
# materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without specific
|
||||
# prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set(server_ipc_sources
|
||||
wap_server/wap_server.c
|
||||
wap_proto.c
|
||||
daemon_ipc_handlers.cpp)
|
||||
|
||||
set(client_ipc_sources
|
||||
wap_client/wap_client.c
|
||||
wap_proto.c)
|
||||
|
||||
set_source_files_properties(${server_ipc_sources} ${client_ipc_sources} PROPERTIES LANGUAGE CXX)
|
||||
set_source_files_properties(${server_ipc_sources} ${client_ipc_sources} PROPERTIES COMPILE_FLAGS "-Wno-write-strings -Wno-error -fpermissive")
|
||||
|
||||
set(server_ipc_headers)
|
||||
set(client_ipc_headers)
|
||||
|
||||
bitmonero_add_library(server_ipc
|
||||
${server_ipc_sources}
|
||||
${server_ipc_headers})
|
||||
target_link_libraries(server_ipc
|
||||
LINK_PRIVATE
|
||||
cryptonote_core
|
||||
${ZMQ_LIB}
|
||||
${CZMQ_LIB})
|
||||
|
||||
bitmonero_add_library(client_ipc
|
||||
${client_ipc_sources}
|
||||
${client_ipc_headers})
|
||||
target_link_libraries(client_ipc
|
||||
LINK_PRIVATE
|
||||
cryptonote_core
|
||||
${ZMQ_LIB}
|
||||
${CZMQ_LIB})
|
|
@ -0,0 +1,734 @@
|
|||
// Copyright (c) 2014, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
/*!
|
||||
* \file daemon_ipc_handlers.cpp
|
||||
* \brief Implementation of Daemon IPC handlers
|
||||
*
|
||||
* Most of this code is borrowed from core_rpc_server.cpp but changed to use 0MQ objects.
|
||||
*/
|
||||
|
||||
//TODO: Recheck memory leaks
|
||||
|
||||
#include "daemon_ipc_handlers.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/*!
|
||||
* \namespace IPC
|
||||
* \brief Anonymous namepsace to keep things in the scope of this file
|
||||
*/
|
||||
namespace
|
||||
{
|
||||
cryptonote::core *core; /*!< Pointer to the core */
|
||||
nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> > *p2p;
|
||||
/*!< Pointer to p2p node server */
|
||||
zactor_t *server; /*!< 0MQ server */
|
||||
bool testnet; /*!< testnet mode or not */
|
||||
|
||||
/*!
|
||||
* \brief Checks if core is busy
|
||||
*
|
||||
* \return true if core is busy
|
||||
*/
|
||||
bool check_core_busy()
|
||||
{
|
||||
if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Checks if core is ready
|
||||
*
|
||||
* \return true if core is ready
|
||||
*/
|
||||
bool check_core_ready()
|
||||
{
|
||||
if (p2p->get_payload_object().is_synchronized())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return check_core_busy();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief equivalent of strstr, but with arbitrary bytes (ie, NULs)
|
||||
* This does not differentiate between "not found" and "found at offset 0"
|
||||
* (taken straight from core_rpc_server.cpp)
|
||||
*/
|
||||
uint64_t slow_memmem(const void *start_buff, size_t buflen, const void *pat, size_t patlen)
|
||||
{
|
||||
const void *buf = start_buff;
|
||||
const void *end = (const char*)buf + buflen;
|
||||
if (patlen > buflen || patlen == 0) return 0;
|
||||
while (buflen > 0 && (buf = memchr(buf, ((const char*)pat)[0], buflen-patlen + 1)))
|
||||
{
|
||||
if (memcmp(buf,pat,patlen) == 0)
|
||||
return (const char*)buf - (const char*)start_buff;
|
||||
buf = (const char*)buf + 1;
|
||||
buflen = (const char*)end - (const char*)buf;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \namespace IPC
|
||||
* \brief Namespace pertaining to IPC.
|
||||
*/
|
||||
namespace IPC
|
||||
{
|
||||
/*!
|
||||
* \namespace Daemon
|
||||
* \brief Namespace pertaining to Daemon IPC.
|
||||
*/
|
||||
namespace Daemon
|
||||
{
|
||||
/*!
|
||||
* \brief initializes it with objects necessary to handle IPC requests and starts
|
||||
* IPC server
|
||||
*
|
||||
* \param p_core cryptonote core object
|
||||
* \param p_p2p p2p object
|
||||
* \param p_testnet testnet mode or not
|
||||
*/
|
||||
void init(cryptonote::core &p_core,
|
||||
nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> > &p_p2p,
|
||||
bool p_testnet)
|
||||
{
|
||||
p2p = &p_p2p;
|
||||
core = &p_core;
|
||||
testnet = p_testnet;
|
||||
server = zactor_new (wap_server, NULL);
|
||||
zsock_send (server, "ss", "BIND", "ipc://@/monero");
|
||||
zsock_send (server, "sss", "SET", "server/timeout", "5000");
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief stops the IPC server
|
||||
*
|
||||
* \param p_core cryptonote core object
|
||||
* \param p_p2p p2p object
|
||||
* \param p_testnet testnet mode or not
|
||||
*/
|
||||
void stop() {
|
||||
zactor_destroy(&server);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief start_mining IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void start_mining(wap_proto_t *message)
|
||||
{
|
||||
if (!check_core_busy()) {
|
||||
wap_proto_set_status(message, STATUS_CORE_BUSY);
|
||||
return;
|
||||
}
|
||||
cryptonote::account_public_address adr;
|
||||
zchunk_t *address_chunk = wap_proto_address(message);
|
||||
char *address = (char*)zchunk_data(address_chunk);
|
||||
std::string address_string(address, zchunk_size(address_chunk));
|
||||
|
||||
if (!get_account_address_from_str(adr, testnet, std::string(address_string)))
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_WRONG_ADDRESS);
|
||||
return;
|
||||
}
|
||||
|
||||
boost::thread::attributes attrs;
|
||||
attrs.set_stack_size(THREAD_STACK_SIZE);
|
||||
|
||||
uint64_t thread_count = wap_proto_thread_count(message);
|
||||
if (!core->get_miner().start(adr, static_cast<size_t>(thread_count), attrs))
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_MINING_NOT_STARTED);
|
||||
return;
|
||||
}
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief stop_mining IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void stop_mining(wap_proto_t *message)
|
||||
{
|
||||
if (!core->get_miner().stop())
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_MINING_NOT_STOPPED);
|
||||
return;
|
||||
}
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief get_blocks IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void retrieve_blocks(wap_proto_t *message)
|
||||
{
|
||||
if (!check_core_busy()) {
|
||||
wap_proto_set_status(message, STATUS_CORE_BUSY);
|
||||
return;
|
||||
}
|
||||
uint64_t start_height = wap_proto_start_height(message);
|
||||
zlist_t *z_block_ids = wap_proto_block_ids(message);
|
||||
|
||||
std::list<crypto::hash> block_ids;
|
||||
char *block_id = (char*)zlist_first(z_block_ids);
|
||||
while (block_id) {
|
||||
crypto::hash hash;
|
||||
memcpy(hash.data, block_id + 1, crypto::HASH_SIZE);
|
||||
block_ids.push_back(hash);
|
||||
block_id = (char*)zlist_next(z_block_ids);
|
||||
}
|
||||
|
||||
std::list<std::pair<cryptonote::block, std::list<cryptonote::transaction> > > bs;
|
||||
uint64_t result_current_height = 0;
|
||||
uint64_t result_start_height = 0;
|
||||
if (!core->find_blockchain_supplement(start_height, block_ids, bs, result_current_height,
|
||||
result_start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// We are using JSON to encode blocks. The JSON string will sit in a
|
||||
// 0MQ frame which gets sent in a zmsg_t object. One could put each block
|
||||
// a different frame too.
|
||||
|
||||
// First create a rapidjson object and then stringify it.
|
||||
rapidjson::Document result_json;
|
||||
result_json.SetObject();
|
||||
rapidjson::Document::AllocatorType &allocator = result_json.GetAllocator();
|
||||
rapidjson::Value block_json(rapidjson::kArrayType);
|
||||
std::string blob;
|
||||
BOOST_FOREACH(auto &b, bs)
|
||||
{
|
||||
rapidjson::Value this_block(rapidjson::kObjectType);
|
||||
blob = block_to_blob(b.first);
|
||||
rapidjson::Value string_value(rapidjson::kStringType);
|
||||
string_value.SetString(blob.c_str(), blob.length(), allocator);
|
||||
this_block.AddMember("block", string_value.Move(), allocator);
|
||||
rapidjson::Value txs_blocks(rapidjson::kArrayType);
|
||||
BOOST_FOREACH(auto &t, b.second)
|
||||
{
|
||||
rapidjson::Value string_value(rapidjson::kStringType);
|
||||
blob = cryptonote::tx_to_blob(t);
|
||||
string_value.SetString(blob.c_str(), blob.length(), allocator);
|
||||
txs_blocks.PushBack(string_value.Move(), allocator);
|
||||
}
|
||||
this_block.AddMember("txs", txs_blocks, allocator);
|
||||
block_json.PushBack(this_block, allocator);
|
||||
}
|
||||
|
||||
result_json.AddMember("blocks", block_json, allocator);
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
result_json.Accept(writer);
|
||||
std::string block_string = buffer.GetString();
|
||||
zmsg_t *block_data = zmsg_new();
|
||||
// Put the JSON string in a frame.
|
||||
zframe_t *frame = zframe_new(block_string.c_str(), block_string.length());
|
||||
zmsg_prepend(block_data, &frame);
|
||||
wap_proto_set_start_height(message, result_start_height);
|
||||
wap_proto_set_curr_height(message, result_current_height);
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
wap_proto_set_block_data(message, &block_data);
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief send_raw_transaction IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void send_raw_transaction(wap_proto_t *message)
|
||||
{
|
||||
if (!check_core_busy()) {
|
||||
wap_proto_set_status(message, STATUS_CORE_BUSY);
|
||||
return;
|
||||
}
|
||||
std::string tx_blob;
|
||||
zchunk_t *tx_as_hex_chunk = wap_proto_tx_as_hex(message);
|
||||
char *tx_as_hex = (char*)zchunk_data(tx_as_hex_chunk);
|
||||
std::string tx_as_hex_string(tx_as_hex, zchunk_size(tx_as_hex_chunk));
|
||||
if (!string_tools::parse_hexstr_to_binbuff(tx_as_hex_string, tx_blob))
|
||||
{
|
||||
LOG_PRINT_L0("[on_send_raw_tx]: Failed to parse tx from hexbuff: " << tx_as_hex_string);
|
||||
wap_proto_set_status(message, STATUS_INVALID_TX);
|
||||
return;
|
||||
}
|
||||
|
||||
cryptonote::cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
|
||||
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
|
||||
if (!core->handle_incoming_tx(tx_blob, tvc, false))
|
||||
{
|
||||
LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx");
|
||||
wap_proto_set_status(message, STATUS_INVALID_TX);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tvc.m_verifivation_failed)
|
||||
{
|
||||
LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed");
|
||||
wap_proto_set_status(message, STATUS_TX_VERIFICATION_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tvc.m_should_be_relayed)
|
||||
{
|
||||
LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed");
|
||||
wap_proto_set_status(message, STATUS_TX_NOT_RELAYED);
|
||||
return;
|
||||
}
|
||||
|
||||
cryptonote::NOTIFY_NEW_TRANSACTIONS::request r;
|
||||
r.txs.push_back(tx_blob);
|
||||
core->get_protocol()->relay_transactions(r, fake_context);
|
||||
// TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief get_output_indexes IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void get_output_indexes(wap_proto_t *message)
|
||||
{
|
||||
if (!check_core_busy()) {
|
||||
wap_proto_set_status(message, STATUS_CORE_BUSY);
|
||||
return;
|
||||
}
|
||||
zchunk_t *tx_id = wap_proto_tx_id(message);
|
||||
crypto::hash hash;
|
||||
memcpy(hash.data, zchunk_data(tx_id), crypto::HASH_SIZE);
|
||||
std::vector<uint64_t> output_indexes;
|
||||
|
||||
bool r = core->get_tx_outputs_gindexs(hash, output_indexes);
|
||||
if (!r)
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
// Spec guarantees that vector elements are contiguous. So coversion to uint64_t[] is easy.
|
||||
uint64_t *indexes = &output_indexes[0];
|
||||
zframe_t *frame = zframe_new(indexes, sizeof(uint64_t) * output_indexes.size());
|
||||
wap_proto_set_o_indexes(message, &frame);
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief get_random_outputs IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void get_random_outs(wap_proto_t *message) {
|
||||
if (!check_core_busy()) {
|
||||
wap_proto_set_status(message, STATUS_CORE_BUSY);
|
||||
return;
|
||||
}
|
||||
// The core does its stuff with old style RPC objects.
|
||||
// So we construct and read from those objects.
|
||||
cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req;
|
||||
uint64_t outs_count = wap_proto_outs_count(message);
|
||||
req.outs_count = outs_count;
|
||||
zframe_t *amounts_frame = wap_proto_amounts(message);
|
||||
uint64_t amounts_count = zframe_size(amounts_frame) / sizeof(uint64_t);
|
||||
uint64_t *amounts = (uint64_t*)zframe_data(amounts_frame);
|
||||
for (unsigned int i = 0; i < amounts_count; i++) {
|
||||
req.amounts.push_back(amounts[i]);
|
||||
}
|
||||
|
||||
cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res;
|
||||
if (!core->get_random_outs_for_amounts(req, res))
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_RANDOM_OUTS_FAILED);
|
||||
}
|
||||
|
||||
// We convert the result into a JSON string and put it into a 0MQ frame.
|
||||
rapidjson::Document result_json;
|
||||
result_json.SetObject();
|
||||
rapidjson::Document::AllocatorType &allocator = result_json.GetAllocator();
|
||||
rapidjson::Value outputs_json(rapidjson::kArrayType);
|
||||
|
||||
typedef cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount;
|
||||
typedef cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
|
||||
std::stringstream ss;
|
||||
std::for_each(res.outs.begin(), res.outs.end(), [&](outs_for_amount& ofa)
|
||||
{
|
||||
ss << "[" << ofa.amount << "]:";
|
||||
CHECK_AND_ASSERT_MES(ofa.outs.size(), ;, "internal error: ofa.outs.size() is empty for amount " << ofa.amount);
|
||||
std::for_each(ofa.outs.begin(), ofa.outs.end(), [&](out_entry& oe)
|
||||
{
|
||||
ss << oe.global_amount_index << " ";
|
||||
});
|
||||
ss << ENDL;
|
||||
});
|
||||
std::string s = ss.str();
|
||||
LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s);
|
||||
for (unsigned int i = 0; i < res.outs.size(); i++) {
|
||||
rapidjson::Value output(rapidjson::kObjectType);
|
||||
outs_for_amount out = res.outs[i];
|
||||
rapidjson::Value output_entries(rapidjson::kArrayType);
|
||||
for (std::list<out_entry>::iterator it = out.outs.begin(); it != out.outs.end(); it++) {
|
||||
rapidjson::Value output_entry(rapidjson::kObjectType);
|
||||
out_entry entry = *it;
|
||||
output_entry.AddMember("global_amount_index", entry.global_amount_index, allocator);
|
||||
rapidjson::Value string_value(rapidjson::kStringType);
|
||||
string_value.SetString(entry.out_key.data, 32, allocator);
|
||||
output_entry.AddMember("out_key", string_value.Move(), allocator);
|
||||
output_entries.PushBack(output_entry, allocator);
|
||||
}
|
||||
output.AddMember("amount", out.amount, allocator);
|
||||
output.AddMember("outs", output_entries, allocator);
|
||||
outputs_json.PushBack(output, allocator);
|
||||
}
|
||||
result_json.AddMember("outputs", outputs_json, allocator);
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
result_json.Accept(writer);
|
||||
std::string block_string = buffer.GetString();
|
||||
|
||||
zframe_t *frame = zframe_new(block_string.c_str(), block_string.length());
|
||||
wap_proto_set_random_outputs(message, &frame);
|
||||
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief get_height IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void get_height(wap_proto_t *message) {
|
||||
if (!check_core_busy()) {
|
||||
wap_proto_set_status(message, STATUS_CORE_BUSY);
|
||||
return;
|
||||
}
|
||||
wap_proto_set_height(message, core->get_current_blockchain_height());
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief save_bc IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void save_bc(wap_proto_t *message) {
|
||||
if (!check_core_busy()) {
|
||||
wap_proto_set_status(message, STATUS_CORE_BUSY);
|
||||
return;
|
||||
}
|
||||
if (!core->get_blockchain_storage().store_blockchain()) {
|
||||
wap_proto_set_status(message, STATUS_ERROR_STORING_BLOCKCHAIN);
|
||||
return;
|
||||
}
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief get_info IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void get_info(wap_proto_t *message) {
|
||||
if (!check_core_busy()) {
|
||||
wap_proto_set_status(message, STATUS_CORE_BUSY);
|
||||
return;
|
||||
}
|
||||
uint64_t height = core->get_current_blockchain_height();
|
||||
wap_proto_set_height(message, height);
|
||||
wap_proto_set_target_height(message, core->get_target_blockchain_height());
|
||||
wap_proto_set_difficulty(message, core->get_blockchain_storage().get_difficulty_for_next_block());
|
||||
wap_proto_set_tx_count(message, core->get_blockchain_storage().get_total_transactions() - height);
|
||||
wap_proto_set_tx_pool_size(message, core->get_pool_transactions_count());
|
||||
wap_proto_set_alt_blocks_count(message, core->get_blockchain_storage().get_alternative_blocks_count());
|
||||
uint64_t outgoing_connections_count = p2p->get_outgoing_connections_count();
|
||||
wap_proto_set_outgoing_connections_count(message, outgoing_connections_count);
|
||||
uint64_t total_connections = p2p->get_connections_count();
|
||||
wap_proto_set_incoming_connections_count(message, total_connections - outgoing_connections_count);
|
||||
wap_proto_set_white_peerlist_size(message, p2p->get_peerlist_manager().get_white_peers_count());
|
||||
wap_proto_set_grey_peerlist_size(message, p2p->get_peerlist_manager().get_gray_peers_count());
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief get_peer_list IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void get_peer_list(wap_proto_t *message) {
|
||||
std::list<nodetool::peerlist_entry> white_list;
|
||||
std::list<nodetool::peerlist_entry> gray_list;
|
||||
p2p->get_peerlist_manager().get_peerlist_full(white_list, gray_list);
|
||||
|
||||
// The response is of non-trivial type and so is encoded as JSON.
|
||||
// Each peer list is going to look like this:
|
||||
// {"peers": [{"id": ....}, ...]}
|
||||
|
||||
rapidjson::Document white_list_json;
|
||||
white_list_json.SetObject();
|
||||
rapidjson::Document::AllocatorType &white_list_allocator = white_list_json.GetAllocator();
|
||||
rapidjson::Value white_peers(rapidjson::kArrayType);
|
||||
|
||||
for (auto & entry : white_list) {
|
||||
// Each peer object is encoded as JSON
|
||||
rapidjson::Value output(rapidjson::kObjectType);
|
||||
output.AddMember("id", entry.id, white_list_allocator);
|
||||
output.AddMember("ip", entry.adr.ip, white_list_allocator);
|
||||
output.AddMember("port", entry.adr.port, white_list_allocator);
|
||||
output.AddMember("last_seen", entry.last_seen, white_list_allocator);
|
||||
white_peers.PushBack(output, white_list_allocator);
|
||||
}
|
||||
white_list_json.AddMember("peers", white_peers, white_list_allocator);
|
||||
|
||||
rapidjson::Document gray_list_json;
|
||||
gray_list_json.SetObject();
|
||||
rapidjson::Document::AllocatorType &gray_list_allocator = gray_list_json.GetAllocator();
|
||||
rapidjson::Value gray_peers(rapidjson::kArrayType);
|
||||
|
||||
for (auto & entry : gray_list) {
|
||||
// Each peer object is encoded as JSON
|
||||
rapidjson::Value output(rapidjson::kObjectType);
|
||||
output.AddMember("id", entry.id, gray_list_allocator);
|
||||
output.AddMember("ip", entry.adr.ip, gray_list_allocator);
|
||||
output.AddMember("port", entry.adr.port, gray_list_allocator);
|
||||
output.AddMember("last_seen", entry.last_seen, gray_list_allocator);
|
||||
gray_peers.PushBack(output, gray_list_allocator);
|
||||
}
|
||||
gray_list_json.AddMember("peers", gray_peers, gray_list_allocator);
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
white_list_json.Accept(writer);
|
||||
std::string white_list_string = buffer.GetString();
|
||||
|
||||
zframe_t *white_list_frame = zframe_new(white_list_string.c_str(), white_list_string.length());
|
||||
|
||||
buffer.Clear();
|
||||
gray_list_json.Accept(writer);
|
||||
std::string gray_list_string = buffer.GetString();
|
||||
zframe_t *gray_list_frame = zframe_new(gray_list_string.c_str(), gray_list_string.length());
|
||||
|
||||
wap_proto_set_white_list(message, &white_list_frame);
|
||||
wap_proto_set_gray_list(message, &gray_list_frame);
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief get_mining_status IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void get_mining_status(wap_proto_t *message) {
|
||||
if (!check_core_ready()) {
|
||||
wap_proto_set_status(message, STATUS_CORE_BUSY);
|
||||
return;
|
||||
}
|
||||
const cryptonote::miner& lMiner = core->get_miner();
|
||||
wap_proto_set_active(message, lMiner.is_mining() ? 1 : 0);
|
||||
|
||||
if (lMiner.is_mining()) {
|
||||
wap_proto_set_speed(message, lMiner.get_speed());
|
||||
wap_proto_set_thread_count(message, lMiner.get_threads_count());
|
||||
const cryptonote::account_public_address& lMiningAdr = lMiner.get_mining_address();
|
||||
std::string address = get_account_address_as_str(testnet, lMiningAdr);
|
||||
zchunk_t *address_chunk = zchunk_new((void*)address.c_str(), address.length());
|
||||
wap_proto_set_address(message, &address_chunk);
|
||||
}
|
||||
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief set_log_hash_rate IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void set_log_hash_rate(wap_proto_t *message) {
|
||||
if (core->get_miner().is_mining())
|
||||
{
|
||||
core->get_miner().do_print_hashrate(wap_proto_visible(message));
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
else
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_NOT_MINING);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief set_log_hash_rate IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void set_log_level(wap_proto_t *message) {
|
||||
// zproto supports only unsigned integers afaik. so the log level is sent as
|
||||
// one and casted to signed int here.
|
||||
int8_t level = (int8_t)wap_proto_level(message);
|
||||
if (level < LOG_LEVEL_MIN || level > LOG_LEVEL_MAX)
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_INVALID_LOG_LEVEL);
|
||||
}
|
||||
else
|
||||
{
|
||||
epee::log_space::log_singletone::get_set_log_detalisation_level(true, level);
|
||||
int otshell_utils_log_level = 100 - (level * 20);
|
||||
gCurrentLogger.setDebugLevel(otshell_utils_log_level);
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief start_save_graph IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void start_save_graph(wap_proto_t *message) {
|
||||
p2p->set_save_graph(true);
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief stop_save_graph IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void stop_save_graph(wap_proto_t *message) {
|
||||
p2p->set_save_graph(false);
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief get_block_hash IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void get_block_hash(wap_proto_t *message) {
|
||||
if (!check_core_busy())
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_CORE_BUSY);
|
||||
return;
|
||||
}
|
||||
uint64_t height = wap_proto_height(message);
|
||||
if (core->get_current_blockchain_height() <= height)
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_HEIGHT_TOO_BIG);
|
||||
return;
|
||||
}
|
||||
std::string hash = string_tools::pod_to_hex(core->get_block_id_by_height(height));
|
||||
zchunk_t *hash_chunk = zchunk_new((void*)(hash.c_str()), hash.length());
|
||||
wap_proto_set_hash(message, &hash_chunk);
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief get_block_template IPC
|
||||
*
|
||||
* \param message 0MQ response object to populate
|
||||
*/
|
||||
void get_block_template(wap_proto_t *message) {
|
||||
if (!check_core_ready())
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_CORE_BUSY);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t reserve_size = wap_proto_reserve_size(message);
|
||||
if (reserve_size > 255)
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_RESERVE_SIZE_TOO_BIG);
|
||||
return;
|
||||
}
|
||||
|
||||
cryptonote::account_public_address acc = AUTO_VAL_INIT(acc);
|
||||
|
||||
zchunk_t *address_chunk = wap_proto_address(message);
|
||||
std::string address((char*)zchunk_data(address_chunk), zchunk_size(address_chunk));
|
||||
if (!address.size() || !cryptonote::get_account_address_from_str(acc, testnet, address))
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_WRONG_ADDRESS);
|
||||
return;
|
||||
}
|
||||
|
||||
cryptonote::block b = AUTO_VAL_INIT(b);
|
||||
cryptonote::blobdata blob_reserve;
|
||||
blob_reserve.resize(reserve_size, 0);
|
||||
uint64_t difficulty = wap_proto_difficulty(message);
|
||||
uint64_t height;
|
||||
if (!core->get_block_template(b, acc, difficulty, height, blob_reserve))
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
cryptonote::blobdata block_blob = t_serializable_object_to_blob(b);
|
||||
crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx);
|
||||
if (tx_pub_key == cryptonote::null_pkey)
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
uint64_t reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key));
|
||||
if (!reserved_offset)
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
reserved_offset += sizeof(tx_pub_key) + 3; // 3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
|
||||
if (reserved_offset + reserve_size > block_blob.size())
|
||||
{
|
||||
wap_proto_set_status(message, STATUS_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
wap_proto_set_height(message, height);
|
||||
wap_proto_set_difficulty(message, difficulty);
|
||||
wap_proto_set_reserved_offset(message, reserved_offset);
|
||||
std::string prev_hash = string_tools::pod_to_hex(b.prev_id);
|
||||
zchunk_t *prev_hash_chunk = zchunk_new((void*)prev_hash.c_str(), prev_hash.length());
|
||||
wap_proto_set_prev_hash(message, &prev_hash_chunk);
|
||||
|
||||
cryptonote::blobdata blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob);
|
||||
zchunk_t *blob_chunk = zchunk_new((void*)blocktemplate_blob.c_str(), blocktemplate_blob.length());
|
||||
wap_proto_set_block_template_blob(message, &blob_chunk);
|
||||
wap_proto_set_status(message, STATUS_OK);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
// Copyright (c) 2014, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
/*!
|
||||
* \file daemon_ipc_handlers.h
|
||||
* \brief Header for Daemon IPC handlers
|
||||
*/
|
||||
|
||||
#ifndef DAEMON_IPC_HANDLERS_H
|
||||
#define DAEMON_IPC_HANDLERS_H
|
||||
|
||||
#include "include_base_utils.h"
|
||||
using namespace epee;
|
||||
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "p2p/net_node.h"
|
||||
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
|
||||
#include "common/command_line.h"
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
#include "cryptonote_core/account.h"
|
||||
#include "misc_language.h"
|
||||
#include "string_tools.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "wap_library.h"
|
||||
#include "wap_classes.h"
|
||||
#include "net/http_server_impl_base.h"
|
||||
#include "cryptonote_core/cryptonote_basic_impl.h"
|
||||
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/writer.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
|
||||
/*!
|
||||
* \namespace IPC
|
||||
* \brief Namespace pertaining to IPC.
|
||||
*/
|
||||
namespace IPC
|
||||
{
|
||||
// A bunch of response statuses and error codes
|
||||
const uint64_t STATUS_OK = 0;
|
||||
const uint64_t STATUS_CORE_BUSY = 1;
|
||||
const uint64_t STATUS_WRONG_ADDRESS = 2;
|
||||
const uint64_t STATUS_MINING_NOT_STARTED = 3;
|
||||
const uint64_t STATUS_WRONG_BLOCK_ID_LENGTH = 4;
|
||||
const uint64_t STATUS_INTERNAL_ERROR = 5;
|
||||
const uint64_t STATUS_INVALID_TX = 6;
|
||||
const uint64_t STATUS_TX_VERIFICATION_FAILED = 7;
|
||||
const uint64_t STATUS_TX_NOT_RELAYED = 8;
|
||||
const uint64_t STATUS_RANDOM_OUTS_FAILED = 9;
|
||||
const uint64_t STATUS_MINING_NOT_STOPPED = 10;
|
||||
const uint64_t STATUS_NOT_MINING = 11;
|
||||
const uint64_t STATUS_INVALID_LOG_LEVEL = 12;
|
||||
const uint64_t STATUS_ERROR_STORING_BLOCKCHAIN = 13;
|
||||
const uint64_t STATUS_HEIGHT_TOO_BIG = 13;
|
||||
const uint64_t STATUS_RESERVE_SIZE_TOO_BIG = 14;
|
||||
/*!
|
||||
* \namespace Daemon
|
||||
* \brief Namespace pertaining to Daemon IPC.
|
||||
*/
|
||||
namespace Daemon
|
||||
{
|
||||
void get_height(wap_proto_t *message);
|
||||
void start_mining(wap_proto_t *message);
|
||||
void stop_mining(wap_proto_t *message);
|
||||
void get_info(wap_proto_t *message);
|
||||
void get_peer_list(wap_proto_t *message);
|
||||
void get_mining_status(wap_proto_t *message);
|
||||
void set_log_hash_rate(wap_proto_t *message);
|
||||
void set_log_level(wap_proto_t *message);
|
||||
void start_save_graph(wap_proto_t *message);
|
||||
void stop_save_graph(wap_proto_t *message);
|
||||
void get_block_hash(wap_proto_t *message);
|
||||
void get_block_template(wap_proto_t *message);
|
||||
void retrieve_blocks(wap_proto_t *message);
|
||||
void send_raw_transaction(wap_proto_t *message);
|
||||
void get_output_indexes(wap_proto_t *message);
|
||||
void get_random_outs(wap_proto_t *message);
|
||||
void save_bc(wap_proto_t *message);
|
||||
|
||||
/*!
|
||||
* \brief initializes it with objects necessary to handle IPC requests and starts
|
||||
* IPC server
|
||||
*
|
||||
* \param p_core cryptonote core object
|
||||
* \param p_p2p p2p object
|
||||
* \param p_testnet testnet mode or not
|
||||
*/
|
||||
void init(cryptonote::core &p_core,
|
||||
nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> > &p_p2p,
|
||||
bool p_testnet);
|
||||
|
||||
/*!
|
||||
* \brief stops the IPC server
|
||||
*
|
||||
* \param p_core cryptonote core object
|
||||
* \param p_p2p p2p object
|
||||
* \param p_testnet testnet mode or not
|
||||
*/
|
||||
void stop();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,18 @@
|
|||
/* =========================================================================
|
||||
wallet - Monero Wallet API
|
||||
|
||||
Copyright (c) the Contributors as noted in the AUTHORS file.
|
||||
|
||||
(insert license text here)
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __WALLET_H_INCLUDED__
|
||||
#define __WALLET_H_INCLUDED__
|
||||
|
||||
// Include the project library file
|
||||
#include "wap_library.h"
|
||||
|
||||
// Add your own public definitions here, if you need them
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
/* =========================================================================
|
||||
wap_classes - private header file
|
||||
|
||||
Copyright (c) the Contributors as noted in the AUTHORS file.
|
||||
|
||||
(insert license text here)
|
||||
################################################################################
|
||||
# THIS FILE IS 100% GENERATED BY ZPROJECT; DO NOT EDIT EXCEPT EXPERIMENTALLY #
|
||||
# Please refer to the README for information about making permanent changes. #
|
||||
################################################################################
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __WAP_CLASSES_H_INCLUDED__
|
||||
#define __WAP_CLASSES_H_INCLUDED__
|
||||
|
||||
// External API
|
||||
#include "../include/wallet.h"
|
||||
|
||||
// Internal API
|
||||
|
||||
#endif
|
|
@ -0,0 +1,286 @@
|
|||
/* =========================================================================
|
||||
wap_client - Wallet Client API
|
||||
|
||||
** WARNING *************************************************************
|
||||
THIS SOURCE FILE IS 100% GENERATED. If you edit this file, you will lose
|
||||
your changes at the next build cycle. This is great for temporary printf
|
||||
statements. DO NOT MAKE ANY CHANGES YOU WISH TO KEEP. The correct places
|
||||
for commits are:
|
||||
|
||||
* The XML model used for this code generation: wap_client.xml, or
|
||||
* The code generation script that built this file: zproto_client_c
|
||||
************************************************************************
|
||||
Copyright (c) the Contributors as noted in the AUTHORS file.
|
||||
|
||||
(insert license text here)
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
#ifndef WAP_CLIENT_H_INCLUDED
|
||||
#define WAP_CLIENT_H_INCLUDED
|
||||
|
||||
#include <czmq.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Opaque class structure
|
||||
#ifndef WAP_CLIENT_T_DEFINED
|
||||
typedef struct _wap_client_t wap_client_t;
|
||||
#define WAP_CLIENT_T_DEFINED
|
||||
#endif
|
||||
|
||||
// @interface
|
||||
// Create a new wap_client, return the reference if successful, or NULL
|
||||
// if construction failed due to lack of available memory.
|
||||
WAP_EXPORT wap_client_t *
|
||||
wap_client_new (void);
|
||||
|
||||
// Destroy the wap_client and free all memory used by the object.
|
||||
WAP_EXPORT void
|
||||
wap_client_destroy (wap_client_t **self_p);
|
||||
|
||||
// Return actor, when caller wants to work with multiple actors and/or
|
||||
// input sockets asynchronously.
|
||||
WAP_EXPORT zactor_t *
|
||||
wap_client_actor (wap_client_t *self);
|
||||
|
||||
// Return message pipe for asynchronous message I/O. In the high-volume case,
|
||||
// we send methods and get replies to the actor, in a synchronous manner, and
|
||||
// we send/recv high volume message data to a second pipe, the msgpipe. In
|
||||
// the low-volume case we can do everything over the actor pipe, if traffic
|
||||
// is never ambiguous.
|
||||
WAP_EXPORT zsock_t *
|
||||
wap_client_msgpipe (wap_client_t *self);
|
||||
|
||||
// Return true if client is currently connected, else false. Note that the
|
||||
// client will automatically re-connect if the server dies and restarts after
|
||||
// a successful first connection.
|
||||
WAP_EXPORT bool
|
||||
wap_client_connected (wap_client_t *self);
|
||||
|
||||
// Connect to server endpoint, with specified timeout in msecs (zero means wait
|
||||
// forever). Constructor succeeds if connection is successful. The caller may
|
||||
// specify its address.
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_connect (wap_client_t *self, const char *endpoint, uint32_t timeout, const char *identity);
|
||||
|
||||
// Request a set of blocks from the server.
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_blocks (wap_client_t *self, zlist_t **block_ids_p, uint64_t start_height);
|
||||
|
||||
// Send a raw transaction to the daemon.
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_put (wap_client_t *self, zchunk_t **tx_as_hex_p);
|
||||
|
||||
// Request a set of blocks from the server.
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_get (wap_client_t *self, zchunk_t **tx_id_p);
|
||||
|
||||
// Request a set of blocks from the server.
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_save_bc (wap_client_t *self);
|
||||
|
||||
// Ask for tx output indexes.
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_output_indexes (wap_client_t *self, zchunk_t **tx_id_p);
|
||||
|
||||
// Ask for tx output indexes.
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_random_outs (wap_client_t *self, uint64_t outs_count, zframe_t **amounts_p);
|
||||
|
||||
// Ask for height.
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_get_height (wap_client_t *self);
|
||||
|
||||
// Ask for height.
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_get_info (wap_client_t *self);
|
||||
|
||||
// Send start command to server.
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_start (wap_client_t *self, zchunk_t **address_p, uint64_t thread_count);
|
||||
|
||||
// Send stop command to server.
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_stop (wap_client_t *self);
|
||||
|
||||
// Get peer list
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_get_peer_list (wap_client_t *self);
|
||||
|
||||
// Get mining status
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_get_mining_status (wap_client_t *self);
|
||||
|
||||
// Set log hash rate
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_set_log_hash_rate (wap_client_t *self, uint8_t visible);
|
||||
|
||||
// Set log hash rate
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_set_log_level (wap_client_t *self, uint8_t level);
|
||||
|
||||
// Start save graph
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_start_save_graph (wap_client_t *self);
|
||||
|
||||
// Stop save graph
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_stop_save_graph (wap_client_t *self);
|
||||
|
||||
// Get block hash
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_get_block_hash (wap_client_t *self, uint64_t height);
|
||||
|
||||
// Get block template
|
||||
// Returns >= 0 if successful, -1 if interrupted.
|
||||
WAP_EXPORT int
|
||||
wap_client_get_block_template (wap_client_t *self, uint64_t reserve_size, zchunk_t **address_p);
|
||||
|
||||
// Return last received status
|
||||
WAP_EXPORT int
|
||||
wap_client_status (wap_client_t *self);
|
||||
|
||||
// Return last received reason
|
||||
WAP_EXPORT const char *
|
||||
wap_client_reason (wap_client_t *self);
|
||||
|
||||
// Return last received start_height
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_start_height (wap_client_t *self);
|
||||
|
||||
// Return last received curr_height
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_curr_height (wap_client_t *self);
|
||||
|
||||
// Return last received block_data
|
||||
WAP_EXPORT zmsg_t *
|
||||
wap_client_block_data (wap_client_t *self);
|
||||
|
||||
// Return last received tx_data
|
||||
WAP_EXPORT zchunk_t *
|
||||
wap_client_tx_data (wap_client_t *self);
|
||||
|
||||
// Return last received o_indexes
|
||||
WAP_EXPORT zframe_t *
|
||||
wap_client_o_indexes (wap_client_t *self);
|
||||
|
||||
// Return last received random_outputs
|
||||
WAP_EXPORT zframe_t *
|
||||
wap_client_random_outputs (wap_client_t *self);
|
||||
|
||||
// Return last received height
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_height (wap_client_t *self);
|
||||
|
||||
// Return last received target_height
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_target_height (wap_client_t *self);
|
||||
|
||||
// Return last received difficulty
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_difficulty (wap_client_t *self);
|
||||
|
||||
// Return last received tx_count
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_tx_count (wap_client_t *self);
|
||||
|
||||
// Return last received tx_pool_size
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_tx_pool_size (wap_client_t *self);
|
||||
|
||||
// Return last received alt_blocks_count
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_alt_blocks_count (wap_client_t *self);
|
||||
|
||||
// Return last received outgoing_connections_count
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_outgoing_connections_count (wap_client_t *self);
|
||||
|
||||
// Return last received incoming_connections_count
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_incoming_connections_count (wap_client_t *self);
|
||||
|
||||
// Return last received white_peerlist_size
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_white_peerlist_size (wap_client_t *self);
|
||||
|
||||
// Return last received grey_peerlist_size
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_grey_peerlist_size (wap_client_t *self);
|
||||
|
||||
// Return last received white_list
|
||||
WAP_EXPORT zframe_t *
|
||||
wap_client_white_list (wap_client_t *self);
|
||||
|
||||
// Return last received gray_list
|
||||
WAP_EXPORT zframe_t *
|
||||
wap_client_gray_list (wap_client_t *self);
|
||||
|
||||
// Return last received active
|
||||
WAP_EXPORT uint8_t
|
||||
wap_client_active (wap_client_t *self);
|
||||
|
||||
// Return last received speed
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_speed (wap_client_t *self);
|
||||
|
||||
// Return last received thread_count
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_thread_count (wap_client_t *self);
|
||||
|
||||
// Return last received address
|
||||
WAP_EXPORT zchunk_t *
|
||||
wap_client_address (wap_client_t *self);
|
||||
|
||||
// Return last received hash
|
||||
WAP_EXPORT zchunk_t *
|
||||
wap_client_hash (wap_client_t *self);
|
||||
|
||||
// Return last received reserved_offset
|
||||
WAP_EXPORT uint64_t
|
||||
wap_client_reserved_offset (wap_client_t *self);
|
||||
|
||||
// Return last received prev_hash
|
||||
WAP_EXPORT zchunk_t *
|
||||
wap_client_prev_hash (wap_client_t *self);
|
||||
|
||||
// Return last received block_template_blob
|
||||
WAP_EXPORT zchunk_t *
|
||||
wap_client_block_template_blob (wap_client_t *self);
|
||||
|
||||
// Self test of this class
|
||||
WAP_EXPORT void
|
||||
wap_client_test (bool verbose);
|
||||
|
||||
// To enable verbose tracing (animation) of wap_client instances, set
|
||||
// this to true. This lets you trace from and including construction.
|
||||
WAP_EXPORT extern volatile int
|
||||
wap_client_verbose;
|
||||
// @end
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,64 @@
|
|||
/* =========================================================================
|
||||
wallet - WALLET wrapper
|
||||
|
||||
Copyright (c) the Contributors as noted in the AUTHORS file.
|
||||
|
||||
(insert license text here)
|
||||
|
||||
################################################################################
|
||||
# THIS FILE IS 100% GENERATED BY ZPROJECT; DO NOT EDIT EXCEPT EXPERIMENTALLY #
|
||||
# Please refer to the README for information about making permanent changes. #
|
||||
################################################################################
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __wap_library_H_INCLUDED__
|
||||
#define __wap_library_H_INCLUDED__
|
||||
|
||||
// External dependencies
|
||||
#include <czmq.h>
|
||||
|
||||
// WALLET version macros for compile-time API detection
|
||||
|
||||
#define WALLET_VERSION_MAJOR 0
|
||||
#define WALLET_VERSION_MINOR 0
|
||||
#define WALLET_VERSION_PATCH 1
|
||||
|
||||
#define WALLET_MAKE_VERSION(major, minor, patch) \
|
||||
((major) * 10000 + (minor) * 100 + (patch))
|
||||
#define WALLET_VERSION \
|
||||
WALLET_MAKE_VERSION(WALLET_VERSION_MAJOR, WALLET_VERSION_MINOR, WALLET_VERSION_PATCH)
|
||||
|
||||
#if defined (__WINDOWS__)
|
||||
# if defined LIBWAP_STATIC
|
||||
# define WAP_EXPORT
|
||||
# elif defined LIBWAP_EXPORTS
|
||||
# define WAP_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define WAP_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define WAP_EXPORT
|
||||
#endif
|
||||
|
||||
// Opaque class structures to allow forward references
|
||||
typedef struct _wap_proto_t wap_proto_t;
|
||||
#define WAP_PROTO_T_DEFINED
|
||||
typedef struct _wap_server_t wap_server_t;
|
||||
#define WAP_SERVER_T_DEFINED
|
||||
typedef struct _wap_client_t wap_client_t;
|
||||
#define WAP_CLIENT_T_DEFINED
|
||||
|
||||
|
||||
// Public API classes
|
||||
#include "wap_proto.h"
|
||||
#include "wap_server.h"
|
||||
#include "wap_client.h"
|
||||
|
||||
#endif
|
||||
/*
|
||||
################################################################################
|
||||
# THIS FILE IS 100% GENERATED BY ZPROJECT; DO NOT EDIT EXCEPT EXPERIMENTALLY #
|
||||
# Please refer to the README for information about making permanent changes. #
|
||||
################################################################################
|
||||
*/
|
|
@ -0,0 +1,577 @@
|
|||
/* =========================================================================
|
||||
wap_proto - Wallet Access Protocol
|
||||
|
||||
Codec header for wap_proto.
|
||||
|
||||
** WARNING *************************************************************
|
||||
THIS SOURCE FILE IS 100% GENERATED. If you edit this file, you will lose
|
||||
your changes at the next build cycle. This is great for temporary printf
|
||||
statements. DO NOT MAKE ANY CHANGES YOU WISH TO KEEP. The correct places
|
||||
for commits are:
|
||||
|
||||
* The XML model used for this code generation: wap_proto.xml, or
|
||||
* The code generation script that built this file: zproto_codec_c
|
||||
************************************************************************
|
||||
Copyright (c) the Contributors as noted in the AUTHORS file.
|
||||
|
||||
(insert license text here)
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
#ifndef WAP_PROTO_H_INCLUDED
|
||||
#define WAP_PROTO_H_INCLUDED
|
||||
|
||||
/* These are the wap_proto messages:
|
||||
|
||||
OPEN - Wallet opens a connection to the daemon.
|
||||
protocol string
|
||||
version number 2 Protocol version 1
|
||||
identity string Wallet identity
|
||||
|
||||
OPEN_OK - Daemon accepts wallet open request.
|
||||
|
||||
BLOCKS - Wallet requests a set of blocks from the daemon. Daemon replies with
|
||||
BLOCKS-OK, or ERROR if the request is invalid.
|
||||
block_ids strings
|
||||
start_height number 8
|
||||
|
||||
BLOCKS_OK - Daemon returns a set of blocks to the wallet.
|
||||
status number 8
|
||||
start_height number 8
|
||||
curr_height number 8
|
||||
block_data msg Frames of block data
|
||||
|
||||
PUT - Wallet sends a raw transaction to the daemon. Daemon replies with
|
||||
PUT-OK, or ERROR.
|
||||
tx_as_hex chunk Transaction as hex
|
||||
|
||||
PUT_OK - Daemon confirms that it accepted the raw transaction.
|
||||
status number 8 Transaction ID
|
||||
|
||||
OUTPUT_INDEXES - Ask for tx output indexes.
|
||||
tx_id chunk Transaction ID
|
||||
|
||||
OUTPUT_INDEXES_OK - Daemon returns tx output indexes.
|
||||
status number 8 Status
|
||||
o_indexes frame Output Indexes
|
||||
|
||||
RANDOM_OUTS - Get random outputs for amounts.
|
||||
outs_count number 8 Outs count
|
||||
amounts frame Amounts
|
||||
|
||||
RANDOM_OUTS_OK - Daemon returns random outputs for amounts.
|
||||
status number 8 Status
|
||||
random_outputs frame Outputs
|
||||
|
||||
GET_HEIGHT - Get height.
|
||||
|
||||
GET_HEIGHT_OK - Daemon returns height.
|
||||
status number 8 Status
|
||||
height number 8 Height
|
||||
|
||||
GET - Wallet requests transaction data from the daemon. Daemon replies
|
||||
with GET-OK, or ERROR.
|
||||
tx_id chunk Transaction ID
|
||||
|
||||
GET_OK - Daemon replies with transaction data.
|
||||
tx_data chunk Transaction data
|
||||
|
||||
SAVE_BC - save_bc command. Details tbd.
|
||||
|
||||
SAVE_BC_OK - Daemon replies to a save_bc command.
|
||||
status number 8 Status
|
||||
|
||||
START - Wallet asks daemon to start mining. Daemon replies with START-OK, or
|
||||
ERROR.
|
||||
address chunk
|
||||
thread_count number 8
|
||||
|
||||
START_OK - Daemon replies to a start mining request.
|
||||
status number 8
|
||||
|
||||
GET_INFO - getinfo IPC
|
||||
|
||||
GET_INFO_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd)
|
||||
status number 8 Status
|
||||
height number 8 Height
|
||||
target_height number 8 Target Height
|
||||
difficulty number 8 Difficulty
|
||||
tx_count number 8 TX Count
|
||||
tx_pool_size number 8 TX Pool Size
|
||||
alt_blocks_count number 8 Alt Blocks Count
|
||||
outgoing_connections_count number 8 Outgoing Connections Count
|
||||
incoming_connections_count number 8 Incoming Connections Count
|
||||
white_peerlist_size number 8 White Peerlist Size
|
||||
grey_peerlist_size number 8 Grey Peerlist Size
|
||||
|
||||
GET_PEER_LIST - get_peer_list IPC
|
||||
|
||||
GET_PEER_LIST_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd)
|
||||
status number 8 Status
|
||||
white_list frame White list
|
||||
gray_list frame Gray list
|
||||
|
||||
GET_MINING_STATUS - get_mining_status IPC
|
||||
|
||||
GET_MINING_STATUS_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd)
|
||||
status number 8 Status
|
||||
active number 1 Active
|
||||
speed number 8 Speed
|
||||
thread_count number 8 Threads count
|
||||
address chunk Address
|
||||
|
||||
SET_LOG_HASH_RATE - set_log_hash_rate IPC
|
||||
visible number 1 Visible
|
||||
|
||||
SET_LOG_HASH_RATE_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd)
|
||||
status number 8 Status
|
||||
|
||||
SET_LOG_LEVEL - set_log_level IPC
|
||||
level number 1 Level
|
||||
|
||||
SET_LOG_LEVEL_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd)
|
||||
status number 8 Status
|
||||
|
||||
START_SAVE_GRAPH - start_save_graph IPC
|
||||
|
||||
START_SAVE_GRAPH_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd)
|
||||
status number 8 Status
|
||||
|
||||
STOP_SAVE_GRAPH - stop_save_graph IPC
|
||||
|
||||
STOP_SAVE_GRAPH_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd)
|
||||
status number 8 Status
|
||||
|
||||
GET_BLOCK_HASH - get_block_hash IPC
|
||||
height number 8 Height
|
||||
|
||||
GET_BLOCK_HASH_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd)
|
||||
status number 8 Status
|
||||
hash chunk Hash
|
||||
|
||||
GET_BLOCK_TEMPLATE - get_block_template IPC
|
||||
reserve_size number 8 Reserve size
|
||||
address chunk Address
|
||||
|
||||
GET_BLOCK_TEMPLATE_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd)
|
||||
status number 8 Status
|
||||
reserved_offset number 8 Rservered Offset
|
||||
height number 8 Height
|
||||
difficulty number 8 Difficulty
|
||||
prev_hash chunk Previous Hash
|
||||
block_template_blob chunk Block template blob
|
||||
|
||||
STOP - Wallet asks daemon to start mining. Daemon replies with STOP-OK, or
|
||||
ERROR.
|
||||
|
||||
STOP_OK - Daemon replies to a stop mining request.
|
||||
|
||||
CLOSE - Wallet closes the connection. This is polite though not mandatory.
|
||||
Daemon will reply with CLOSE-OK or ERROR.
|
||||
|
||||
CLOSE_OK - Daemon replies to a wallet connection close request.
|
||||
|
||||
PING - Wallet heartbeats an idle connection.
|
||||
|
||||
PING_OK - Daemon replies to a wallet ping request.
|
||||
|
||||
ERROR - Daemon replies with failure status. Status codes tbd.
|
||||
status number 2 Error status
|
||||
reason string Printable explanation
|
||||
*/
|
||||
|
||||
#define WAP_PROTO_SUCCESS 200
|
||||
#define WAP_PROTO_NOT_DELIVERED 300
|
||||
#define WAP_PROTO_CONTENT_TOO_LARGE 301
|
||||
#define WAP_PROTO_TIMEOUT_EXPIRED 302
|
||||
#define WAP_PROTO_CONNECTION_REFUSED 303
|
||||
#define WAP_PROTO_RESOURCE_LOCKED 400
|
||||
#define WAP_PROTO_ACCESS_REFUSED 401
|
||||
#define WAP_PROTO_NOT_FOUND 404
|
||||
#define WAP_PROTO_COMMAND_INVALID 500
|
||||
#define WAP_PROTO_NOT_IMPLEMENTED 501
|
||||
#define WAP_PROTO_INTERNAL_ERROR 502
|
||||
|
||||
#define WAP_PROTO_OPEN 1
|
||||
#define WAP_PROTO_OPEN_OK 2
|
||||
#define WAP_PROTO_BLOCKS 3
|
||||
#define WAP_PROTO_BLOCKS_OK 4
|
||||
#define WAP_PROTO_PUT 5
|
||||
#define WAP_PROTO_PUT_OK 6
|
||||
#define WAP_PROTO_OUTPUT_INDEXES 7
|
||||
#define WAP_PROTO_OUTPUT_INDEXES_OK 8
|
||||
#define WAP_PROTO_RANDOM_OUTS 9
|
||||
#define WAP_PROTO_RANDOM_OUTS_OK 10
|
||||
#define WAP_PROTO_GET_HEIGHT 11
|
||||
#define WAP_PROTO_GET_HEIGHT_OK 12
|
||||
#define WAP_PROTO_GET 13
|
||||
#define WAP_PROTO_GET_OK 14
|
||||
#define WAP_PROTO_SAVE_BC 15
|
||||
#define WAP_PROTO_SAVE_BC_OK 16
|
||||
#define WAP_PROTO_START 17
|
||||
#define WAP_PROTO_START_OK 18
|
||||
#define WAP_PROTO_GET_INFO 19
|
||||
#define WAP_PROTO_GET_INFO_OK 20
|
||||
#define WAP_PROTO_GET_PEER_LIST 21
|
||||
#define WAP_PROTO_GET_PEER_LIST_OK 22
|
||||
#define WAP_PROTO_GET_MINING_STATUS 23
|
||||
#define WAP_PROTO_GET_MINING_STATUS_OK 24
|
||||
#define WAP_PROTO_SET_LOG_HASH_RATE 25
|
||||
#define WAP_PROTO_SET_LOG_HASH_RATE_OK 26
|
||||
#define WAP_PROTO_SET_LOG_LEVEL 27
|
||||
#define WAP_PROTO_SET_LOG_LEVEL_OK 28
|
||||
#define WAP_PROTO_START_SAVE_GRAPH 29
|
||||
#define WAP_PROTO_START_SAVE_GRAPH_OK 30
|
||||
#define WAP_PROTO_STOP_SAVE_GRAPH 31
|
||||
#define WAP_PROTO_STOP_SAVE_GRAPH_OK 32
|
||||
#define WAP_PROTO_GET_BLOCK_HASH 33
|
||||
#define WAP_PROTO_GET_BLOCK_HASH_OK 34
|
||||
#define WAP_PROTO_GET_BLOCK_TEMPLATE 35
|
||||
#define WAP_PROTO_GET_BLOCK_TEMPLATE_OK 36
|
||||
#define WAP_PROTO_STOP 37
|
||||
#define WAP_PROTO_STOP_OK 38
|
||||
#define WAP_PROTO_CLOSE 39
|
||||
#define WAP_PROTO_CLOSE_OK 40
|
||||
#define WAP_PROTO_PING 41
|
||||
#define WAP_PROTO_PING_OK 42
|
||||
#define WAP_PROTO_ERROR 43
|
||||
|
||||
#include <czmq.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Opaque class structure
|
||||
#ifndef WAP_PROTO_T_DEFINED
|
||||
typedef struct _wap_proto_t wap_proto_t;
|
||||
#define WAP_PROTO_T_DEFINED
|
||||
#endif
|
||||
|
||||
// @interface
|
||||
// Create a new empty wap_proto
|
||||
wap_proto_t *
|
||||
wap_proto_new (void);
|
||||
|
||||
// Destroy a wap_proto instance
|
||||
void
|
||||
wap_proto_destroy (wap_proto_t **self_p);
|
||||
|
||||
// Receive a wap_proto from the socket. Returns 0 if OK, -1 if
|
||||
// there was an error. Blocks if there is no message waiting.
|
||||
int
|
||||
wap_proto_recv (wap_proto_t *self, zsock_t *input);
|
||||
|
||||
// Send the wap_proto to the output socket, does not destroy it
|
||||
int
|
||||
wap_proto_send (wap_proto_t *self, zsock_t *output);
|
||||
|
||||
// Print contents of message to stdout
|
||||
void
|
||||
wap_proto_print (wap_proto_t *self);
|
||||
|
||||
// Get/set the message routing id
|
||||
zframe_t *
|
||||
wap_proto_routing_id (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_routing_id (wap_proto_t *self, zframe_t *routing_id);
|
||||
|
||||
// Get the wap_proto id and printable command
|
||||
int
|
||||
wap_proto_id (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_id (wap_proto_t *self, int id);
|
||||
const char *
|
||||
wap_proto_command (wap_proto_t *self);
|
||||
|
||||
// Get/set the identity field
|
||||
const char *
|
||||
wap_proto_identity (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_identity (wap_proto_t *self, const char *value);
|
||||
|
||||
// Get/set the block_ids field
|
||||
zlist_t *
|
||||
wap_proto_block_ids (wap_proto_t *self);
|
||||
// Get the block_ids field and transfer ownership to caller
|
||||
zlist_t *
|
||||
wap_proto_get_block_ids (wap_proto_t *self);
|
||||
// Set the block_ids field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_block_ids (wap_proto_t *self, zlist_t **block_ids_p);
|
||||
|
||||
// Get/set the start_height field
|
||||
uint64_t
|
||||
wap_proto_start_height (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_start_height (wap_proto_t *self, uint64_t start_height);
|
||||
|
||||
// Get/set the status field
|
||||
uint64_t
|
||||
wap_proto_status (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_status (wap_proto_t *self, uint64_t status);
|
||||
|
||||
// Get/set the curr_height field
|
||||
uint64_t
|
||||
wap_proto_curr_height (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_curr_height (wap_proto_t *self, uint64_t curr_height);
|
||||
|
||||
// Get a copy of the block_data field
|
||||
zmsg_t *
|
||||
wap_proto_block_data (wap_proto_t *self);
|
||||
// Get the block_data field and transfer ownership to caller
|
||||
zmsg_t *
|
||||
wap_proto_get_block_data (wap_proto_t *self);
|
||||
// Set the block_data field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_block_data (wap_proto_t *self, zmsg_t **msg_p);
|
||||
|
||||
// Get a copy of the tx_as_hex field
|
||||
zchunk_t *
|
||||
wap_proto_tx_as_hex (wap_proto_t *self);
|
||||
// Get the tx_as_hex field and transfer ownership to caller
|
||||
zchunk_t *
|
||||
wap_proto_get_tx_as_hex (wap_proto_t *self);
|
||||
// Set the tx_as_hex field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_tx_as_hex (wap_proto_t *self, zchunk_t **chunk_p);
|
||||
|
||||
// Get a copy of the tx_id field
|
||||
zchunk_t *
|
||||
wap_proto_tx_id (wap_proto_t *self);
|
||||
// Get the tx_id field and transfer ownership to caller
|
||||
zchunk_t *
|
||||
wap_proto_get_tx_id (wap_proto_t *self);
|
||||
// Set the tx_id field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_tx_id (wap_proto_t *self, zchunk_t **chunk_p);
|
||||
|
||||
// Get a copy of the o_indexes field
|
||||
zframe_t *
|
||||
wap_proto_o_indexes (wap_proto_t *self);
|
||||
// Get the o_indexes field and transfer ownership to caller
|
||||
zframe_t *
|
||||
wap_proto_get_o_indexes (wap_proto_t *self);
|
||||
// Set the o_indexes field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_o_indexes (wap_proto_t *self, zframe_t **frame_p);
|
||||
|
||||
// Get/set the outs_count field
|
||||
uint64_t
|
||||
wap_proto_outs_count (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_outs_count (wap_proto_t *self, uint64_t outs_count);
|
||||
|
||||
// Get a copy of the amounts field
|
||||
zframe_t *
|
||||
wap_proto_amounts (wap_proto_t *self);
|
||||
// Get the amounts field and transfer ownership to caller
|
||||
zframe_t *
|
||||
wap_proto_get_amounts (wap_proto_t *self);
|
||||
// Set the amounts field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_amounts (wap_proto_t *self, zframe_t **frame_p);
|
||||
|
||||
// Get a copy of the random_outputs field
|
||||
zframe_t *
|
||||
wap_proto_random_outputs (wap_proto_t *self);
|
||||
// Get the random_outputs field and transfer ownership to caller
|
||||
zframe_t *
|
||||
wap_proto_get_random_outputs (wap_proto_t *self);
|
||||
// Set the random_outputs field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_random_outputs (wap_proto_t *self, zframe_t **frame_p);
|
||||
|
||||
// Get/set the height field
|
||||
uint64_t
|
||||
wap_proto_height (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_height (wap_proto_t *self, uint64_t height);
|
||||
|
||||
// Get a copy of the tx_data field
|
||||
zchunk_t *
|
||||
wap_proto_tx_data (wap_proto_t *self);
|
||||
// Get the tx_data field and transfer ownership to caller
|
||||
zchunk_t *
|
||||
wap_proto_get_tx_data (wap_proto_t *self);
|
||||
// Set the tx_data field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_tx_data (wap_proto_t *self, zchunk_t **chunk_p);
|
||||
|
||||
// Get a copy of the address field
|
||||
zchunk_t *
|
||||
wap_proto_address (wap_proto_t *self);
|
||||
// Get the address field and transfer ownership to caller
|
||||
zchunk_t *
|
||||
wap_proto_get_address (wap_proto_t *self);
|
||||
// Set the address field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_address (wap_proto_t *self, zchunk_t **chunk_p);
|
||||
|
||||
// Get/set the thread_count field
|
||||
uint64_t
|
||||
wap_proto_thread_count (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_thread_count (wap_proto_t *self, uint64_t thread_count);
|
||||
|
||||
// Get/set the target_height field
|
||||
uint64_t
|
||||
wap_proto_target_height (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_target_height (wap_proto_t *self, uint64_t target_height);
|
||||
|
||||
// Get/set the difficulty field
|
||||
uint64_t
|
||||
wap_proto_difficulty (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_difficulty (wap_proto_t *self, uint64_t difficulty);
|
||||
|
||||
// Get/set the tx_count field
|
||||
uint64_t
|
||||
wap_proto_tx_count (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_tx_count (wap_proto_t *self, uint64_t tx_count);
|
||||
|
||||
// Get/set the tx_pool_size field
|
||||
uint64_t
|
||||
wap_proto_tx_pool_size (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_tx_pool_size (wap_proto_t *self, uint64_t tx_pool_size);
|
||||
|
||||
// Get/set the alt_blocks_count field
|
||||
uint64_t
|
||||
wap_proto_alt_blocks_count (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_alt_blocks_count (wap_proto_t *self, uint64_t alt_blocks_count);
|
||||
|
||||
// Get/set the outgoing_connections_count field
|
||||
uint64_t
|
||||
wap_proto_outgoing_connections_count (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_outgoing_connections_count (wap_proto_t *self, uint64_t outgoing_connections_count);
|
||||
|
||||
// Get/set the incoming_connections_count field
|
||||
uint64_t
|
||||
wap_proto_incoming_connections_count (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_incoming_connections_count (wap_proto_t *self, uint64_t incoming_connections_count);
|
||||
|
||||
// Get/set the white_peerlist_size field
|
||||
uint64_t
|
||||
wap_proto_white_peerlist_size (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_white_peerlist_size (wap_proto_t *self, uint64_t white_peerlist_size);
|
||||
|
||||
// Get/set the grey_peerlist_size field
|
||||
uint64_t
|
||||
wap_proto_grey_peerlist_size (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_grey_peerlist_size (wap_proto_t *self, uint64_t grey_peerlist_size);
|
||||
|
||||
// Get a copy of the white_list field
|
||||
zframe_t *
|
||||
wap_proto_white_list (wap_proto_t *self);
|
||||
// Get the white_list field and transfer ownership to caller
|
||||
zframe_t *
|
||||
wap_proto_get_white_list (wap_proto_t *self);
|
||||
// Set the white_list field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_white_list (wap_proto_t *self, zframe_t **frame_p);
|
||||
|
||||
// Get a copy of the gray_list field
|
||||
zframe_t *
|
||||
wap_proto_gray_list (wap_proto_t *self);
|
||||
// Get the gray_list field and transfer ownership to caller
|
||||
zframe_t *
|
||||
wap_proto_get_gray_list (wap_proto_t *self);
|
||||
// Set the gray_list field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_gray_list (wap_proto_t *self, zframe_t **frame_p);
|
||||
|
||||
// Get/set the active field
|
||||
byte
|
||||
wap_proto_active (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_active (wap_proto_t *self, byte active);
|
||||
|
||||
// Get/set the speed field
|
||||
uint64_t
|
||||
wap_proto_speed (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_speed (wap_proto_t *self, uint64_t speed);
|
||||
|
||||
// Get/set the visible field
|
||||
byte
|
||||
wap_proto_visible (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_visible (wap_proto_t *self, byte visible);
|
||||
|
||||
// Get/set the level field
|
||||
byte
|
||||
wap_proto_level (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_level (wap_proto_t *self, byte level);
|
||||
|
||||
// Get a copy of the hash field
|
||||
zchunk_t *
|
||||
wap_proto_hash (wap_proto_t *self);
|
||||
// Get the hash field and transfer ownership to caller
|
||||
zchunk_t *
|
||||
wap_proto_get_hash (wap_proto_t *self);
|
||||
// Set the hash field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_hash (wap_proto_t *self, zchunk_t **chunk_p);
|
||||
|
||||
// Get/set the reserve_size field
|
||||
uint64_t
|
||||
wap_proto_reserve_size (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_reserve_size (wap_proto_t *self, uint64_t reserve_size);
|
||||
|
||||
// Get/set the reserved_offset field
|
||||
uint64_t
|
||||
wap_proto_reserved_offset (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_reserved_offset (wap_proto_t *self, uint64_t reserved_offset);
|
||||
|
||||
// Get a copy of the prev_hash field
|
||||
zchunk_t *
|
||||
wap_proto_prev_hash (wap_proto_t *self);
|
||||
// Get the prev_hash field and transfer ownership to caller
|
||||
zchunk_t *
|
||||
wap_proto_get_prev_hash (wap_proto_t *self);
|
||||
// Set the prev_hash field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_prev_hash (wap_proto_t *self, zchunk_t **chunk_p);
|
||||
|
||||
// Get a copy of the block_template_blob field
|
||||
zchunk_t *
|
||||
wap_proto_block_template_blob (wap_proto_t *self);
|
||||
// Get the block_template_blob field and transfer ownership to caller
|
||||
zchunk_t *
|
||||
wap_proto_get_block_template_blob (wap_proto_t *self);
|
||||
// Set the block_template_blob field, transferring ownership from caller
|
||||
void
|
||||
wap_proto_set_block_template_blob (wap_proto_t *self, zchunk_t **chunk_p);
|
||||
|
||||
// Get/set the reason field
|
||||
const char *
|
||||
wap_proto_reason (wap_proto_t *self);
|
||||
void
|
||||
wap_proto_set_reason (wap_proto_t *self, const char *value);
|
||||
|
||||
// Self test of this class
|
||||
int
|
||||
wap_proto_test (bool verbose);
|
||||
// @end
|
||||
|
||||
// For backwards compatibility with old codecs
|
||||
#define wap_proto_dump wap_proto_print
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,91 @@
|
|||
/* =========================================================================
|
||||
wap_server - Wallet Server
|
||||
|
||||
** WARNING *************************************************************
|
||||
THIS SOURCE FILE IS 100% GENERATED. If you edit this file, you will lose
|
||||
your changes at the next build cycle. This is great for temporary printf
|
||||
statements. DO NOT MAKE ANY CHANGES YOU WISH TO KEEP. The correct places
|
||||
for commits are:
|
||||
|
||||
* The XML model used for this code generation: wap_server.xml, or
|
||||
* The code generation script that built this file: zproto_server_c
|
||||
************************************************************************
|
||||
Copyright (c) the Contributors as noted in the AUTHORS file.
|
||||
|
||||
(insert license text here)
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
#ifndef WAP_SERVER_H_INCLUDED
|
||||
#define WAP_SERVER_H_INCLUDED
|
||||
|
||||
#include <czmq.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// @interface
|
||||
// To work with wap_server, use the CZMQ zactor API:
|
||||
//
|
||||
// Create new wap_server instance, passing logging prefix:
|
||||
//
|
||||
// zactor_t *wap_server = zactor_new (wap_server, "myname");
|
||||
//
|
||||
// Destroy wap_server instance
|
||||
//
|
||||
// zactor_destroy (&wap_server);
|
||||
//
|
||||
// Enable verbose logging of commands and activity:
|
||||
//
|
||||
// zstr_send (wap_server, "VERBOSE");
|
||||
//
|
||||
// Bind wap_server to specified endpoint. TCP endpoints may specify
|
||||
// the port number as "*" to aquire an ephemeral port:
|
||||
//
|
||||
// zstr_sendx (wap_server, "BIND", endpoint, NULL);
|
||||
//
|
||||
// Return assigned port number, specifically when BIND was done using an
|
||||
// an ephemeral port:
|
||||
//
|
||||
// zstr_sendx (wap_server, "PORT", NULL);
|
||||
// char *command, *port_str;
|
||||
// zstr_recvx (wap_server, &command, &port_str, NULL);
|
||||
// assert (streq (command, "PORT"));
|
||||
//
|
||||
// Specify configuration file to load, overwriting any previous loaded
|
||||
// configuration file or options:
|
||||
//
|
||||
// zstr_sendx (wap_server, "LOAD", filename, NULL);
|
||||
//
|
||||
// Set configuration path value:
|
||||
//
|
||||
// zstr_sendx (wap_server, "SET", path, value, NULL);
|
||||
//
|
||||
// Save configuration data to config file on disk:
|
||||
//
|
||||
// zstr_sendx (wap_server, "SAVE", filename, NULL);
|
||||
//
|
||||
// Send zmsg_t instance to wap_server:
|
||||
//
|
||||
// zactor_send (wap_server, &msg);
|
||||
//
|
||||
// Receive zmsg_t instance from wap_server:
|
||||
//
|
||||
// zmsg_t *msg = zactor_recv (wap_server);
|
||||
//
|
||||
// This is the wap_server constructor as a zactor_fn:
|
||||
//
|
||||
WAP_EXPORT void
|
||||
wap_server (zsock_t *pipe, void *args);
|
||||
|
||||
// Self test of this class
|
||||
WAP_EXPORT void
|
||||
wap_server_test (bool verbose);
|
||||
// @end
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,544 @@
|
|||
/* =========================================================================
|
||||
wap_client - Wallet Client API
|
||||
|
||||
Copyright (c) the Contributors as noted in the AUTHORS file.
|
||||
|
||||
(insert license text here)
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@header
|
||||
Description of class for man page.
|
||||
@discuss
|
||||
Detailed discussion of the class, if any.
|
||||
@end
|
||||
*/
|
||||
|
||||
#include "wap_classes.h"
|
||||
|
||||
// Forward reference to method arguments structure
|
||||
typedef struct _client_args_t client_args_t;
|
||||
|
||||
// This structure defines the context for a client connection
|
||||
typedef struct {
|
||||
// These properties must always be present in the client_t
|
||||
// and are set by the generated engine. The cmdpipe gets
|
||||
// messages sent to the actor; the msgpipe may be used for
|
||||
// faster asynchronous message flows.
|
||||
zsock_t *cmdpipe; // Command pipe to/from caller API
|
||||
zsock_t *msgpipe; // Message pipe to/from caller API
|
||||
zsock_t *dealer; // Socket to talk to server
|
||||
wap_proto_t *message; // Message to/from server
|
||||
client_args_t *args; // Arguments from methods
|
||||
|
||||
// Own properties
|
||||
int heartbeat_timer; // Timeout for heartbeats to server
|
||||
int retries; // How many heartbeats we've tried
|
||||
} client_t;
|
||||
|
||||
// Include the generated client engine
|
||||
#include "wap_client_engine.inc"
|
||||
|
||||
// Allocate properties and structures for a new client instance.
|
||||
// Return 0 if OK, -1 if failed
|
||||
|
||||
static int
|
||||
client_initialize (client_t *self)
|
||||
{
|
||||
// We'll ping the server once per second
|
||||
self->heartbeat_timer = 1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Free properties and structures for a client instance
|
||||
|
||||
static void
|
||||
client_terminate (client_t *self)
|
||||
{
|
||||
// Destroy properties here
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// connect_to_server_endpoint
|
||||
//
|
||||
|
||||
static void
|
||||
connect_to_server_endpoint (client_t *self)
|
||||
{
|
||||
if (zsock_connect (self->dealer, "%s", self->args->endpoint)) {
|
||||
engine_set_exception (self, bad_endpoint_event);
|
||||
zsys_warning ("could not connect to %s", self->args->endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// set_client_identity
|
||||
//
|
||||
|
||||
static void
|
||||
set_client_identity (client_t *self)
|
||||
{
|
||||
wap_proto_set_identity (self->message, self->args->identity);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// use_connect_timeout
|
||||
//
|
||||
|
||||
static void
|
||||
use_connect_timeout (client_t *self)
|
||||
{
|
||||
engine_set_timeout (self, self->args->timeout);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// client_is_connected
|
||||
//
|
||||
|
||||
static void
|
||||
client_is_connected (client_t *self)
|
||||
{
|
||||
self->retries = 0;
|
||||
engine_set_connected (self, true);
|
||||
engine_set_timeout (self, self->heartbeat_timer);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// check_if_connection_is_dead
|
||||
//
|
||||
|
||||
static void
|
||||
check_if_connection_is_dead (client_t *self)
|
||||
{
|
||||
// We send at most 3 heartbeats before expiring the server
|
||||
if (++self->retries >= 3) {
|
||||
engine_set_timeout (self, 0);
|
||||
engine_set_connected (self, false);
|
||||
engine_set_exception (self, exception_event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// prepare_blocks_command
|
||||
//
|
||||
|
||||
static void
|
||||
prepare_blocks_command (client_t *self)
|
||||
{
|
||||
wap_proto_set_block_ids (self->message, &self->args->block_ids);
|
||||
wap_proto_set_start_height (self->message, self->args->start_height);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_blocks_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_blocks_ok (client_t *self)
|
||||
{
|
||||
zmsg_t *msg = wap_proto_get_block_data (self->message);
|
||||
assert(msg != 0);
|
||||
printf("%p <--\n", (void*)msg);
|
||||
zsock_send (self->cmdpipe, "s888p", "BLOCKS OK", wap_proto_status(self->message),
|
||||
wap_proto_start_height (self->message),
|
||||
wap_proto_curr_height (self->message),
|
||||
msg);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// prepare_start_command
|
||||
//
|
||||
|
||||
static void
|
||||
prepare_start_command (client_t *self)
|
||||
{
|
||||
wap_proto_set_address (self->message, &self->args->address);
|
||||
wap_proto_set_thread_count (self->message, self->args->thread_count);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// prepare_put_command
|
||||
//
|
||||
|
||||
static void
|
||||
prepare_put_command (client_t *self)
|
||||
{
|
||||
wap_proto_set_tx_as_hex (self->message, &self->args->tx_as_hex);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_put_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_put_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8", "PUT OK",
|
||||
wap_proto_status (self->message));
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// prepare_get_command
|
||||
//
|
||||
|
||||
static void
|
||||
prepare_get_command (client_t *self)
|
||||
{
|
||||
wap_proto_set_tx_id (self->message, &self->args->tx_id);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_get_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_get_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "sip", "GET OK", 0,
|
||||
wap_proto_get_tx_data (self->message));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_get_height_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_get_height_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "si8", "GET HEIGHT OK", 0,
|
||||
wap_proto_height (self->message));
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_save_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_save_bc_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8", "SAVE BC OK", wap_proto_status(self->message));
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_start_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_start_ok (client_t *self)
|
||||
{
|
||||
zsock_send(self->cmdpipe, "s8", "START OK", wap_proto_status(self->message));
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_stop_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_stop_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "si", "STOP OK", 0);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_success
|
||||
//
|
||||
|
||||
static void
|
||||
signal_success (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "si", "SUCCESS", 0);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_bad_endpoint
|
||||
//
|
||||
|
||||
static void
|
||||
signal_bad_endpoint (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "sis", "FAILURE", -1, "Bad server endpoint");
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_failure
|
||||
//
|
||||
|
||||
static void
|
||||
signal_failure (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "sis", "FAILURE", -1, wap_proto_reason (self->message));
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// check_status_code
|
||||
//
|
||||
|
||||
static void
|
||||
check_status_code (client_t *self)
|
||||
{
|
||||
if (wap_proto_status (self->message) == WAP_PROTO_COMMAND_INVALID)
|
||||
engine_set_next_event (self, command_invalid_event);
|
||||
else
|
||||
engine_set_next_event (self, other_event);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_unhandled_error
|
||||
//
|
||||
|
||||
static void
|
||||
signal_unhandled_error (client_t *self)
|
||||
{
|
||||
zsys_error ("unhandled error code from server");
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_server_not_present
|
||||
//
|
||||
|
||||
static void
|
||||
signal_server_not_present (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "sis", "FAILURE", -1, "Server is not reachable");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// prepare_get_output_indexes_command
|
||||
//
|
||||
|
||||
static void
|
||||
prepare_get_output_indexes_command (client_t *self)
|
||||
{
|
||||
wap_proto_set_tx_id (self->message, &self->args->tx_id);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_output_indexes_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_output_indexes_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8p", "OUTPUT INDEXES OK",
|
||||
wap_proto_status (self->message),
|
||||
wap_proto_get_o_indexes (self->message));
|
||||
}
|
||||
|
||||
void wap_client_test(bool verbose) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// prepare_get_random_outs_command
|
||||
//
|
||||
|
||||
static void
|
||||
prepare_get_random_outs_command (client_t *self)
|
||||
{
|
||||
wap_proto_set_amounts (self->message, &self->args->amounts);
|
||||
wap_proto_set_outs_count (self->message, self->args->outs_count);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_random_outs_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_random_outs_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8p", "RANDOM OUTS OK",
|
||||
wap_proto_status (self->message),
|
||||
wap_proto_get_random_outputs (self->message));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_get_info_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_get_info_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s88888888888", "GET INFO OK",
|
||||
wap_proto_status (self->message),
|
||||
wap_proto_height (self->message),
|
||||
wap_proto_target_height (self->message),
|
||||
wap_proto_difficulty (self->message),
|
||||
wap_proto_tx_count (self->message),
|
||||
wap_proto_tx_pool_size (self->message),
|
||||
wap_proto_alt_blocks_count (self->message),
|
||||
wap_proto_outgoing_connections_count (self->message),
|
||||
wap_proto_incoming_connections_count (self->message),
|
||||
wap_proto_white_peerlist_size (self->message),
|
||||
wap_proto_grey_peerlist_size (self->message));
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_get_peer_list_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_get_peer_list_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8pp", "GET PEER LIST OK",
|
||||
wap_proto_status (self->message),
|
||||
wap_proto_get_white_list (self->message),
|
||||
wap_proto_get_gray_list (self->message));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_get_mining_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_get_mining_status_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8188p", "GET MINING STATUS OK",
|
||||
wap_proto_status (self->message),
|
||||
wap_proto_active (self->message),
|
||||
wap_proto_speed (self->message),
|
||||
wap_proto_thread_count (self->message),
|
||||
wap_proto_get_address (self->message));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// prepare_set_hash_log_rate_command
|
||||
//
|
||||
|
||||
static void
|
||||
prepare_set_log_hash_rate_command (client_t *self)
|
||||
{
|
||||
wap_proto_set_visible (self->message, self->args->visible);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_set_log_hash_rate_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_set_log_hash_rate_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8", "SET LOG HASH RATE OK",
|
||||
wap_proto_status (self->message));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// prepare_set_log_level_command
|
||||
//
|
||||
|
||||
static void
|
||||
prepare_set_log_level_command (client_t *self)
|
||||
{
|
||||
wap_proto_set_level (self->message, self->args->level);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_set_log_level_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_set_log_level_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8", "SET LOG LEVEL OK",
|
||||
wap_proto_status (self->message));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_start_save_graph_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_start_save_graph_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8", "START SAVE GRAPH OK",
|
||||
wap_proto_status (self->message));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_stop_save_graph_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_stop_save_graph_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8", "STOP SAVE GRAPH OK",
|
||||
wap_proto_status (self->message));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// prepare_get_block_hash_command
|
||||
//
|
||||
|
||||
static void
|
||||
prepare_get_block_hash_command (client_t *self)
|
||||
{
|
||||
wap_proto_set_height (self->message, self->args->height);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_get_block_hash_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_get_block_hash_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8p", "GET BLOCK HASH OK",
|
||||
wap_proto_status (self->message), wap_proto_get_hash (self->message));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// prepare_get_block_template_command
|
||||
//
|
||||
|
||||
static void
|
||||
prepare_get_block_template_command (client_t *self)
|
||||
{
|
||||
wap_proto_set_reserve_size (self->message, self->args->reserve_size);
|
||||
wap_proto_set_address (self->message, &self->args->address);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_have_get_block_template_ok
|
||||
//
|
||||
|
||||
static void
|
||||
signal_have_get_block_template_ok (client_t *self)
|
||||
{
|
||||
zsock_send (self->cmdpipe, "s8888pp", "GET BLOCK TEMPLATE OK",
|
||||
wap_proto_status (self->message),
|
||||
wap_proto_reserved_offset (self->message),
|
||||
wap_proto_height (self->message),
|
||||
wap_proto_difficulty (self->message),
|
||||
wap_proto_get_prev_hash (self->message),
|
||||
wap_proto_get_block_template_blob (self->message));
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,384 @@
|
|||
/* =========================================================================
|
||||
wap_server - wap_server
|
||||
|
||||
Copyright (c) the Contributors as noted in the AUTHORS file.
|
||||
|
||||
(insert license text here)
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@header
|
||||
Description of class for man page.
|
||||
@discuss
|
||||
Detailed discussion of the class, if any.
|
||||
@end
|
||||
*/
|
||||
|
||||
#include "wap_classes.h"
|
||||
// TODO: Change these to match your project's needs
|
||||
#include "../include/wap_proto.h"
|
||||
#include "../include/wap_server.h"
|
||||
#include "daemon_ipc_handlers.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Forward declarations for the two main classes we use here
|
||||
|
||||
typedef struct _server_t server_t;
|
||||
typedef struct _client_t client_t;
|
||||
|
||||
// This structure defines the context for each running server. Store
|
||||
// whatever properties and structures you need for the server.
|
||||
|
||||
struct _server_t {
|
||||
// These properties must always be present in the server_t
|
||||
// and are set by the generated engine; do not modify them!
|
||||
zsock_t *pipe; // Actor pipe back to caller
|
||||
zconfig_t *config; // Current loaded configuration
|
||||
|
||||
// TODO: Add any properties you need here
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// This structure defines the state for each client connection. It will
|
||||
// be passed to each action in the 'self' argument.
|
||||
|
||||
struct _client_t {
|
||||
// These properties must always be present in the client_t
|
||||
// and are set by the generated engine; do not modify them!
|
||||
server_t *server; // Reference to parent server
|
||||
wap_proto_t *message; // Message in and out
|
||||
|
||||
// TODO: Add specific properties for your application
|
||||
};
|
||||
|
||||
// Include the generated server engine
|
||||
#include "wap_server_engine.inc"
|
||||
|
||||
// Allocate properties and structures for a new server instance.
|
||||
// Return 0 if OK, or -1 if there was an error.
|
||||
|
||||
static int
|
||||
server_initialize (server_t *self)
|
||||
{
|
||||
// Construct properties here
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Free properties and structures for a server instance
|
||||
|
||||
static void
|
||||
server_terminate (server_t *self)
|
||||
{
|
||||
// Destroy properties here
|
||||
}
|
||||
|
||||
// Process server API method, return reply message if any
|
||||
|
||||
static zmsg_t *
|
||||
server_method (server_t *self, const char *method, zmsg_t *msg)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Allocate properties and structures for a new client connection and
|
||||
// optionally engine_set_next_event (). Return 0 if OK, or -1 on error.
|
||||
|
||||
static int
|
||||
client_initialize (client_t *self)
|
||||
{
|
||||
// Construct properties here
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Free properties and structures for a client connection
|
||||
|
||||
static void
|
||||
client_terminate (client_t *self)
|
||||
{
|
||||
// Destroy properties here
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Selftest
|
||||
|
||||
void
|
||||
wap_server_test (bool verbose)
|
||||
{
|
||||
printf (" * wap_server: ");
|
||||
if (verbose)
|
||||
printf ("\n");
|
||||
|
||||
// @selftest
|
||||
zactor_t *server = zactor_new (wap_server, "server");
|
||||
if (verbose)
|
||||
zstr_send (server, "VERBOSE");
|
||||
zstr_sendx (server, "BIND", "ipc://@/wap_server", NULL);
|
||||
|
||||
zsock_t *client = zsock_new (ZMQ_DEALER);
|
||||
assert (client);
|
||||
zsock_set_rcvtimeo (client, 2000);
|
||||
zsock_connect (client, "ipc://@/wap_server");
|
||||
|
||||
// TODO: fill this out
|
||||
wap_proto_t *request = wap_proto_new ();
|
||||
wap_proto_destroy (&request);
|
||||
|
||||
zsock_destroy (&client);
|
||||
zactor_destroy (&server);
|
||||
// @end
|
||||
printf ("OK\n");
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// register_wallet
|
||||
//
|
||||
|
||||
static void
|
||||
register_wallet (client_t *self)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// retrieve_blocks
|
||||
//
|
||||
|
||||
static void
|
||||
retrieve_blocks (client_t *self)
|
||||
{
|
||||
IPC::Daemon::retrieve_blocks(self->message);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// store_transaction
|
||||
//
|
||||
|
||||
static void
|
||||
store_transaction (client_t *self)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// retrieve_transaction
|
||||
//
|
||||
|
||||
static void
|
||||
retrieve_transaction (client_t *self)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// start_mining_process
|
||||
//
|
||||
|
||||
static void
|
||||
start_mining_process (client_t *self)
|
||||
{
|
||||
IPC::Daemon::start_mining(self->message);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// stop_mining_process
|
||||
//
|
||||
|
||||
static void
|
||||
stop_mining_process (client_t *self)
|
||||
{
|
||||
IPC::Daemon::stop_mining(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// output_indexes
|
||||
//
|
||||
|
||||
static void
|
||||
output_indexes (client_t *self)
|
||||
{
|
||||
IPC::Daemon::get_output_indexes(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// send_transaction
|
||||
//
|
||||
|
||||
static void
|
||||
send_transaction (client_t *self)
|
||||
{
|
||||
IPC::Daemon::send_raw_transaction(self->message);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// deregister_wallet
|
||||
//
|
||||
|
||||
static void
|
||||
deregister_wallet (client_t *self)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// allow_time_to_settle
|
||||
//
|
||||
|
||||
static void
|
||||
allow_time_to_settle (client_t *self)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// register_new_client
|
||||
//
|
||||
|
||||
static void
|
||||
register_new_client (client_t *self)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// signal_command_not_valid
|
||||
//
|
||||
|
||||
static void
|
||||
signal_command_not_valid (client_t *self)
|
||||
{
|
||||
wap_proto_set_status (self->message, WAP_PROTO_COMMAND_INVALID);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// random_outs
|
||||
//
|
||||
|
||||
static void
|
||||
random_outs (client_t *self)
|
||||
{
|
||||
IPC::Daemon::get_random_outs(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// height
|
||||
//
|
||||
|
||||
static void
|
||||
height (client_t *self)
|
||||
{
|
||||
IPC::Daemon::get_height(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// save_bc
|
||||
//
|
||||
|
||||
static void
|
||||
save_bc (client_t *self)
|
||||
{
|
||||
IPC::Daemon::save_bc(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// getinfo
|
||||
//
|
||||
|
||||
static void
|
||||
getinfo (client_t *self)
|
||||
{
|
||||
IPC::Daemon::get_info(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// get_peer_list
|
||||
//
|
||||
|
||||
static void
|
||||
get_peer_list (client_t *self)
|
||||
{
|
||||
IPC::Daemon::get_peer_list(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// get_mining_status
|
||||
//
|
||||
|
||||
static void
|
||||
get_mining_status (client_t *self)
|
||||
{
|
||||
IPC::Daemon::get_mining_status(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// set_log_hash_rate
|
||||
//
|
||||
|
||||
static void
|
||||
set_log_hash_rate (client_t *self)
|
||||
{
|
||||
IPC::Daemon::set_log_hash_rate(self->message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// set_log_level
|
||||
//
|
||||
|
||||
static void
|
||||
set_log_level (client_t *self)
|
||||
{
|
||||
IPC::Daemon::set_log_level(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// start_save_graph
|
||||
//
|
||||
|
||||
static void
|
||||
start_save_graph (client_t *self)
|
||||
{
|
||||
IPC::Daemon::start_save_graph(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// stop_save_graph
|
||||
//
|
||||
|
||||
static void
|
||||
stop_save_graph (client_t *self)
|
||||
{
|
||||
IPC::Daemon::stop_save_graph(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// get_block_hash
|
||||
//
|
||||
|
||||
static void
|
||||
get_block_hash (client_t *self)
|
||||
{
|
||||
IPC::Daemon::get_block_hash(self->message);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// get_block_template
|
||||
//
|
||||
|
||||
static void
|
||||
get_block_template (client_t *self)
|
||||
{
|
||||
IPC::Daemon::get_block_template(self->message);
|
||||
}
|
|
@ -27,7 +27,8 @@
|
|||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set(rpc_sources
|
||||
core_rpc_server.cpp)
|
||||
core_rpc_server.cpp
|
||||
json_rpc_http_server.cpp)
|
||||
|
||||
set(rpc_headers)
|
||||
|
||||
|
@ -53,3 +54,24 @@ target_link_libraries(rpc
|
|||
${EXTRA_LIBRARIES})
|
||||
add_dependencies(rpc
|
||||
version)
|
||||
|
||||
set(translator_rpc_sources
|
||||
daemon_deprecated_rpc.cpp
|
||||
json_rpc_http_server.cpp
|
||||
json_rpc.cpp)
|
||||
bitmonero_add_executable(rpc_translator
|
||||
${translator_rpc_sources}
|
||||
)
|
||||
target_link_libraries(rpc_translator
|
||||
LINK_PRIVATE
|
||||
client_ipc
|
||||
cryptonote_core
|
||||
cryptonote_protocol
|
||||
${EXTRA_LIBRARIES}
|
||||
${NET_SKELETON_LIBRARY}
|
||||
${ZMQ_LIB}
|
||||
${CZMQ_LIB}
|
||||
${CMAKE_THREAD_LIBS_INIT})
|
||||
set_property(TARGET rpc_translator
|
||||
PROPERTY
|
||||
OUTPUT_NAME "monero-rpc-deprecated")
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,86 @@
|
|||
// Copyright (c) 2014, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
/*!
|
||||
* \file daemon_json_rpc_handlers.h
|
||||
* \brief Header for JSON RPC handlers (Daemon)
|
||||
*/
|
||||
|
||||
#ifndef DAEMON_JSON_RPC_HANDLERS_H
|
||||
#define DAEMON_JSON_RPC_HANDLERS_H
|
||||
|
||||
#include "net_skeleton/net_skeleton.h"
|
||||
#include "json_rpc_http_server.h"
|
||||
#include "common/command_line.h"
|
||||
#include "net/http_server_impl_base.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "p2p/net_node.h"
|
||||
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
|
||||
#include <string>
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/writer.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
#include <cstring>
|
||||
#include "cryptonote_core/cryptonote_basic.h"
|
||||
#include "crypto/hash-ops.h"
|
||||
#include "ipc/include/wallet.h"
|
||||
#include "ipc/include/daemon_ipc_handlers.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/*!
|
||||
* \namespace RPC
|
||||
* \brief RPC related utilities
|
||||
*/
|
||||
namespace RPC
|
||||
{
|
||||
/*!
|
||||
* \namespace Daemon
|
||||
* \brief RPC relevant to daemon
|
||||
*/
|
||||
namespace DaemonDeprecated
|
||||
{
|
||||
const int SUCCESS = 0;
|
||||
const int FAILURE_DAEMON_NOT_RUNNING = 1;
|
||||
const int FAILURE_HTTP_SERVER = 2;
|
||||
/*!
|
||||
* \brief Starts an HTTP server that listens to old style JSON RPC requests
|
||||
* and creates an IPC client to be able to talk to the daemon
|
||||
* \return status code
|
||||
*/
|
||||
int start();
|
||||
/*!
|
||||
* \brief Stops the HTTP server and destroys the IPC client
|
||||
*/
|
||||
void stop();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) 2014, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
/*!
|
||||
* \file json_rpc.cpp
|
||||
* \brief Monero RPC deprecated
|
||||
*
|
||||
* Uses net_skeleton (fossa) as the HTTP server to translate JSON RPC requests
|
||||
* into 0MQ IPC requests, sends them to the daemon, translates back 0MQ IPC responses
|
||||
* into JSON RPC responses all as per the old monero JSON RPC API.
|
||||
*
|
||||
* Written for backwards compatiblity purposes.
|
||||
*/
|
||||
|
||||
#include "daemon_deprecated_rpc.h"
|
||||
#include <signal.h>
|
||||
#include <iostream>
|
||||
|
||||
static bool execute = true;
|
||||
void trap(int signal) {
|
||||
RPC::DaemonDeprecated::stop();
|
||||
execute = false;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int res = RPC::DaemonDeprecated::start();
|
||||
if (res == RPC::DaemonDeprecated::FAILURE_HTTP_SERVER) {
|
||||
std::cerr << "Couldn't start HTTP server\n";
|
||||
execute = false;
|
||||
} else if (res == RPC::DaemonDeprecated::FAILURE_DAEMON_NOT_RUNNING) {
|
||||
std::cerr << "Couldn't connect to daemon\n";
|
||||
execute = false;
|
||||
}
|
||||
signal(SIGINT, &trap);
|
||||
while (execute) {
|
||||
|
||||
}
|
||||
std::cout << "out!\n";
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*!
|
||||
* \file json_rpc_http_server.h
|
||||
* \brief Header for Json_rpc_http_server class
|
||||
*/
|
||||
|
||||
#include "json_rpc_http_server.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/*!
|
||||
* \namespace RPC
|
||||
* \brief RPC related utilities
|
||||
*/
|
||||
namespace RPC
|
||||
{
|
||||
|
||||
/**
|
||||
* \brief Constructor
|
||||
* \param ip IP address to bind
|
||||
* \param port Port number to bind
|
||||
* \param ev_handler Event handler function pointer
|
||||
*/
|
||||
Json_rpc_http_server::Json_rpc_http_server(const std::string &ip, const std::string &port,
|
||||
const std::string &path, void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data))
|
||||
{
|
||||
m_ip = ip;
|
||||
m_port = port;
|
||||
m_path = path;
|
||||
m_is_running = false;
|
||||
m_ev_handler = ev_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
Json_rpc_http_server::~Json_rpc_http_server()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Starts the server
|
||||
* \return True if start was successful
|
||||
*/
|
||||
bool Json_rpc_http_server::start()
|
||||
{
|
||||
if (m_is_running)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_is_running = true;
|
||||
ns_mgr_init(&mgr, NULL);
|
||||
nc = ns_bind(&mgr, (m_ip + ":" + m_port + "/" + m_path).c_str(), m_ev_handler);
|
||||
if (!nc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ns_set_protocol_http_websocket(nc);
|
||||
// Start a new thread so it doesn't block.
|
||||
server_thread = new boost::thread(&Json_rpc_http_server::poll, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Repeatedly loops processing requests if any.
|
||||
*/
|
||||
void Json_rpc_http_server::poll()
|
||||
{
|
||||
// Loop until the server is running and poll.
|
||||
while (m_is_running) {
|
||||
ns_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Stops the server
|
||||
*/
|
||||
void Json_rpc_http_server::stop()
|
||||
{
|
||||
m_is_running = false;
|
||||
server_thread->join();
|
||||
delete server_thread;
|
||||
ns_mgr_free(&mgr);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*!
|
||||
* \file json_rpc_http_server.h
|
||||
* \brief Header for Json_rpc_http_server class
|
||||
*/
|
||||
|
||||
#ifndef JSON_RPC_HTTP_SERVER_H
|
||||
#define JSON_RPC_HTTP_SERVER_H
|
||||
|
||||
#include "net_skeleton/net_skeleton.h"
|
||||
#include <boost/thread.hpp>
|
||||
#include <string>
|
||||
|
||||
/*!
|
||||
* \namespace RPC
|
||||
* \brief RPC related utilities
|
||||
*/
|
||||
namespace RPC
|
||||
{
|
||||
/*!
|
||||
* \class Json_rpc_http_server
|
||||
* \brief JSON HTTP RPC Server implemented with net_skeleton (aka fossa).
|
||||
*
|
||||
* Provides a higher level interface to C-like net_skeleton.
|
||||
*/
|
||||
class Json_rpc_http_server
|
||||
{
|
||||
struct ns_mgr mgr; /*!< Connection manager */
|
||||
struct ns_connection *nc; /*!< Connection pointer */
|
||||
boost::thread *server_thread; /*!< Server runs on this thread */
|
||||
/*!
|
||||
* \brief Repeatedly loops processing requests if any.
|
||||
*/
|
||||
void poll();
|
||||
std::string m_ip; /*!< IP address where its listening */
|
||||
std::string m_port; /*!< Port where its listening */
|
||||
std::string m_path; /*!< Path */
|
||||
bool m_is_running; /*!< Whether the server is currently running */
|
||||
void (*m_ev_handler)(struct ns_connection *nc, int ev, void *ev_data); /*!< Server event handler function pointer */
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Constructor
|
||||
* \param ip IP address to bind
|
||||
* \param port Port number to bind
|
||||
* \param ev_handler Event handler function pointer
|
||||
*/
|
||||
Json_rpc_http_server(const std::string &ip, const std::string &port, const std::string &path,
|
||||
void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data));
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
~Json_rpc_http_server();
|
||||
|
||||
/*!
|
||||
* \brief Starts the server
|
||||
* \return True if start was successful
|
||||
*/
|
||||
bool start();
|
||||
|
||||
/*!
|
||||
* \brief Stops the server
|
||||
*/
|
||||
void stop();
|
||||
|
||||
static int parse_error; /*!< JSON request passed couldn't be parsed */
|
||||
static int invalid_request; /*!< JSON request invalid */
|
||||
static int invalid_params; /*!< JSON request had faulty/missing params */
|
||||
static int internal_error; /*!< JSON request resulted in an internal error */
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -54,6 +54,7 @@ target_link_libraries(simplewallet
|
|||
${UNBOUND_LIBRARY}
|
||||
${UPNP_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${EXTRA_LIBRARIES})
|
||||
${EXTRA_LIBRARIES}
|
||||
${NET_SKELETON_LIBRARY})
|
||||
add_dependencies(simplewallet
|
||||
version)
|
||||
|
|
|
@ -53,6 +53,8 @@
|
|||
#include "storages/http_abstract_invoke.h"
|
||||
#include "rpc/core_rpc_server_commands_defs.h"
|
||||
#include "wallet/wallet_rpc_server.h"
|
||||
#include "wallet/wallet_json_rpc_handlers.h"
|
||||
#include "rpc/json_rpc_http_server.h"
|
||||
#include "version.h"
|
||||
#include "crypto/crypto.h" // for crypto::secret_key definition
|
||||
#include "mnemonics/electrum-words.h"
|
||||
|
@ -692,6 +694,7 @@ bool simple_wallet::deinit()
|
|||
if (!m_wallet.get())
|
||||
return true;
|
||||
|
||||
m_wallet->stop_ipc_client();
|
||||
return close_wallet();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
@ -986,21 +989,25 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
|
|||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
COMMAND_RPC_START_MINING::request req;
|
||||
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
|
||||
// COMMAND_RPC_START_MINING::request req;
|
||||
// req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
|
||||
std::string miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
|
||||
uint64_t threads_count;
|
||||
|
||||
bool ok = true;
|
||||
size_t max_mining_threads_count = (std::max)(std::thread::hardware_concurrency(), static_cast<unsigned>(2));
|
||||
if (0 == args.size())
|
||||
{
|
||||
req.threads_count = 1;
|
||||
// req.threads_count = 1;
|
||||
threads_count = 1;
|
||||
}
|
||||
else if (1 == args.size())
|
||||
{
|
||||
uint16_t num = 1;
|
||||
ok = string_tools::get_xtype_from_string(num, args[0]);
|
||||
ok = ok && (1 <= num && num <= max_mining_threads_count);
|
||||
req.threads_count = num;
|
||||
// req.threads_count = num;
|
||||
threads_count = num;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1014,13 +1021,17 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
|
|||
return true;
|
||||
}
|
||||
|
||||
COMMAND_RPC_START_MINING::response res;
|
||||
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/start_mining", req, res, m_http_client);
|
||||
std::string err = interpret_rpc_response(r, res.status);
|
||||
if (err.empty())
|
||||
success_msg_writer() << tr("Mining started in daemon");
|
||||
// COMMAND_RPC_START_MINING::response res;
|
||||
// bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/start_mining", req, res, m_http_client);
|
||||
// std::string err = interpret_rpc_response(r, res.status);
|
||||
|
||||
uint64_t status = m_wallet->start_mining(miner_address, threads_count);
|
||||
// res has to be true since we have checked before.
|
||||
if (status == IPC::STATUS_OK)
|
||||
success_msg_writer() << "Mining started in daemon";
|
||||
else
|
||||
fail_msg_writer() << tr("mining has NOT been started: ") << err;
|
||||
fail_msg_writer() << "mining has NOT been started: " << status;
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
@ -1029,14 +1040,16 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args)
|
|||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
COMMAND_RPC_STOP_MINING::request req;
|
||||
COMMAND_RPC_STOP_MINING::response res;
|
||||
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/stop_mining", req, res, m_http_client);
|
||||
std::string err = interpret_rpc_response(r, res.status);
|
||||
if (err.empty())
|
||||
success_msg_writer() << tr("Mining stopped in daemon");
|
||||
// COMMAND_RPC_STOP_MINING::request req;
|
||||
// COMMAND_RPC_STOP_MINING::response res;
|
||||
// bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/stop_mining", req, res, m_http_client);
|
||||
// std::string err = interpret_rpc_response(r, res.status);
|
||||
uint64_t status = m_wallet->stop_mining();
|
||||
if (status == IPC::STATUS_OK)
|
||||
success_msg_writer() << "Mining stopped in daemon";
|
||||
else
|
||||
fail_msg_writer() << tr("mining has NOT been stopped: ") << err;
|
||||
fail_msg_writer() << "mining has NOT been stopped: " << status;
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
@ -1045,14 +1058,16 @@ bool simple_wallet::save_bc(const std::vector<std::string>& args)
|
|||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
COMMAND_RPC_SAVE_BC::request req;
|
||||
COMMAND_RPC_SAVE_BC::response res;
|
||||
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/save_bc", req, res, m_http_client);
|
||||
std::string err = interpret_rpc_response(r, res.status);
|
||||
if (err.empty())
|
||||
success_msg_writer() << tr("Blockchain saved");
|
||||
// COMMAND_RPC_SAVE_BC::request req;
|
||||
// COMMAND_RPC_SAVE_BC::response res;
|
||||
// bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/save_bc", req, res, m_http_client);
|
||||
// std::string err = interpret_rpc_response(r, res.status);
|
||||
uint64_t status = m_wallet->save_bc();
|
||||
if (status == IPC::STATUS_OK)
|
||||
success_msg_writer() << "Blockchain saved";
|
||||
else
|
||||
fail_msg_writer() << tr("Blockchain can't be saved: ") << err;
|
||||
fail_msg_writer() << "Blockchain can't be saved: " << status;
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
@ -1279,11 +1294,18 @@ bool simple_wallet::show_payments(const std::vector<std::string> &args)
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err)
|
||||
{
|
||||
COMMAND_RPC_GET_HEIGHT::request req;
|
||||
COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized<COMMAND_RPC_GET_HEIGHT::response>();
|
||||
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client);
|
||||
err = interpret_rpc_response(r, res.status);
|
||||
return res.height;
|
||||
// COMMAND_RPC_GET_HEIGHT::request req;
|
||||
// COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized<COMMAND_RPC_GET_HEIGHT::response>();
|
||||
// bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client);
|
||||
// err = interpret_rpc_response(r, res.status);
|
||||
uint64_t height;
|
||||
uint64_t status = m_wallet->get_height(height);
|
||||
// res has to be true since we have checked before.
|
||||
if (status != IPC::STATUS_OK) {
|
||||
// TODO: map proper error messages to codes.
|
||||
err = "Couldn't get blockchain height.";
|
||||
}
|
||||
return height;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
|
||||
|
@ -1915,7 +1937,8 @@ int main(int argc, char* argv[])
|
|||
command_line::add_arg(desc_params, arg_electrum_seed );
|
||||
command_line::add_arg(desc_params, arg_testnet);
|
||||
command_line::add_arg(desc_params, arg_restricted);
|
||||
tools::wallet_rpc_server::init_options(desc_params);
|
||||
|
||||
RPC::Wallet::init_options(desc_params);
|
||||
|
||||
po::positional_options_description positional_options;
|
||||
positional_options.add(arg_command.name, -1);
|
||||
|
@ -1982,7 +2005,7 @@ int main(int argc, char* argv[])
|
|||
log_level % log_file_path.string();
|
||||
log_space::get_set_log_detalisation_level(true, log_level);
|
||||
|
||||
if(command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port))
|
||||
if (command_line::has_arg(vm, RPC::Wallet::arg_rpc_bind_port))
|
||||
{
|
||||
log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2);
|
||||
//runs wallet with rpc interface
|
||||
|
@ -2017,6 +2040,7 @@ int main(int argc, char* argv[])
|
|||
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
|
||||
|
||||
tools::wallet2 wal(testnet,restricted);
|
||||
// RPC::Wallet::init(&wal);
|
||||
try
|
||||
{
|
||||
LOG_PRINT_L0(sw::tr("Loading wallet..."));
|
||||
|
@ -2030,17 +2054,27 @@ int main(int argc, char* argv[])
|
|||
LOG_ERROR(sw::tr("Wallet initialization failed: ") << e.what());
|
||||
return 1;
|
||||
}
|
||||
// std::string ip_address, port;
|
||||
// RPC::Wallet::get_address_and_port(vm, ip_address, port);
|
||||
// RPC::Json_rpc_http_server rpc_server(ip_address, port, &RPC::Wallet::ev_handler);
|
||||
|
||||
tools::wallet_rpc_server wrpc(wal);
|
||||
bool r = wrpc.init(vm);
|
||||
CHECK_AND_ASSERT_MES(r, 1, sw::tr("Failed to initialize wallet rpc server"));
|
||||
|
||||
/*tools::signal_handler::install([&rpc_server, &wal] {
|
||||
rpc_server.stop();
|
||||
wal.store();
|
||||
});*/
|
||||
tools::signal_handler::install([&wrpc, &wal] {
|
||||
wrpc.send_stop_signal();
|
||||
wal.store();
|
||||
});
|
||||
LOG_PRINT_L0(sw::tr("Starting wallet rpc server"));
|
||||
wrpc.run();
|
||||
|
||||
LOG_PRINT_L0(sw::tr("Stopped wallet rpc server"));
|
||||
wal.stop_ipc_client();
|
||||
try
|
||||
{
|
||||
LOG_PRINT_L0(sw::tr("Storing wallet..."));
|
||||
|
@ -2052,7 +2086,8 @@ int main(int argc, char* argv[])
|
|||
LOG_ERROR(sw::tr("Failed to store wallet: ") << e.what());
|
||||
return 1;
|
||||
}
|
||||
}else
|
||||
}
|
||||
else
|
||||
{
|
||||
//runs wallet with console interface
|
||||
r = w.init(vm);
|
||||
|
|
|
@ -28,7 +28,8 @@
|
|||
|
||||
set(wallet_sources
|
||||
wallet2.cpp
|
||||
wallet_rpc_server.cpp)
|
||||
wallet_rpc_server.cpp
|
||||
wallet_json_rpc_handlers.cpp)
|
||||
|
||||
set(wallet_headers)
|
||||
|
||||
|
@ -47,10 +48,13 @@ bitmonero_add_library(wallet
|
|||
${wallet_private_headers})
|
||||
target_link_libraries(wallet
|
||||
LINK_PUBLIC
|
||||
client_ipc
|
||||
cryptonote_core
|
||||
mnemonics
|
||||
LINK_PRIVATE
|
||||
${Boost_SERIALIZATION_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY}
|
||||
${ZMQ_LIB}
|
||||
${CZMQ_LIB}
|
||||
${EXTRA_LIBRARIES})
|
||||
|
|
|
@ -92,7 +92,8 @@ namespace tools
|
|||
const size_t MAX_SPLIT_ATTEMPTS = 30;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit)
|
||||
void wallet2::init(const std::string& daemon_address,
|
||||
uint64_t upper_transaction_size_limit)
|
||||
{
|
||||
m_upper_transaction_size_limit = upper_transaction_size_limit;
|
||||
m_daemon_address = daemon_address;
|
||||
|
@ -180,18 +181,32 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
|
||||
if(!outs.empty() && tx_money_got_in_outs)
|
||||
{
|
||||
connect_to_daemon();
|
||||
THROW_WALLET_EXCEPTION_IF(!check_connection(), error::no_connection_to_daemon, "get_output_indexes");
|
||||
|
||||
//good news - got money! take care about it
|
||||
//usually we have only one transfer for user in transaction
|
||||
cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req);
|
||||
cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res);
|
||||
req.txid = get_transaction_hash(tx);
|
||||
bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_o_indexes.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_out_indices_error, res.status);
|
||||
THROW_WALLET_EXCEPTION_IF(res.o_indexes.size() != tx.vout.size(), error::wallet_internal_error,
|
||||
"transactions outputs size=" + std::to_string(tx.vout.size()) +
|
||||
" not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size()));
|
||||
crypto::hash tx_id = get_transaction_hash(tx);
|
||||
|
||||
zchunk_t *tx_id_chunk = zchunk_new(tx_id.data, crypto::HASH_SIZE);
|
||||
int rc = wap_client_output_indexes(ipc_client, &tx_id_chunk);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "get_output_indexes");
|
||||
uint64_t status = wap_client_status(ipc_client);
|
||||
THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "get_output_indexes");
|
||||
THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_INTERNAL_ERROR, error::daemon_internal_error, "get_output_indexes");
|
||||
THROW_WALLET_EXCEPTION_IF(status != IPC::STATUS_OK, error::get_out_indices_error, "get_output_indexes");
|
||||
|
||||
zframe_t *frame = wap_client_o_indexes(ipc_client);
|
||||
THROW_WALLET_EXCEPTION_IF(!frame, error::get_out_indices_error, "get_output_indexes");
|
||||
size_t size = zframe_size(frame) / sizeof(uint64_t);
|
||||
uint64_t *o_indexes_array = reinterpret_cast<uint64_t*>(zframe_data(frame));
|
||||
std::vector<uint64_t> o_indexes;
|
||||
for (uint64_t i = 0; i < size; i++) {
|
||||
o_indexes.push_back(o_indexes_array[i]);
|
||||
}
|
||||
|
||||
BOOST_FOREACH(size_t o, outs)
|
||||
{
|
||||
|
@ -202,7 +217,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
transfer_details& td = m_transfers.back();
|
||||
td.m_block_height = height;
|
||||
td.m_internal_output_index = o;
|
||||
td.m_global_output_index = res.o_indexes[o];
|
||||
td.m_global_output_index = o_indexes[o];
|
||||
td.m_tx = tx;
|
||||
td.m_spent = false;
|
||||
cryptonote::keypair in_ephemeral;
|
||||
|
@ -348,24 +363,63 @@ void wallet2::get_short_chain_history(std::list<crypto::hash>& ids) const
|
|||
if(!genesis_included)
|
||||
ids.push_back(m_blockchain[0]);
|
||||
}
|
||||
void wallet2::get_blocks_from_zmq_msg(zmsg_t *msg, std::list<cryptonote::block_complete_entry> &blocks) {
|
||||
zframe_t *frame = zmsg_first(msg);
|
||||
THROW_WALLET_EXCEPTION_IF(!frame, error::get_blocks_error, "getblocks");
|
||||
size_t size = zframe_size(frame);
|
||||
char *block_data = reinterpret_cast<char*>(zframe_data(frame));
|
||||
|
||||
rapidjson::Document json;
|
||||
THROW_WALLET_EXCEPTION_IF(json.Parse(block_data, size).HasParseError(), error::get_blocks_error, "getblocks");
|
||||
for (rapidjson::SizeType i = 0; i < json["blocks"].Size(); i++) {
|
||||
block_complete_entry block_entry;
|
||||
std::string block_string(json["blocks"][i]["block"].GetString(), json["blocks"][i]["block"].GetStringLength());
|
||||
block_entry.block = block_string;
|
||||
for (rapidjson::SizeType j = 0; j < json["blocks"][i]["txs"].Size(); j++) {
|
||||
block_entry.txs.push_back(std::string(json["blocks"][i]["txs"][j].GetString(), json["blocks"][i]["txs"][j].GetStringLength()));
|
||||
}
|
||||
blocks.push_back(block_entry);
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::pull_blocks(uint64_t start_height, size_t& blocks_added)
|
||||
{
|
||||
connect_to_daemon();
|
||||
THROW_WALLET_EXCEPTION_IF(!check_connection(), error::no_connection_to_daemon, "get_blocks");
|
||||
blocks_added = 0;
|
||||
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req);
|
||||
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res);
|
||||
get_short_chain_history(req.block_ids);
|
||||
req.start_height = start_height;
|
||||
bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getblocks.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status);
|
||||
std::list<crypto::hash> block_ids;
|
||||
get_short_chain_history(block_ids);
|
||||
std::list<char*> size_prepended_block_ids;
|
||||
zlist_t *list = zlist_new();
|
||||
for (std::list<crypto::hash>::iterator it = block_ids.begin(); it != block_ids.end(); it++) {
|
||||
char *block_id = new char[crypto::HASH_SIZE + 1];
|
||||
block_id[0] = crypto::HASH_SIZE;
|
||||
memcpy(block_id + 1, it->data, crypto::HASH_SIZE);
|
||||
size_prepended_block_ids.push_back(block_id);
|
||||
}
|
||||
for (std::list<char*>::iterator it = size_prepended_block_ids.begin(); it != size_prepended_block_ids.end(); it++) {
|
||||
zlist_append(list, *it);
|
||||
}
|
||||
int rc = wap_client_blocks(ipc_client, &list, start_height);
|
||||
for (std::list<char*>::iterator it = size_prepended_block_ids.begin(); it != size_prepended_block_ids.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
zlist_destroy(&list);
|
||||
THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "get_blocks");
|
||||
|
||||
size_t current_index = res.start_height;
|
||||
BOOST_FOREACH(auto& bl_entry, res.blocks)
|
||||
uint64_t status = wap_client_status(ipc_client);
|
||||
THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "get_blocks");
|
||||
THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_INTERNAL_ERROR, error::daemon_internal_error, "get_blocks");
|
||||
THROW_WALLET_EXCEPTION_IF(status != IPC::STATUS_OK, error::get_blocks_error, "get_blocks");
|
||||
std::list<block_complete_entry> blocks;
|
||||
zmsg_t *msg = wap_client_block_data(ipc_client);
|
||||
get_blocks_from_zmq_msg(msg, blocks);
|
||||
|
||||
uint64_t current_index = wap_client_start_height(ipc_client);
|
||||
BOOST_FOREACH(auto& bl_entry, blocks)
|
||||
{
|
||||
cryptonote::block bl;
|
||||
r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl);
|
||||
bool r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::block_parse_error, bl_entry.block);
|
||||
|
||||
crypto::hash bl_id = get_block_hash(bl);
|
||||
|
@ -377,9 +431,9 @@ void wallet2::pull_blocks(uint64_t start_height, size_t& blocks_added)
|
|||
else if(bl_id != m_blockchain[current_index])
|
||||
{
|
||||
//split detected here !!!
|
||||
THROW_WALLET_EXCEPTION_IF(current_index == res.start_height, error::wallet_internal_error,
|
||||
THROW_WALLET_EXCEPTION_IF(current_index == start_height, error::wallet_internal_error,
|
||||
"wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) +
|
||||
" (height " + std::to_string(res.start_height) + "), local block id at this height: " +
|
||||
" (height " + std::to_string(start_height) + "), local block id at this height: " +
|
||||
string_tools::pod_to_hex(m_blockchain[current_index]));
|
||||
|
||||
detach_blockchain(current_index);
|
||||
|
@ -492,6 +546,8 @@ void wallet2::detach_blockchain(uint64_t height)
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::deinit()
|
||||
{
|
||||
// Great, it all works. Now to shutdown, we use the destroy method,
|
||||
// which does a proper deconnect handshake internally:
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
@ -846,18 +902,7 @@ bool wallet2::prepare_file_names(const std::string& file_path)
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::check_connection()
|
||||
{
|
||||
if(m_http_client.is_connected())
|
||||
return true;
|
||||
|
||||
net_utils::http::url_content u;
|
||||
net_utils::parse_url(m_daemon_address, u);
|
||||
|
||||
if(!u.port)
|
||||
{
|
||||
u.port = m_testnet ? config::testnet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT;
|
||||
}
|
||||
|
||||
return m_http_client.connect(u.host, std::to_string(u.port), WALLET_RCP_CONNECTION_TIMEOUT);
|
||||
return ipc_client && wap_client_connected(ipc_client);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const
|
||||
|
@ -1317,15 +1362,17 @@ std::string wallet2::address_from_txt_record(const std::string& s)
|
|||
void wallet2::commit_tx(pending_tx& ptx)
|
||||
{
|
||||
using namespace cryptonote;
|
||||
crypto::hash txid;
|
||||
|
||||
COMMAND_RPC_SEND_RAW_TX::request req;
|
||||
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx));
|
||||
COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp;
|
||||
bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction");
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction");
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status);
|
||||
connect_to_daemon();
|
||||
THROW_WALLET_EXCEPTION_IF(!check_connection(), error::no_connection_to_daemon, "send_raw_transaction");
|
||||
std::string tx_as_hex_string = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx));
|
||||
zchunk_t *tx_as_hex = zchunk_new((void*)tx_as_hex_string.c_str(), tx_as_hex_string.length());
|
||||
int rc = wap_client_put(ipc_client, &tx_as_hex);
|
||||
uint64_t status = wap_client_status(ipc_client);
|
||||
THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "send_raw_transaction");
|
||||
THROW_WALLET_EXCEPTION_IF((status == IPC::STATUS_INVALID_TX) || (status == IPC::STATUS_TX_VERIFICATION_FAILED) ||
|
||||
(status == IPC::STATUS_TX_NOT_RELAYED), error::tx_rejected, ptx.tx, status);
|
||||
crypto::hash txid;
|
||||
|
||||
txid = get_transaction_hash(ptx.tx);
|
||||
add_unconfirmed_tx(ptx.tx, ptx.change_dts.amount);
|
||||
|
@ -1493,20 +1540,44 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
|||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
|
||||
if(fake_outputs_count)
|
||||
{
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req);
|
||||
req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key
|
||||
connect_to_daemon();
|
||||
THROW_WALLET_EXCEPTION_IF(!check_connection(), error::no_connection_to_daemon, "get_random_outs");
|
||||
uint64_t outs_count = fake_outputs_count + 1;
|
||||
std::vector<uint64_t> amounts;
|
||||
BOOST_FOREACH(transfer_container::iterator it, selected_transfers)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error,
|
||||
"m_internal_output_index = " + std::to_string(it->m_internal_output_index) +
|
||||
" is greater or equal to outputs count = " + std::to_string(it->m_tx.vout.size()));
|
||||
req.amounts.push_back(it->amount());
|
||||
amounts.push_back(it->amount());
|
||||
}
|
||||
zframe_t *amounts_frame = zframe_new(&amounts[0], amounts.size() * sizeof(uint64_t));
|
||||
int rc = wap_client_random_outs(ipc_client, outs_count, &amounts_frame);
|
||||
uint64_t status = wap_client_status(ipc_client);
|
||||
THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "getrandomouts");
|
||||
// TODO: Use a code to string mapping of errors
|
||||
THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_RANDOM_OUTS_FAILED, error::get_random_outs_error, "IPC::STATUS_RANDOM_OUTS_FAILED");
|
||||
THROW_WALLET_EXCEPTION_IF(status != IPC::STATUS_OK, error::get_random_outs_error, "!IPC:STATUS_OK");
|
||||
// Convert ZMQ response back into RPC response object.
|
||||
zframe_t *outputs_frame = wap_client_random_outputs(ipc_client);
|
||||
uint64_t frame_size = zframe_size(outputs_frame);
|
||||
char *frame_data = reinterpret_cast<char*>(zframe_data(outputs_frame));
|
||||
rapidjson::Document json;
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
|
||||
THROW_WALLET_EXCEPTION_IF(json.Parse(frame_data, frame_size).HasParseError(), error::get_random_outs_error, "Couldn't JSON parse random outputs.");
|
||||
for (rapidjson::SizeType i = 0; i < json["outputs"].Size(); i++) {
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount output;
|
||||
output.amount = json["outputs"][i]["amount"].GetInt64();
|
||||
for (rapidjson::SizeType j = 0; j < json["outputs"][i]["outs"].Size(); j++) {
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry entry;
|
||||
entry.global_amount_index = json["outputs"][i]["outs"][j]["global_amount_index"].GetInt64();
|
||||
std::string out_key(json["outputs"][i]["outs"][j]["out_key"].GetString(), json["outputs"][i]["outs"][j]["out_key"].GetStringLength());
|
||||
memcpy(entry.out_key.data, out_key.c_str(), 32);
|
||||
output.outs.push_back(entry);
|
||||
}
|
||||
daemon_resp.outs.push_back(output);
|
||||
}
|
||||
|
||||
bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error,
|
||||
"daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " +
|
||||
std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_transfers.size()));
|
||||
|
@ -2083,4 +2154,45 @@ void wallet2::generate_genesis(cryptonote::block& b) {
|
|||
cryptonote::generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE);
|
||||
}
|
||||
}
|
||||
|
||||
void wallet2::stop_ipc_client() {
|
||||
if (ipc_client) {
|
||||
wap_client_destroy(&ipc_client);
|
||||
}
|
||||
}
|
||||
|
||||
void wallet2::connect_to_daemon() {
|
||||
if (check_connection()) {
|
||||
return;
|
||||
}
|
||||
ipc_client = wap_client_new();
|
||||
wap_client_connect(ipc_client, "ipc://@/monero", 200, "wallet identity");
|
||||
}
|
||||
|
||||
uint64_t wallet2::start_mining(const std::string &address, uint64_t thread_count) {
|
||||
zchunk_t *address_chunk = zchunk_new((void*)address.c_str(), address.length());
|
||||
int rc = wap_client_start(ipc_client, &address_chunk, thread_count);
|
||||
zchunk_destroy(&address_chunk);
|
||||
THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "start_mining");
|
||||
return wap_client_status(ipc_client);
|
||||
}
|
||||
|
||||
uint64_t wallet2::stop_mining() {
|
||||
int rc = wap_client_stop(ipc_client);
|
||||
THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "stop_mining");
|
||||
return wap_client_status(ipc_client);
|
||||
}
|
||||
|
||||
uint64_t wallet2::get_height(uint64_t &height) {
|
||||
int rc = wap_client_get_height(ipc_client);
|
||||
THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "get_height");
|
||||
height = wap_client_height(ipc_client);
|
||||
return wap_client_status(ipc_client);
|
||||
}
|
||||
|
||||
uint64_t wallet2::save_bc() {
|
||||
int rc = wap_client_save_bc(ipc_client);
|
||||
THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "save_bc");
|
||||
return wap_client_status(ipc_client);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "crypto/hash.h"
|
||||
|
||||
#include "wallet_errors.h"
|
||||
#include "daemon_ipc_handlers.h"
|
||||
|
||||
#include <iostream>
|
||||
#define DEFAULT_TX_SPENDABLE_AGE 10
|
||||
|
@ -82,7 +83,18 @@ namespace tools
|
|||
{
|
||||
wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers (false), m_store_tx_keys(false) {};
|
||||
public:
|
||||
wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_restricted(restricted), is_old_file_format(false), m_store_tx_keys(false) {};
|
||||
wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_restricted(restricted), is_old_file_format(false), m_store_tx_keys(false) {
|
||||
ipc_client = NULL;
|
||||
connect_to_daemon();
|
||||
if (!ipc_client) {
|
||||
std::cout << "Couldn't connect to daemon\n\n";
|
||||
// Let ipc_client remain null. All request sending code will verify that
|
||||
// it's not null and otherwise throw.
|
||||
}
|
||||
};
|
||||
~wallet2() {
|
||||
stop_ipc_client();
|
||||
};
|
||||
struct transfer_details
|
||||
{
|
||||
uint64_t m_block_height;
|
||||
|
@ -188,7 +200,9 @@ namespace tools
|
|||
// free block size. TODO: fix this so that it actually takes
|
||||
// into account the current median block size rather than
|
||||
// the minimum block size.
|
||||
void init(const std::string& daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = ((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
|
||||
void init(const std::string& daemon_address = "http://localhost:8080",
|
||||
uint64_t upper_transaction_size_limit = ((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 125) / 100) -
|
||||
CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
|
||||
bool deinit();
|
||||
|
||||
void stop() { m_run.store(false, std::memory_order_relaxed); }
|
||||
|
@ -268,6 +282,7 @@ namespace tools
|
|||
a & m_tx_keys;
|
||||
}
|
||||
|
||||
void stop_ipc_client();
|
||||
/*!
|
||||
* \brief Check if wallet keys and bin files exist
|
||||
* \param file_path Wallet file path
|
||||
|
@ -290,6 +305,11 @@ namespace tools
|
|||
|
||||
static std::string address_from_txt_record(const std::string& s);
|
||||
|
||||
uint64_t start_mining(const std::string &address, uint64_t thread_count);
|
||||
uint64_t stop_mining();
|
||||
uint64_t get_height(uint64_t &height);
|
||||
uint64_t save_bc();
|
||||
|
||||
bool always_confirm_transfers() const { return m_always_confirm_transfers; }
|
||||
void always_confirm_transfers(bool always) { m_always_confirm_transfers = always; }
|
||||
bool store_tx_keys() const { return m_store_tx_keys; }
|
||||
|
@ -319,12 +339,14 @@ namespace tools
|
|||
bool is_tx_spendtime_unlocked(uint64_t unlock_time) const;
|
||||
bool is_transfer_unlocked(const transfer_details& td) const;
|
||||
bool clear();
|
||||
void get_blocks_from_zmq_msg(zmsg_t *msg, std::list<cryptonote::block_complete_entry> &blocks);
|
||||
void pull_blocks(uint64_t start_height, size_t& blocks_added);
|
||||
uint64_t select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list<transfer_container::iterator>& selected_transfers);
|
||||
bool prepare_file_names(const std::string& file_path);
|
||||
void process_unconfirmed(const cryptonote::transaction& tx);
|
||||
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount);
|
||||
void generate_genesis(cryptonote::block& b);
|
||||
void connect_to_daemon();
|
||||
void check_genesis(const crypto::hash& genesis_hash) const; //throws
|
||||
bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const;
|
||||
|
||||
|
@ -351,6 +373,7 @@ namespace tools
|
|||
bool m_restricted;
|
||||
std::string seed_language; /*!< Language of the mnemonics (seed). */
|
||||
bool is_old_file_format; /*!< Whether the wallet file is of an old file format */
|
||||
wap_client_t *ipc_client;
|
||||
bool m_watch_only; /*!< no spend key */
|
||||
bool m_always_confirm_transfers;
|
||||
bool m_store_tx_keys; /*!< request txkey to be returned in RPC, and store in the wallet cache file */
|
||||
|
@ -498,23 +521,45 @@ namespace tools
|
|||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
|
||||
if(fake_outputs_count)
|
||||
{
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req);
|
||||
req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key
|
||||
connect_to_daemon();
|
||||
THROW_WALLET_EXCEPTION_IF(!check_connection(), error::no_connection_to_daemon, "get_random_outs");
|
||||
uint64_t outs_count = fake_outputs_count + 1;
|
||||
std::vector<uint64_t> amounts;
|
||||
BOOST_FOREACH(transfer_container::iterator it, selected_transfers)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error,
|
||||
"m_internal_output_index = " + std::to_string(it->m_internal_output_index) +
|
||||
" is greater or equal to outputs count = " + std::to_string(it->m_tx.vout.size()));
|
||||
req.amounts.push_back(it->amount());
|
||||
amounts.push_back(it->amount());
|
||||
}
|
||||
|
||||
bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error,
|
||||
"daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " +
|
||||
std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_transfers.size()));
|
||||
zframe_t *amounts_frame = zframe_new(&amounts[0], amounts.size() * sizeof(uint64_t));
|
||||
int rc = wap_client_random_outs(ipc_client, outs_count, &amounts_frame);
|
||||
|
||||
uint64_t status = wap_client_status(ipc_client);
|
||||
THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "getrandomouts");
|
||||
// TODO: Use a code to string mapping of errors
|
||||
THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_RANDOM_OUTS_FAILED, error::get_random_outs_error, "IPC::STATUS_RANDOM_OUTS_FAILED");
|
||||
THROW_WALLET_EXCEPTION_IF(status != IPC::STATUS_OK, error::get_random_outs_error, "!IPC:STATUS_OK");
|
||||
|
||||
// Convert ZMQ response back into RPC response object.
|
||||
zframe_t *outputs_frame = wap_client_random_outputs(ipc_client);
|
||||
uint64_t frame_size = zframe_size(outputs_frame);
|
||||
char *frame_data = reinterpret_cast<char*>(zframe_data(outputs_frame));
|
||||
rapidjson::Document json;
|
||||
THROW_WALLET_EXCEPTION_IF(json.Parse(frame_data, frame_size).HasParseError(), error::get_random_outs_error, "Couldn't JSON parse random outputs.");
|
||||
for (rapidjson::SizeType i = 0; i < json["outputs"].Size(); i++) {
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount output;
|
||||
output.amount = json["outputs"][i]["amount"].GetInt64();
|
||||
for (rapidjson::SizeType j = 0; j < json["outputs"][i]["outs"].Size(); j++) {
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry entry;
|
||||
entry.global_amount_index = json["outputs"][i]["outs"][j]["global_amount_index"].GetInt64();
|
||||
std::string out_key(json["outputs"][i]["outs"][j]["out_key"].GetString(), json["outputs"][i]["outs"][j]["out_key"].GetStringLength());
|
||||
memcpy(entry.out_key.data, out_key.c_str(), 32);
|
||||
output.outs.push_back(entry);
|
||||
}
|
||||
daemon_resp.outs.push_back(output);
|
||||
}
|
||||
|
||||
std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount> scanty_outs;
|
||||
BOOST_FOREACH(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs, daemon_resp.outs)
|
||||
|
|
|
@ -440,7 +440,7 @@ namespace tools
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
struct tx_rejected : public transfer_error
|
||||
{
|
||||
explicit tx_rejected(std::string&& loc, const cryptonote::transaction& tx, const std::string& status)
|
||||
explicit tx_rejected(std::string&& loc, const cryptonote::transaction& tx, uint64_t status)
|
||||
: transfer_error(std::move(loc), "transaction was rejected by daemon")
|
||||
, m_tx(tx)
|
||||
, m_status(status)
|
||||
|
@ -448,7 +448,7 @@ namespace tools
|
|||
}
|
||||
|
||||
const cryptonote::transaction& tx() const { return m_tx; }
|
||||
const std::string& status() const { return m_status; }
|
||||
uint64_t status() const { return m_status; }
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
|
@ -461,7 +461,7 @@ namespace tools
|
|||
|
||||
private:
|
||||
cryptonote::transaction m_tx;
|
||||
std::string m_status;
|
||||
uint64_t m_status;
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct tx_sum_overflow : public transfer_error
|
||||
|
@ -566,6 +566,13 @@ namespace tools
|
|||
{
|
||||
}
|
||||
};
|
||||
struct daemon_internal_error : public wallet_rpc_error
|
||||
{
|
||||
explicit daemon_internal_error(std::string&& loc, const std::string& request)
|
||||
: wallet_rpc_error(std::move(loc), "daemon had an internal error processing the request", request)
|
||||
{
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct no_connection_to_daemon : public wallet_rpc_error
|
||||
{
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
/*!
|
||||
* \file wallet_json_rpc_handlers.cpp
|
||||
* \brief Implementations of JSON RPC handlers (Wallet)
|
||||
*/
|
||||
|
||||
// NOTE:
|
||||
// While this uses net_skeleton (aka fossa) for JSON RPC handling, JSON parsing
|
||||
// and string conversion are done with rapidjson because it is way easier and better
|
||||
// suited.
|
||||
// To add a new method, add the name and function pointer to `method_names` and `handlers`.
|
||||
// The handler function should have the same signature as the rest of them here.
|
||||
// It should use rapidjson to parse the request string and the internal objects kept in the
|
||||
// anonymous namespace to generate the response. The response must eventually get
|
||||
// stringified using rapidjson.
|
||||
// Trivial and error responses may be returned with ns_create_rpc_reply and ns_create_rpc_error
|
||||
// respectively.
|
||||
|
||||
#include "wallet_json_rpc_handlers.h"
|
||||
|
||||
#define MAX_RESPONSE_SIZE 2000
|
||||
|
||||
/*!
|
||||
* \namespace
|
||||
* \brief Anonymous namespace to keep things in the scope of this file.
|
||||
*/
|
||||
namespace
|
||||
{
|
||||
tools::wallet2 *wallet;
|
||||
|
||||
/*!
|
||||
* \brief Constructs a response string given a result JSON object.
|
||||
*
|
||||
* It also adds boilerplate properties like id, method.
|
||||
* \param req net_skeleton request object
|
||||
* \param result_json rapidjson result object
|
||||
* \param response_json Root rapidjson document that will eventually have the whole response
|
||||
* \param response Response as a string gets written here.
|
||||
*/
|
||||
/*
|
||||
void construct_response_string(struct ns_rpc_request *req, rapidjson::Value &result_json,
|
||||
rapidjson::Document &response_json, std::string &response)
|
||||
{
|
||||
//TODO: uncomment and use this function
|
||||
response_json.SetObject();
|
||||
rapidjson::Value string_value(rapidjson::kStringType);
|
||||
// If ID was present in request use it else use "null".
|
||||
if (req->id != NULL)
|
||||
{
|
||||
string_value.SetString(req->id[0].ptr, req->id[0].len);
|
||||
}
|
||||
else
|
||||
{
|
||||
string_value.SetString("null", 4);
|
||||
}
|
||||
response_json.AddMember("id", string_value, response_json.GetAllocator());
|
||||
string_value.SetString(req->method[0].ptr, req->method[0].len);
|
||||
response_json.AddMember("method", string_value, response_json.GetAllocator());
|
||||
response_json.AddMember("result", result_json, response_json.GetAllocator());
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
response_json.Accept(writer);
|
||||
// Write string to `response`.
|
||||
response = buffer.GetString();
|
||||
}
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Implementation of 'getbalance' method.
|
||||
* \param buf Buffer to fill in response.
|
||||
* \param len Max length of response.
|
||||
* \param req net_skeleton RPC request
|
||||
* \return Actual response length.
|
||||
*/
|
||||
int getbalance(char *buf, int len, struct ns_rpc_request *req)
|
||||
{
|
||||
//TODO: uncomment and use this function
|
||||
return 0;
|
||||
/*uint64_t balance, unlocked_balance;
|
||||
try
|
||||
{
|
||||
balance = wallet->balance();
|
||||
unlocked_balance = wallet->unlocked_balance();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error,
|
||||
"Internal error", "{}");
|
||||
}
|
||||
rapidjson::Document response_json;
|
||||
rapidjson::Value result_json;
|
||||
result_json.SetObject();
|
||||
result_json.AddMember("balance", balance, response_json.GetAllocator());
|
||||
result_json.AddMember("unlocked_balance", unlocked_balance, response_json.GetAllocator());
|
||||
result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator());
|
||||
std::string response;
|
||||
construct_response_string(req, result_json, response_json, response);
|
||||
size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len;
|
||||
strncpy(buf, response.c_str(), copy_length);
|
||||
return response.length();*/
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Implementation of 'getaddress' method.
|
||||
* \param buf Buffer to fill in response.
|
||||
* \param len Max length of response.
|
||||
* \param req net_skeleton RPC request
|
||||
* \return Actual response length.
|
||||
*/
|
||||
int getaddress(char *buf, int len, struct ns_rpc_request *req)
|
||||
{
|
||||
//TODO: uncomment and use this function
|
||||
return 0;
|
||||
/*std::string address;
|
||||
try
|
||||
{
|
||||
address = wallet->get_account().get_public_address_str(wallet->testnet());
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error,
|
||||
"Internal error", "{}");
|
||||
}
|
||||
rapidjson::Document response_json;
|
||||
rapidjson::Value result_json;
|
||||
result_json.SetObject();
|
||||
rapidjson::Value string_value(rapidjson::kStringType);
|
||||
string_value.SetString(address.c_str(), address.length());
|
||||
result_json.AddMember("address", string_value, response_json.GetAllocator());
|
||||
result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator());
|
||||
std::string response;
|
||||
construct_response_string(req, result_json, response_json, response);
|
||||
size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len;
|
||||
strncpy(buf, response.c_str(), copy_length);
|
||||
return response.length();*/
|
||||
}
|
||||
|
||||
// Contains a list of method names.
|
||||
const char *method_names[] = {
|
||||
"getbalance",
|
||||
"getaddress",
|
||||
NULL
|
||||
};
|
||||
|
||||
// Contains a list of function pointers. These must map 1-1 by index with `method_names`.
|
||||
ns_rpc_handler_t handlers[] = {
|
||||
getbalance,
|
||||
getaddress,
|
||||
NULL
|
||||
};
|
||||
}
|
||||
|
||||
/*!
|
||||
* \namespace RPC
|
||||
* \brief RPC related utilities
|
||||
*/
|
||||
namespace RPC
|
||||
{
|
||||
/*!
|
||||
* \namespace Wallet
|
||||
* \brief RPC relevant to wallet
|
||||
*/
|
||||
namespace Wallet
|
||||
{
|
||||
/*!
|
||||
* \brief initializes module (must call this before handling requests)
|
||||
* \param p_wallet Pointer to wallet2 object
|
||||
*/
|
||||
void init(tools::wallet2 *p_wallet)
|
||||
{
|
||||
wallet = p_wallet;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \Inits certain options used in Wallet CLI.
|
||||
* \param desc Instance of options description object
|
||||
*/
|
||||
void init_options(boost::program_options::options_description& desc)
|
||||
{
|
||||
command_line::add_arg(desc, arg_rpc_bind_ip);
|
||||
command_line::add_arg(desc, arg_rpc_bind_port);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Gets IP address and port number from variable map
|
||||
* \param vm Variable map
|
||||
* \param ip_address IP address
|
||||
* \param port Port number
|
||||
*/
|
||||
void get_address_and_port(const boost::program_options::variables_map& vm,
|
||||
std::string &ip_address, std::string &port)
|
||||
{
|
||||
ip_address = command_line::get_arg(vm, arg_rpc_bind_ip);
|
||||
port = command_line::get_arg(vm, arg_rpc_bind_port);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Event handler that is invoked upon net_skeleton network events.
|
||||
*
|
||||
* Any change in behavior of RPC should happen from this point.
|
||||
* \param nc net_skeleton connection
|
||||
* \param ev Type of event
|
||||
* \param ev_data Event data
|
||||
*/
|
||||
void ev_handler(struct ns_connection *nc, int ev, void *ev_data)
|
||||
{
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
char buf[MAX_RESPONSE_SIZE];
|
||||
switch (ev) {
|
||||
case NS_HTTP_REQUEST:
|
||||
ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf),
|
||||
method_names, handlers);
|
||||
ns_printf(nc, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
|
||||
"Content-Type: application/json\r\n\r\n%s",
|
||||
(int) strlen(buf), buf);
|
||||
nc->flags |= NSF_FINISHED_SENDING_DATA;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*!
|
||||
* \file wallet_json_rpc_handlers.h
|
||||
* \brief Header for JSON RPC handlers (Wallet)
|
||||
*/
|
||||
|
||||
#ifndef WALLET_JSON_RPC_HANDLERS_H
|
||||
#define WALLET_JSON_RPC_HANDLERS_H
|
||||
|
||||
#include "net_skeleton/net_skeleton.h"
|
||||
#include "rpc/json_rpc_http_server.h"
|
||||
#include "common/command_line.h"
|
||||
#include "net/http_server_impl_base.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "p2p/net_node.h"
|
||||
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
|
||||
#include <string>
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/writer.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
#include <cstring>
|
||||
#include "cryptonote_core/cryptonote_basic.h"
|
||||
#include "crypto/hash-ops.h"
|
||||
#include "wallet2.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/*!
|
||||
* \namespace RPC
|
||||
* \brief RPC related utilities
|
||||
*/
|
||||
namespace RPC
|
||||
{
|
||||
/*!
|
||||
* \namespace Wallet
|
||||
* \brief RPC relevant to wallet
|
||||
*/
|
||||
namespace Wallet
|
||||
{
|
||||
const command_line::arg_descriptor<std::string> arg_rpc_bind_ip = {
|
||||
"rpc-bind-ip",
|
||||
"Specify ip to bind rpc server",
|
||||
"127.0.0.1"
|
||||
};
|
||||
|
||||
const command_line::arg_descriptor<std::string> arg_rpc_bind_port = {
|
||||
"rpc-bind-port",
|
||||
"Starts wallet as rpc server for wallet operations, sets bind port for server",
|
||||
"",
|
||||
true
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief initializes module (must call this before handling requests)
|
||||
* \param p_wallet Pointer to wallet2 object
|
||||
*/
|
||||
void init(tools::wallet2 *p_wallet);
|
||||
|
||||
/*!
|
||||
* \Inits certain options used in Daemon CLI.
|
||||
* \param desc Instance of options description object
|
||||
*/
|
||||
void init_options(boost::program_options::options_description& desc);
|
||||
|
||||
/*!
|
||||
* \brief Gets IP address and port number from variable map
|
||||
* \param vm Variable map
|
||||
* \param ip_address IP address
|
||||
* \param port Port number
|
||||
*/
|
||||
void get_address_and_port(const boost::program_options::variables_map& vm,
|
||||
std::string &ip_address, std::string &port);
|
||||
|
||||
/*!
|
||||
* \brief Event handler that is invoked upon net_skeleton network events.
|
||||
*
|
||||
* Any change in behavior of RPC should happen from this point.
|
||||
* \param nc net_skeleton connection
|
||||
* \param ev Type of event
|
||||
* \param ev_data Event data
|
||||
*/
|
||||
void ev_handler(struct ns_connection *nc, int ev, void *ev_data);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -137,8 +137,6 @@ bool transactions_flow_test(std::string& working_folder,
|
|||
return false;
|
||||
}
|
||||
|
||||
w1.init(daemon_addr_a);
|
||||
|
||||
size_t blocks_fetched = 0;
|
||||
bool received_money;
|
||||
bool ok;
|
||||
|
|
Loading…
Reference in New Issue