Merge branch 'oranjuice-zmq' into development

This commit is contained in:
Riccardo Spagni 2015-08-29 23:46:33 +02:00
commit 3daece9439
86 changed files with 20902 additions and 119 deletions

View File

@ -146,6 +146,8 @@ endif()
# set(BSDI TRUE) # set(BSDI TRUE)
include_directories(src contrib/epee/include external "${CMAKE_BINARY_DIR}/version") include_directories(src contrib/epee/include external "${CMAKE_BINARY_DIR}/version")
include_directories(src/common/rapidjson/include/)
include_directories(src/ipc/include)
if(APPLE) if(APPLE)
include_directories(SYSTEM /usr/include/malloc) include_directories(SYSTEM /usr/include/malloc)

4
external/CMakeLists.txt vendored Normal file → Executable file
View File

@ -98,4 +98,8 @@ else()
endif() endif()
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) add_subdirectory(db_drivers)

2
external/net_skeleton/.jigplugins.txt vendored Normal file
View File

@ -0,0 +1,2 @@
http://github.com/robmadole/jig-plugins@whitespace
http://github.com/robmadole/jig-plugins@woops

15
external/net_skeleton/LICENSE vendored Normal file
View File

@ -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/>.

458
external/net_skeleton/README.adoc vendored Normal file
View File

@ -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].

15
external/net_skeleton/examples/Makefile vendored Normal file
View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

@ -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)

BIN
external/net_skeleton/examples/netcat/nc vendored Normal file

Binary file not shown.

View File

@ -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;
}

View File

@ -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.

View File

@ -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;
}

View File

@ -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.

View File

@ -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;
}

View File

@ -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)

View File

@ -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">&nbsp;</span>
</div><div>
</div>
</body>
</html>

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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;
}

View File

@ -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.

View File

@ -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;
}

View File

@ -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

View File

@ -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">
publishsubscribe 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>

View File

@ -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;
}

17
external/net_skeleton/modules/Makefile vendored Normal file
View File

@ -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'))

485
external/net_skeleton/modules/http.c vendored Normal file
View File

@ -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 */

78
external/net_skeleton/modules/http.h vendored Normal file
View File

@ -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 */

141
external/net_skeleton/modules/json-rpc.c vendored Normal file
View File

@ -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 */

View File

@ -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 */

141
external/net_skeleton/modules/sha1.c vendored Normal file
View File

@ -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 */

26
external/net_skeleton/modules/sha1.h vendored Normal file
View File

@ -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 */

957
external/net_skeleton/modules/skeleton.c vendored Normal file
View File

@ -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, &current_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);
}
}

263
external/net_skeleton/modules/skeleton.h vendored Normal file
View File

@ -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 */

166
external/net_skeleton/modules/util.c vendored Normal file
View File

@ -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;
}

30
external/net_skeleton/modules/util.h vendored Normal file
View File

@ -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 */

2376
external/net_skeleton/net_skeleton.c vendored Normal file

File diff suppressed because it is too large Load Diff

537
external/net_skeleton/net_skeleton.h vendored Normal file
View File

@ -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 */

View File

@ -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"

View File

@ -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

10
external/net_skeleton/test/.gitignore vendored Normal file
View File

@ -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

46
external/net_skeleton/test/Makefile vendored Normal file
View File

@ -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

49
external/net_skeleton/test/ca.pem vendored Normal file
View File

@ -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-----

45
external/net_skeleton/test/client.pem vendored Normal file
View File

@ -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-----

45
external/net_skeleton/test/server.pem vendored Normal file
View File

@ -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-----

558
external/net_skeleton/test/unit_test.c vendored Normal file
View File

@ -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;
}

595
external/zmq.hpp vendored Normal file
View File

@ -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

View File

