Files: 0a1f41b812c27e9ddccaae10f72b6312f3dda913 / electrumc.c
3740 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(int status) { |
54 | fputs("usage: electrumc <address> <port> <method> [<params>]\n", |
55 | status ? stderr : stdout); |
56 | exit(status); |
57 | } |
58 | |
59 | static int read_response(int s, SSL *ssl, bool use_ssl) { |
60 | char buf[8192]; |
61 | int len; |
62 | |
63 | if (use_ssl) { |
64 | len = SSL_pending(ssl); |
65 | if (len < 0) errx(1, "ssl pending"); |
66 | if (len == 0) len = 1; |
67 | else if (len > (int)sizeof(buf)) len = sizeof(buf); |
68 | len = SSL_read(ssl, buf, len); |
69 | if (len < 0) errx(1, "ssl read failed"); |
70 | } else { |
71 | len = read(s, buf, sizeof(buf)); |
72 | if (len == 0) return -1; |
73 | if (len < 0) err(1, "read"); |
74 | } |
75 | |
76 | len = fwrite(buf, 1, len, stdout); |
77 | if (len < 0) errx(1, "failed to print response"); |
78 | |
79 | if (buf[len-1] == '\n') return 1; |
80 | return 0; |
81 | } |
82 | |
83 | int main(int argc, char *argv[]) { |
84 | int rc; |
85 | SSL *ssl; |
86 | SSL_CTX *ssl_ctx; |
87 | char *host, *port, *type, *method, *params; |
88 | int s; |
89 | bool use_ssl; |
90 | |
91 | if (argc == 1) usage(0); |
92 | if (argc != 3 && argc != 4) usage(1); |
93 | host = argv[1]; |
94 | method = argv[2]; |
95 | params = argc == 3 ? NULL : argv[3]; |
96 | |
97 | port = strchr(host, ':'); |
98 | if (!port) errx(1, "address should be in format <host>:<port>:<type>"); |
99 | *port++ = '\0'; |
100 | type = strchr(port, ':'); |
101 | if (!type) errx(1, "address type missing"); |
102 | *type++ = '\0'; |
103 | |
104 | if (!strcmp(type, "t")) use_ssl = false; |
105 | else if (!strcmp(type, "s")) use_ssl = true; |
106 | else errx(1, "address type must be 't' or 's'"); |
107 | |
108 | if (use_ssl) { |
109 | ssl_ctx = SSL_CTX_new(TLS_client_method()); |
110 | if (ssl_ctx == NULL) errx(1, "failed to create ssl context"); |
111 | (void)SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); |
112 | |
113 | ssl = SSL_new(ssl_ctx); |
114 | if (ssl_ctx == NULL) errx(1, "failed to create ssl object"); |
115 | } |
116 | |
117 | s = tcp_connect(host, port); |
118 | if (s < 0) err(1, "failed to connect"); |
119 | |
120 | if (use_ssl) { |
121 | rc = SSL_set_fd(ssl, s); |
122 | if (rc < 0) errx(1, "failed to set ssl s"); |
123 | |
124 | rc = SSL_connect(ssl); |
125 | if (rc < 0) errx(1, "ssl_connect failed"); |
126 | } |
127 | |
128 | char buf[8192]; |
129 | int len = snprintf(buf, sizeof(buf)-1, "{\"id\":0,\"method\":\"%s\"%s%s}\n", |
130 | method, |
131 | params ? ",\"params\":" : "", |
132 | params ? params : ""); |
133 | if (len < 0 || len == (int)sizeof(buf)) errx(1, "request too long"); |
134 | |
135 | if (use_ssl) { |
136 | rc = SSL_write(ssl, buf, len); |
137 | if (rc < 0) errx(1, "ssl write failed"); |
138 | } else { |
139 | len = write_all(s, buf, len); |
140 | if (len < 0) err(1, "write"); |
141 | } |
142 | |
143 | do rc = read_response(s, ssl, use_ssl); |
144 | while (rc == 0); |
145 | if (rc < 0 && errno == 0) errx(1, "incomplete response"); |
146 | if (rc < 0) err(1, "read"); |
147 | |
148 | if (use_ssl) { |
149 | rc = SSL_shutdown(ssl); |
150 | if (rc < 0) errx(1, "ssl shutdown failed"); |
151 | } |
152 | close(s); |
153 | } |
154 |
Built with git-ssb-web