git ssb

0+

cel / electrumc



Commit b87295647c8a0f85d7f4b8f32a12dda732d12b7d

Init

cel committed on 8/28/2018, 3:25:04 AM

Files changed

.gitignoreadded
LICENSEadded
Makefileadded
README.mdadded
electrumc.1added
electrumc.cadded
.gitignoreView
@@ -1,0 +1,1 @@
1 +electrumc
LICENSEView
@@ -1,0 +1,6 @@
1 +© 2018 cel <@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519>
2 +
3 +Copying and distribution of this file, with or without modification,
4 +are permitted in any medium without royalty provided the copyright
5 +notice and this notice are preserved. This file is offered as-is,
6 +without any warranty.
MakefileView
@@ -1,0 +1,36 @@
1 +# See LICENSE file for copyright and license details.
2 +
3 +BIN = electrumc
4 +MAN1 = $(BIN).1
5 +
6 +PREFIX = /usr/local
7 +BINDIR = $(PREFIX)/bin
8 +MANDIR = $(PREFIX)/share/man
9 +
10 +CFLAGS = -Wall -Werror -Wextra
11 +LDLIBS = -lssl
12 +
13 +all: $(BIN)
14 +
15 +$(BIN): $(BIN).c
16 +
17 +install: all
18 + @mkdir -vp $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
19 + @cp -vf $(BIN) $(DESTDIR)$(BINDIR)
20 + @cp -vf $(MAN1) $(DESTDIR)$(MANDIR)/man1
21 +
22 +link: all
23 + @mkdir -vp $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
24 + @ln -rsvf $(BIN) $(DESTDIR)$(BINDIR)
25 + @ln -rsvf $(MAN1) $(DESTDIR)$(MANDIR)/man1
26 +
27 +uninstall:
28 + @rm -vf \
29 + $(BIN:%=$(DESTDIR)$(BINDIR)/%) \
30 + $(MAN1:%=$(DESTDIR)$(MANDIR)/man1/%)
31 +
32 +clean:
33 + @rm -vf $(BIN)
34 +
35 +.PHONY:
36 + all install link uninstall clean
README.mdView
@@ -1,0 +1,23 @@
1 +# electrumc
2 +
3 +An RPC client utility program for the [Electrum protocol][]. It makes a single
4 +request to a server with the method and parameters you specify, and then
5 +outputs the response from the server.
6 +
7 +## Usage
8 +
9 +```
10 +electrumc <host> <port> <method> [<params>]
11 +```
12 +
13 +`port` follows Electrum's convention to prefix the port number with `t` for
14 +TCP, or `s` for SSL over TCP.
15 +
16 +Example: requesting a server's donation address:
17 +```
18 +$ electrumc electrum.faircoin.world s51812 server.donation_address
19 +{"jsonrpc": "2.0", "id": 0, "result": "fairVs8iHyLzgHQrdxb9j6hR4WGpdDbKN3"}
20 +
21 +```
22 +
23 +[Electrum protocol]: https://electrumx.readthedocs.io/en/latest/protocol-methods.html
electrumc.1View
@@ -1,0 +1,45 @@
1 +.\" See LICENSE file for copyright and license details.
2 +.Dd 2018-08-27
3 +.Dt electrumc 1
4 +.Os
5 +.ds REPO ssb://
6 +.Sh NAME
7 +.Nm electrumc
8 +.Nd Electrum RPC client
9 +.Sh SYNOPSIS
10 +.Nm
11 +.Ar host
12 +.Ar port
13 +.Ar method
14 +.Op Ar params
15 +.Sh DESCRIPTION
16 +Connect to a Electrum server, make a request, and output the response.
17 +.Pp
18 +Arguments:
19 +.Bl -tag -width indent
20 +.It Ar host
21 +host to connect to
22 +.It Ar port
23 +port to connect to, prefixed by
24 +.Ql t
25 +to use TCP, or
26 +.Ql s
27 +for SSL over TCP
28 +.It Ar method
29 +RPC method to call
30 +.It Op Ar params
31 +params as JSON to include in the request
32 +.El
33 +.Sh EXIT
34 +.Ex -std
35 +An error in the server's JSON response is treated as success.
36 +.Sh AUTHORS
37 +The
38 +.Nm
39 +command and manual were written by
40 +.An cel Aq @f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519 .
41 +.Sh BUGS
42 +.Pp
43 +Please report any bugs by making a post on SSB mentioning the repo,
44 +.Lk \*[REPO]
45 +and author.
electrumc.cView
@@ -1,0 +1,148 @@
1 +/* See LICENSE file for copyright and license details. */
2 +#include <err.h>
3 +#include <errno.h>
4 +#include <netdb.h>
5 +#include <netinet/in.h>
6 +#include <stdbool.h>
7 +#include <string.h>
8 +#include <sys/socket.h>
9 +#include <unistd.h>
10 +
11 +#include <openssl/ssl.h>
12 +
13 +static int tcp_connect(const char *host, const char *port) {
14 + struct addrinfo hints;
15 + struct addrinfo *result, *rp;
16 + int s;
17 + int fd;
18 + int err;
19 +
20 + memset(&hints, 0, sizeof(hints));
21 + hints.ai_family = AF_UNSPEC;
22 + hints.ai_protocol = IPPROTO_TCP;
23 +
24 + s = getaddrinfo(host, port, &hints, &result);
25 + if (s < 0) errx(1, "unable to resolve host: %s", gai_strerror(s));
26 +
27 + for (rp = result; rp; rp = rp->ai_next) {
28 + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
29 + if (fd < 0) continue;
30 + if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) break;
31 + err = errno;
32 + close(fd);
33 + errno = err;
34 + }
35 + if (rp == NULL) fd = -1;
36 +
37 + freeaddrinfo(result);
38 + return fd;
39 +}
40 +
41 +static int write_all(int fd, const void *buf, size_t count) {
42 + ssize_t nbytes;
43 + while (count > 0) {
44 + nbytes = write(fd, buf, count);
45 + if (nbytes < 0 && errno == EINTR) continue;
46 + if (nbytes < 0) return -1;
47 + buf += nbytes;
48 + count -= nbytes;
49 + }
50 + return 0;
51 +}
52 +
53 +static void usage() {
54 + fputs("usage: electrumc <host> <port> <method> [<params>]\n", stderr);
55 + exit(EXIT_FAILURE);
56 +}
57 +
58 +static int read_response(int s, SSL *ssl, bool use_ssl) {
59 + char buf[8192];
60 + int len;
61 +
62 + if (use_ssl) {
63 + len = SSL_pending(ssl);
64 + if (len < 0) errx(1, "ssl pending");
65 + if (len == 0) len = 1;
66 + else if (len > (int)sizeof(buf)) len = sizeof(buf);
67 + len = SSL_read(ssl, buf, len);
68 + if (len < 0) errx(1, "ssl read failed");
69 + } else {
70 + len = read(s, buf, sizeof(buf));
71 + if (len == 0) return -1;
72 + if (len < 0) err(1, "read");
73 + }
74 +
75 + len = fwrite(buf, 1, len, stdout);
76 + if (len < 0) errx(1, "failed to print response");
77 +
78 + if (buf[len-1] == '\n') return 1;
79 + return 0;
80 +}
81 +
82 +int main(int argc, char *argv[]) {
83 + int rc;
84 + SSL *ssl;
85 + SSL_CTX *ssl_ctx;
86 + const char *host, *port, *method, *params;
87 + int s;
88 + bool use_ssl;
89 +
90 + if (argc != 4 && argc != 5) usage();
91 + host = argv[1];
92 + port = argv[2];
93 + method = argv[3];
94 + params = argc == 4 ? NULL : argv[4];
95 +
96 + switch (port[0]) {
97 + case 't': use_ssl = false; break;
98 + case 's': use_ssl = true; break;
99 + default: errx(1, "port should begin with 't' for TCP or 's' for TCP+SSL");
100 + }
101 + port++;
102 +
103 + if (use_ssl) {
104 + ssl_ctx = SSL_CTX_new(TLS_client_method());
105 + if (ssl_ctx == NULL) errx(1, "failed to create ssl context");
106 + (void)SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
107 +
108 + ssl = SSL_new(ssl_ctx);
109 + if (ssl_ctx == NULL) errx(1, "failed to create ssl object");
110 + }
111 +
112 + s = tcp_connect(host, port);
113 + if (s < 0) err(1, "failed to connect");
114 +
115 + if (use_ssl) {
116 + rc = SSL_set_fd(ssl, s);
117 + if (rc < 0) errx(1, "failed to set ssl s");
118 +
119 + rc = SSL_connect(ssl);
120 + if (rc < 0) errx(1, "ssl_connect failed");
121 + }
122 +
123 + char buf[8192];
124 + int len = snprintf(buf, sizeof(buf)-1, "{\"id\":0,\"method\":\"%s\"%s%s}\n",
125 + method,
126 + params ? ",\"params\":" : "",
127 + params ? params : "");
128 + if (len < 0 || len == (int)sizeof(buf)) errx(1, "request too long");
129 +
130 + if (use_ssl) {
131 + rc = SSL_write(ssl, buf, len);
132 + if (rc < 0) errx(1, "ssl write failed");
133 + } else {
134 + len = write_all(s, buf, len);
135 + if (len < 0) err(1, "write");
136 + }
137 +
138 + do rc = read_response(s, ssl, use_ssl);
139 + while (rc == 0);
140 + if (rc < 0 && errno == 0) errx(1, "incomplete response");
141 + if (rc < 0) err(1, "read");
142 +
143 + if (use_ssl) {
144 + rc = SSL_shutdown(ssl);
145 + if (rc < 0) errx(1, "ssl shutdown failed");
146 + }
147 + close(s);
148 +}

Built with git-ssb-web