Files: b87295647c8a0f85d7f4b8f32a12dda732d12b7d / electrumc.c
3532 bytesRaw
1 | /* See LICENSE file for copyright and license details. */ |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
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 | } |
149 |
Built with git-ssb-web