@ -51,7 +51,6 @@ function (bitmonero_add_executable name)
source_group("${name}" source_group("${name}"
FILES FILES
${ARGN}) ${ARGN})
add_executable("${name}" add_executable("${name}"
${ARGN}) ${ARGN})
target_link_libraries("${name}" target_link_libraries("${name}"
@ -86,11 +85,15 @@ function (bitmonero_add_library name)
FOLDER "libs") FOLDER "libs")
endfunction () endfunction ()
find_library(ZMQ_LIB zmq)
find_library(CZMQ_LIB czmq)
add_subdirectory(common) add_subdirectory(common)
add_subdirectory(crypto) add_subdirectory(crypto)
add_subdirectory(cryptonote_core) add_subdirectory(cryptonote_core)
add_subdirectory(blockchain_db) add_subdirectory(blockchain_db)
add_subdirectory(mnemonics) add_subdirectory(mnemonics)
add_subdirectory(ipc)
add_subdirectory(rpc) add_subdirectory(rpc)
add_subdirectory(wallet) add_subdirectory(wallet)
add_subdirectory(p2p) add_subdirectory(p2p)

View File

@ -26,6 +26,8 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // 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. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <vector> #include <vector>
#include <string> #include <string>

1
src/common/rapidjson Submodule

@ -0,0 +1 @@
Subproject commit 8307f0f4c9035bd63853bdf9e1b951e8c0e37b62

View File

@ -85,6 +85,7 @@ bitmonero_add_executable(daemon
target_link_libraries(daemon target_link_libraries(daemon
LINK_PRIVATE LINK_PRIVATE
rpc rpc
server_ipc
blockchain_db blockchain_db
cryptonote_core cryptonote_core
crypto crypto
@ -101,8 +102,12 @@ target_link_libraries(daemon
${Boost_THREAD_LIBRARY} ${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_THREAD_LIBS_INIT}
${UPNP_LIBRARIES} ${UPNP_LIBRARIES}
${EXTRA_LIBRARIES}) ${EXTRA_LIBRARIES}
add_dependencies(daemon version) ${NET_SKELETON_LIBRARY}
${ZMQ_LIB}
${CZMQ_LIB})
add_dependencies(daemon
version)
set_property(TARGET daemon set_property(TARGET daemon
PROPERTY PROPERTY
OUTPUT_NAME "bitmonerod") OUTPUT_NAME "bitmonerod")

View File

@ -34,11 +34,11 @@
#include "daemon/core.h" #include "daemon/core.h"
#include "daemon/p2p.h" #include "daemon/p2p.h"
#include "daemon/protocol.h" #include "daemon/protocol.h"
#include "daemon/rpc.h"
#include "daemon/command_server.h" #include "daemon/command_server.h"
#include "misc_log_ex.h" #include "misc_log_ex.h"
#include "version.h" #include "version.h"
#include "../../contrib/epee/include/syncobj.h" #include "../../contrib/epee/include/syncobj.h"
#include "daemon_ipc_handlers.h"
using namespace epee; using namespace epee;
@ -56,7 +56,7 @@ private:
public: public:
t_core core; t_core core;
t_p2p p2p; t_p2p p2p;
t_rpc rpc; bool testnet_mode;
t_internals( t_internals(
boost::program_options::variables_map const & vm boost::program_options::variables_map const & vm
@ -64,11 +64,11 @@ public:
: core{vm} : core{vm}
, protocol{vm, core} , protocol{vm, core}
, p2p{vm, protocol} , p2p{vm, protocol}
, rpc{vm, core, p2p}
{ {
// Handle circular dependencies // Handle circular dependencies
protocol.set_p2p_endpoint(p2p.get()); protocol.set_p2p_endpoint(p2p.get());
core.set_protocol(protocol.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_core::init_options(option_spec);
t_p2p::init_options(option_spec); t_p2p::init_options(option_spec);
t_rpc::init_options(option_spec);
} }
t_daemon::t_daemon( t_daemon::t_daemon(
@ -119,24 +118,19 @@ bool t_daemon::run(bool interactive)
try try
{ {
mp_internals->core.run(); mp_internals->core.run();
mp_internals->rpc.run();
daemonize::t_command_server* rpc_commands;
if (interactive) if (interactive)
{ {
rpc_commands = new daemonize::t_command_server(0, 0, false, mp_internals->rpc.get_server()); IPC::Daemon::init(mp_internals->core.get(), mp_internals->p2p.get(), mp_internals->testnet_mode);
rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this));
} }
mp_internals->p2p.run(); // blocks until p2p goes down mp_internals->p2p.run(); // blocks until p2p goes down
if (interactive) if (interactive)
{ {
rpc_commands->stop_handling(); IPC::Daemon::stop();
} }
mp_internals->rpc.stop();
LOG_PRINT("Node stopped.", LOG_LEVEL_0); LOG_PRINT("Node stopped.", LOG_LEVEL_0);
return true; return true;
} }
@ -159,8 +153,8 @@ void t_daemon::stop()
throw std::runtime_error{"Can't stop stopped daemon"}; throw std::runtime_error{"Can't stop stopped daemon"};
} }
mp_internals->p2p.stop(); mp_internals->p2p.stop();
mp_internals->rpc.stop();
mp_internals.reset(nullptr); // Ensure resources are cleaned up before we return mp_internals.reset(nullptr); // Ensure resources are cleaned up before we return
IPC::Daemon::stop();
} }
void t_daemon::stop_p2p() void t_daemon::stop_p2p()

View File

@ -78,7 +78,7 @@ int main(int argc, char const * argv[])
command_line::add_arg(visible_options, command_line::arg_test_drop_download); command_line::add_arg(visible_options, command_line::arg_test_drop_download);
command_line::add_arg(visible_options, command_line::arg_test_dbg_lock_sleep); command_line::add_arg(visible_options, command_line::arg_test_dbg_lock_sleep);
command_line::add_arg(visible_options, command_line::arg_test_drop_download_height); command_line::add_arg(visible_options, command_line::arg_test_drop_download_height);
// Settings // Settings
bf::path default_log = default_data_dir / std::string(CRYPTONOTE_NAME ".log"); bf::path default_log = default_data_dir / std::string(CRYPTONOTE_NAME ".log");
command_line::add_arg(core_settings, daemon_args::arg_log_file, default_log.string()); command_line::add_arg(core_settings, daemon_args::arg_log_file, default_log.string());
@ -98,6 +98,11 @@ int main(int argc, char const * argv[])
// Hidden options // Hidden options
command_line::add_arg(hidden_options, daemon_args::arg_command); 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); visible_options.add(core_settings);
all_options.add(visible_options); all_options.add(visible_options);
all_options.add(hidden_options); all_options.add(hidden_options);

