git ssb

0+

cel / electrumc



Tree: b87295647c8a0f85d7f4b8f32a12dda732d12b7d

Files: b87295647c8a0f85d7f4b8f32a12dda732d12b7d / electrumc.c

3532 bytesRaw
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
13static 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
41static 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
53static void usage() {
54 fputs("usage: electrumc <host> <port> <method> [<params>]\n", stderr);
55 exit(EXIT_FAILURE);
56}
57
58static 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
82int 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}
149

Built with git-ssb-web