git ssb

0+

cel / electrumc



Tree: a8d58dca64f5a3405f0b03c136e5a70f94226297

Files: a8d58dca64f5a3405f0b03c136e5a70f94226297 / electrumc.c

5154 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/err.h>
12#include <openssl/ssl.h>
13#include <openssl/x509v3.h>
14
15static int tcp_connect(const char *host, const char *port) {
16 struct addrinfo hints;
17 struct addrinfo *result, *rp;
18 int s;
19 int fd;
20 int err;
21
22 memset(&hints, 0, sizeof(hints));
23 hints.ai_family = AF_UNSPEC;
24 hints.ai_protocol = IPPROTO_TCP;
25
26 s = getaddrinfo(host, port, &hints, &result);
27 if (s < 0) errx(1, "unable to resolve host: %s", gai_strerror(s));
28
29 for (rp = result; rp; rp = rp->ai_next) {
30 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
31 if (fd < 0) continue;
32 if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) break;
33 err = errno;
34 close(fd);
35 errno = err;
36 }
37 if (rp == NULL) fd = -1;
38
39 freeaddrinfo(result);
40 return fd;
41}
42
43static int write_all(int fd, const void *buf, size_t count) {
44 ssize_t nbytes;
45 while (count > 0) {
46 nbytes = write(fd, buf, count);
47 if (nbytes < 0 && errno == EINTR) continue;
48 if (nbytes < 0) return -1;
49 buf += nbytes;
50 count -= nbytes;
51 }
52 return 0;
53}
54
55static void usage(int status) {
56 fputs("usage: electrumc <address> <port> <method> [<params>]\n",
57 status ? stderr : stdout);
58 exit(status);
59}
60
61static const char *ssl_strerror(int err) {
62 switch (err) {
63 case SSL_ERROR_NONE: return "no error";
64 case SSL_ERROR_ZERO_RETURN: return "connection closed";
65 case SSL_ERROR_WANT_READ: return "read did not complete";
66 case SSL_ERROR_WANT_WRITE: return "write did not complete";
67 case SSL_ERROR_WANT_CONNECT: return "connect did not complete";
68 case SSL_ERROR_WANT_ACCEPT: return "accept did not complete";
69 case SSL_ERROR_WANT_X509_LOOKUP: return "cert application callback set";
70 case SSL_ERROR_WANT_ASYNC: return "asynchronous engine is still processing data";
71 case SSL_ERROR_WANT_ASYNC_JOB: return "no async jobs available";
72 case SSL_ERROR_SYSCALL: return strerror(errno);
73 case SSL_ERROR_SSL: ERR_print_errors_fp(stderr); return "protocol error"; //return ERR_error_string(ERR_get_error(), NULL);
74 default: return "unknown error";
75 }
76}
77
78static void ssl_err(const char *prefix, const SSL *ssl, int rc) {
79 rc = SSL_get_error(ssl, rc);
80 errx(1, "%s: %s", prefix, ssl_strerror(rc));
81}
82
83static int read_response(int s, SSL *ssl, bool use_ssl) {
84 char buf[8192];
85 int len;
86
87 if (use_ssl) {
88 len = SSL_pending(ssl);
89 if (len < 0) errx(1, "ssl pending");
90 if (len == 0) len = 1;
91 else if (len > (int)sizeof(buf)) len = sizeof(buf);
92 len = SSL_read(ssl, buf, len);
93 if (len < 0) ssl_err("SSL_read", ssl, len);
94 } else {
95 len = read(s, buf, sizeof(buf));
96 if (len == 0) return -1;
97 if (len < 0) err(1, "read");
98 }
99
100 len = fwrite(buf, 1, len, stdout);
101 if (len < 0) errx(1, "failed to print response");
102
103 if (buf[len-1] == '\n') return 1;
104 return 0;
105}
106
107int main(int argc, char *argv[]) {
108 int rc;
109 SSL *ssl;
110 SSL_CTX *ssl_ctx;
111 char *host, *port, *type, *method, *params;
112 int s;
113 bool use_ssl;
114
115 if (argc == 1) usage(0);
116 if (argc != 3 && argc != 4) usage(1);
117 host = argv[1];
118 method = argv[2];
119 params = argc == 3 ? NULL : argv[3];
120
121 type = strrchr(host, ':');
122 if (!type) errx(1, "address should be in format <host>:<port>:<type>");
123 *type++ = '\0';
124 if (!strcmp(type, "t")) use_ssl = false;
125 else if (!strcmp(type, "s")) use_ssl = true;
126 else errx(1, "address type must be 't' or 's', but found: '%s'", type);
127
128 port = strrchr(host, ':');
129 if (!port) errx(1, "address should be in format <host>:<port>:<type>");
130 *port++ = '\0';
131
132 if (!*host) errx(1, "address hostname missing");
133
134 if (use_ssl) {
135 SSL_load_error_strings();
136
137 ssl_ctx = SSL_CTX_new(TLS_client_method());
138 if (ssl_ctx == NULL) errx(1, "failed to create ssl context");
139 (void)SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
140
141 ssl = SSL_new(ssl_ctx);
142 if (ssl_ctx == NULL) errx(1, "failed to create ssl object");
143 }
144
145 s = tcp_connect(host, port);
146 if (s < 0) err(1, "failed to connect");
147
148 if (use_ssl) {
149 rc = SSL_set_fd(ssl, s);
150 if (rc < 0) errx(1, "failed to set SSL fd");
151
152 SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
153 if (!SSL_set1_host(ssl, host)) errx(1, "failed to set SSL host");
154
155 SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
156
157 rc = SSL_connect(ssl);
158 if (rc < 0) ssl_err("SSL_connect", ssl, rc);
159 }
160
161 char buf[8192];
162 int len = snprintf(buf, sizeof(buf)-1, "{\"id\":0,\"method\":\"%s\"%s%s}\n",
163 method,
164 params ? ",\"params\":" : "",
165 params ? params : "");
166 if (len < 0 || len == (int)sizeof(buf)) errx(1, "request too long");
167
168 if (use_ssl) {
169 rc = SSL_write(ssl, buf, len);
170 if (rc < 0) ssl_err("SSL_write", ssl, rc);
171 } else {
172 len = write_all(s, buf, len);
173 if (len < 0) err(1, "write");
174 }
175
176 do rc = read_response(s, ssl, use_ssl);
177 while (rc == 0);
178 if (rc < 0 && errno == 0) errx(1, "incomplete response");
179 if (rc < 0) err(1, "read");
180
181 if (use_ssl) {
182 rc = SSL_shutdown(ssl);
183 if (rc == 0) rc = SSL_shutdown(ssl);
184 if (rc < 0) ssl_err("SSL_shutdown", ssl, rc);
185 }
186 close(s);
187}
188

Built with git-ssb-web