60
src/ipc/CMakeLists.txt Normal file
View File

@ -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})

View File

@ -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);
}
}
}

View File

@ -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

18
src/ipc/include/wallet.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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. #
################################################################################
*/

577
src/ipc/include/wap_proto.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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));
}

3145
src/ipc/wap_proto.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -27,7 +27,8 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(rpc_sources set(rpc_sources
core_rpc_server.cpp) core_rpc_server.cpp
json_rpc_http_server.cpp)
set(rpc_headers) set(rpc_headers)
@ -53,3 +54,24 @@ target_link_libraries(rpc
${EXTRA_LIBRARIES}) ${EXTRA_LIBRARIES})
add_dependencies(rpc add_dependencies(rpc
version) 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

View File

@ -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

67
src/rpc/json_rpc.cpp Normal file
View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -54,6 +54,7 @@ target_link_libraries(simplewallet
${UNBOUND_LIBRARY} ${UNBOUND_LIBRARY}
${UPNP_LIBRARIES} ${UPNP_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES}) ${EXTRA_LIBRARIES}
${NET_SKELETON_LIBRARY})
add_dependencies(simplewallet add_dependencies(simplewallet
version) version)

View File

@ -53,6 +53,8 @@
#include "storages/http_abstract_invoke.h" #include "storages/http_abstract_invoke.h"
#include "rpc/core_rpc_server_commands_defs.h" #include "rpc/core_rpc_server_commands_defs.h"
#include "wallet/wallet_rpc_server.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 "version.h"
#include "crypto/crypto.h" // for crypto::secret_key definition #include "crypto/crypto.h" // for crypto::secret_key definition
#include "mnemonics/electrum-words.h" #include "mnemonics/electrum-words.h"
@ -692,6 +694,7 @@ bool simple_wallet::deinit()
if (!m_wallet.get()) if (!m_wallet.get())
return true; return true;
m_wallet->stop_ipc_client();
return close_wallet(); return close_wallet();
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -986,21 +989,25 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
if (!try_connect_to_daemon()) if (!try_connect_to_daemon())
return true; return true;
COMMAND_RPC_START_MINING::request req; // COMMAND_RPC_START_MINING::request req;
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); // 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; bool ok = true;
size_t max_mining_threads_count = (std::max)(std::thread::hardware_concurrency(), static_cast<unsigned>(2)); size_t max_mining_threads_count = (std::max)(std::thread::hardware_concurrency(), static_cast<unsigned>(2));
if (0 == args.size()) if (0 == args.size())
{ {
req.threads_count = 1; // req.threads_count = 1;
threads_count = 1;
} }
else if (1 == args.size()) else if (1 == args.size())
{ {
uint16_t num = 1; uint16_t num = 1;
ok = string_tools::get_xtype_from_string(num, args[0]); ok = string_tools::get_xtype_from_string(num, args[0]);
ok = ok && (1 <= num && num <= max_mining_threads_count); ok = ok && (1 <= num && num <= max_mining_threads_count);
req.threads_count = num; // req.threads_count = num;
threads_count = num;
} }
else else
{ {
@ -1014,13 +1021,17 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
return true; return true;
} }
COMMAND_RPC_START_MINING::response res; // 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); // 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); // std::string err = interpret_rpc_response(r, res.status);
if (err.empty())
success_msg_writer() << tr("Mining started in daemon"); 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 else
fail_msg_writer() << tr("mining has NOT been started: ") << err; fail_msg_writer() << "mining has NOT been started: " << status;
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -1029,14 +1040,16 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args)
if (!try_connect_to_daemon()) if (!try_connect_to_daemon())
return true; return true;
COMMAND_RPC_STOP_MINING::request req; // COMMAND_RPC_STOP_MINING::request req;
COMMAND_RPC_STOP_MINING::response res; // 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); // 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); // std::string err = interpret_rpc_response(r, res.status);
if (err.empty()) uint64_t status = m_wallet->stop_mining();
success_msg_writer() << tr("Mining stopped in daemon"); if (status == IPC::STATUS_OK)
success_msg_writer() << "Mining stopped in daemon";
else else
fail_msg_writer() << tr("mining has NOT been stopped: ") << err; fail_msg_writer() << "mining has NOT been stopped: " << status;
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -1045,14 +1058,16 @@ bool simple_wallet::save_bc(const std::vector<std::string>& args)
if (!try_connect_to_daemon()) if (!try_connect_to_daemon())
return true; return true;
COMMAND_RPC_SAVE_BC::request req; // COMMAND_RPC_SAVE_BC::request req;
COMMAND_RPC_SAVE_BC::response res; // 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); // 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); // std::string err = interpret_rpc_response(r, res.status);
if (err.empty()) uint64_t status = m_wallet->save_bc();
success_msg_writer() << tr("Blockchain saved"); if (status == IPC::STATUS_OK)
success_msg_writer() << "Blockchain saved";
else else
fail_msg_writer() << tr("Blockchain can't be saved: ") << err; fail_msg_writer() << "Blockchain can't be saved: " << status;
return true; 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) uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err)
{ {
COMMAND_RPC_GET_HEIGHT::request req; // COMMAND_RPC_GET_HEIGHT::request req;
COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized<COMMAND_RPC_GET_HEIGHT::response>(); // 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); // 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); // err = interpret_rpc_response(r, res.status);
return res.height; 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) 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_electrum_seed );
command_line::add_arg(desc_params, arg_testnet); command_line::add_arg(desc_params, arg_testnet);
command_line::add_arg(desc_params, arg_restricted); 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; po::positional_options_description positional_options;
positional_options.add(arg_command.name, -1); positional_options.add(arg_command.name, -1);
@ -1982,7 +2005,7 @@ int main(int argc, char* argv[])
log_level % log_file_path.string(); log_level % log_file_path.string();
log_space::get_set_log_detalisation_level(true, log_level); 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); log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2);
//runs wallet with rpc interface //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); daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
tools::wallet2 wal(testnet,restricted); tools::wallet2 wal(testnet,restricted);
// RPC::Wallet::init(&wal);
try try
{ {
LOG_PRINT_L0(sw::tr("Loading wallet...")); 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()); LOG_ERROR(sw::tr("Wallet initialization failed: ") << e.what());
return 1; 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); tools::wallet_rpc_server wrpc(wal);
bool r = wrpc.init(vm); bool r = wrpc.init(vm);
CHECK_AND_ASSERT_MES(r, 1, sw::tr("Failed to initialize wallet rpc server")); 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] { tools::signal_handler::install([&wrpc, &wal] {
wrpc.send_stop_signal(); wrpc.send_stop_signal();
wal.store(); wal.store();
}); });
LOG_PRINT_L0(sw::tr("Starting wallet rpc server")); LOG_PRINT_L0(sw::tr("Starting wallet rpc server"));
wrpc.run(); wrpc.run();
LOG_PRINT_L0(sw::tr("Stopped wallet rpc server")); LOG_PRINT_L0(sw::tr("Stopped wallet rpc server"));
wal.stop_ipc_client();
try try
{ {
LOG_PRINT_L0(sw::tr("Storing wallet...")); 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()); LOG_ERROR(sw::tr("Failed to store wallet: ") << e.what());
return 1; return 1;
} }
}else }
else
{ {
//runs wallet with console interface //runs wallet with console interface
r = w.init(vm); r = w.init(vm);

View File

@ -28,7 +28,8 @@
set(wallet_sources set(wallet_sources
wallet2.cpp wallet2.cpp
wallet_rpc_server.cpp) wallet_rpc_server.cpp
wallet_json_rpc_handlers.cpp)
set(wallet_headers) set(wallet_headers)
@ -47,10 +48,13 @@ bitmonero_add_library(wallet
${wallet_private_headers}) ${wallet_private_headers})
target_link_libraries(wallet target_link_libraries(wallet
LINK_PUBLIC LINK_PUBLIC
client_ipc
cryptonote_core cryptonote_core
mnemonics mnemonics
LINK_PRIVATE LINK_PRIVATE
${Boost_SERIALIZATION_LIBRARY} ${Boost_SERIALIZATION_LIBRARY}
${Boost_SYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY} ${Boost_THREAD_LIBRARY}
${ZMQ_LIB}
${CZMQ_LIB}
${EXTRA_LIBRARIES}) ${EXTRA_LIBRARIES})

View File

@ -92,7 +92,8 @@ namespace tools
const size_t MAX_SPLIT_ATTEMPTS = 30; 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_upper_transaction_size_limit = upper_transaction_size_limit;
m_daemon_address = daemon_address; 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) 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 //good news - got money! take care about it
//usually we have only one transfer for user in transaction //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::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res); cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res);
req.txid = get_transaction_hash(tx); crypto::hash tx_id = 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"); zchunk_t *tx_id_chunk = zchunk_new(tx_id.data, crypto::HASH_SIZE);
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin"); int rc = wap_client_output_indexes(ipc_client, &tx_id_chunk);
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, THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "get_output_indexes");
"transactions outputs size=" + std::to_string(tx.vout.size()) + uint64_t status = wap_client_status(ipc_client);
" not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size())); 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) 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(); transfer_details& td = m_transfers.back();
td.m_block_height = height; td.m_block_height = height;
td.m_internal_output_index = o; 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_tx = tx;
td.m_spent = false; td.m_spent = false;
cryptonote::keypair in_ephemeral; cryptonote::keypair in_ephemeral;
@ -348,24 +363,63 @@ void wallet2::get_short_chain_history(std::list<crypto::hash>& ids) const
if(!genesis_included) if(!genesis_included)
ids.push_back(m_blockchain[0]); 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) 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; blocks_added = 0;
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); std::list<crypto::hash> block_ids;
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); get_short_chain_history(block_ids);
get_short_chain_history(req.block_ids); std::list<char*> size_prepended_block_ids;
req.start_height = start_height; zlist_t *list = zlist_new();
bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getblocks.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); for (std::list<crypto::hash>::iterator it = block_ids.begin(); it != block_ids.end(); it++) {
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin"); char *block_id = new char[crypto::HASH_SIZE + 1];
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin"); block_id[0] = crypto::HASH_SIZE;
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status); 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; uint64_t status = wap_client_status(ipc_client);
BOOST_FOREACH(auto& bl_entry, res.blocks) 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; 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); THROW_WALLET_EXCEPTION_IF(!r, error::block_parse_error, bl_entry.block);
crypto::hash bl_id = get_block_hash(bl); 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]) else if(bl_id != m_blockchain[current_index])
{ {
//split detected here !!! //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) + "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])); string_tools::pod_to_hex(m_blockchain[current_index]));
detach_blockchain(current_index); detach_blockchain(current_index);
@ -492,6 +546,8 @@ void wallet2::detach_blockchain(uint64_t height)
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::deinit() bool wallet2::deinit()
{ {
// Great, it all works. Now to shutdown, we use the destroy method,
// which does a proper deconnect handshake internally:
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -846,18 +902,7 @@ bool wallet2::prepare_file_names(const std::string& file_path)
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::check_connection() bool wallet2::check_connection()
{ {
if(m_http_client.is_connected()) return ipc_client && wap_client_connected(ipc_client);
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);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const 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) void wallet2::commit_tx(pending_tx& ptx)
{ {
using namespace cryptonote; using namespace cryptonote;
crypto::hash txid;
COMMAND_RPC_SEND_RAW_TX::request req; connect_to_daemon();
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); THROW_WALLET_EXCEPTION_IF(!check_connection(), error::no_connection_to_daemon, "send_raw_transaction");
COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; std::string tx_as_hex_string = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx));
bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000); zchunk_t *tx_as_hex = zchunk_new((void*)tx_as_hex_string.c_str(), tx_as_hex_string.length());
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); int rc = wap_client_put(ipc_client, &tx_as_hex);
THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); uint64_t status = wap_client_status(ipc_client);
THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status); 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); txid = get_transaction_hash(ptx.tx);
add_unconfirmed_tx(ptx.tx, ptx.change_dts.amount); 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); COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
if(fake_outputs_count) if(fake_outputs_count)
{ {
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req); connect_to_daemon();
req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key 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) 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, 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) + "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())); " 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));
bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000); int rc = wap_client_random_outs(ipc_client, outs_count, &amounts_frame);
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin"); uint64_t status = wap_client_status(ipc_client);
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin"); THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "getrandomouts");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status); // 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);
}
THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error, 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 = " + "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())); 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); 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);
}
} }

