Files: a8d58dca64f5a3405f0b03c136e5a70f94226297 / electrumc.c
5154 bytesRaw
1 | /* See LICENSE file for copyright and license details. */ |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | static 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 | |
43 | static 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 | |
55 | static void usage(int status) { |
56 | fputs("usage: electrumc <address> <port> <method> [<params>]\n", |
57 | status ? stderr : stdout); |
58 | exit(status); |
59 | } |
60 | |
61 | static 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 | |
78 | static 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 | |
83 | static 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 | |
107 | int 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