View File

@ -48,6 +48,7 @@
#include "crypto/hash.h" #include "crypto/hash.h"
#include "wallet_errors.h" #include "wallet_errors.h"
#include "daemon_ipc_handlers.h"
#include <iostream> #include <iostream>
#define DEFAULT_TX_SPENDABLE_AGE 10 #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) {}; wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers (false), m_store_tx_keys(false) {};
public: 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 struct transfer_details
{ {
uint64_t m_block_height; uint64_t m_block_height;
@ -188,7 +200,9 @@ namespace tools
// free block size. TODO: fix this so that it actually takes // free block size. TODO: fix this so that it actually takes
// into account the current median block size rather than // into account the current median block size rather than
// the minimum block size. // 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(); bool deinit();
void stop() { m_run.store(false, std::memory_order_relaxed); } void stop() { m_run.store(false, std::memory_order_relaxed); }
@ -268,6 +282,7 @@ namespace tools
a & m_tx_keys; a & m_tx_keys;
} }
void stop_ipc_client();
/*! /*!
* \brief Check if wallet keys and bin files exist * \brief Check if wallet keys and bin files exist
* \param file_path Wallet file path * \param file_path Wallet file path
@ -290,6 +305,11 @@ namespace tools
static std::string address_from_txt_record(const std::string& s); 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; } bool always_confirm_transfers() const { return m_always_confirm_transfers; }
void always_confirm_transfers(bool always) { m_always_confirm_transfers = always; } void always_confirm_transfers(bool always) { m_always_confirm_transfers = always; }
bool store_tx_keys() const { return m_store_tx_keys; } 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_tx_spendtime_unlocked(uint64_t unlock_time) const;
bool is_transfer_unlocked(const transfer_details& td) const; bool is_transfer_unlocked(const transfer_details& td) const;
bool clear(); 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); 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); 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); bool prepare_file_names(const std::string& file_path);
void process_unconfirmed(const cryptonote::transaction& tx); void process_unconfirmed(const cryptonote::transaction& tx);
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount); void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount);
void generate_genesis(cryptonote::block& b); void generate_genesis(cryptonote::block& b);
void connect_to_daemon();
void check_genesis(const crypto::hash& genesis_hash) const; //throws void check_genesis(const crypto::hash& genesis_hash) const; //throws
bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const; bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const;
@ -351,6 +373,7 @@ namespace tools
bool m_restricted; bool m_restricted;
std::string seed_language; /*!< Language of the mnemonics (seed). */ std::string seed_language; /*!< Language of the mnemonics (seed). */
bool is_old_file_format; /*!< Whether the wallet file is of an old file format */ 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_watch_only; /*!< no spend key */
bool m_always_confirm_transfers; bool m_always_confirm_transfers;
bool m_store_tx_keys; /*!< request txkey to be returned in RPC, and store in the wallet cache file */ 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); COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
if(fake_outputs_count) if(fake_outputs_count)
{ {
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req); connect_to_daemon();
req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key 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) 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, 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) + "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())); " 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); zframe_t *amounts_frame = zframe_new(&amounts[0], amounts.size() * sizeof(uint64_t));
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin"); int rc = wap_client_random_outs(ipc_client, outs_count, &amounts_frame);
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); uint64_t status = wap_client_status(ipc_client);
THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "getrandomouts");
"daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " + // TODO: Use a code to string mapping of errors
std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_transfers.size())); 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; 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) BOOST_FOREACH(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs, daemon_resp.outs)

View File

@ -440,7 +440,7 @@ namespace tools
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
struct tx_rejected : public transfer_error 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") : transfer_error(std::move(loc), "transaction was rejected by daemon")
, m_tx(tx) , m_tx(tx)
, m_status(status) , m_status(status)
@ -448,7 +448,7 @@ namespace tools
} }
const cryptonote::transaction& tx() const { return m_tx; } 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 std::string to_string() const
{ {
@ -461,7 +461,7 @@ namespace tools
private: private:
cryptonote::transaction m_tx; cryptonote::transaction m_tx;
std::string m_status; uint64_t m_status;
}; };
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
struct tx_sum_overflow : public transfer_error 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 struct no_connection_to_daemon : public wallet_rpc_error
{ {

View File

@ -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;
}
}
}
}

View File

@ -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

View File

@ -137,8 +137,6 @@ bool transactions_flow_test(std::string& working_folder,
return false; return false;
} }
w1.init(daemon_addr_a);
size_t blocks_fetched = 0; size_t blocks_fetched = 0;
bool received_money; bool received_money;
bool ok; bool ok;