Files: fb6db7d0171f85ee76abb0f0ee5f1827c5eec31e / electrumc.c
3844 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 | type = strrchr(host, ':'); |
98 | if (!type) errx(1, "address should be in format <host>:<port>:<type>"); |
99 | *type++ = '\0'; |
100 | if (!strcmp(type, "t")) use_ssl = false; |
101 | else if (!strcmp(type, "s")) use_ssl = true; |
102 | else errx(1, "address type must be 't' or 's', but found: '%s'", type); |
103 | |
104 | port = strrchr(host, ':'); |
105 | if (!port) errx(1, "address should be in format <host>:<port>:<type>"); |
106 | *port++ = '\0'; |
107 | |
108 | if (!*host) errx(1, "address hostname missing"); |
109 | |
110 | if (use_ssl) { |
111 | ssl_ctx = SSL_CTX_new(TLS_client_method()); |
112 | if (ssl_ctx == NULL) errx(1, "failed to create ssl context"); |
113 | (void)SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); |
114 | |
115 | ssl = SSL_new(ssl_ctx); |
116 | if (ssl_ctx == NULL) errx(1, "failed to create ssl object"); |
117 | } |
118 | |
119 | s = tcp_connect(host, port); |
120 | if (s < 0) err(1, "failed to connect"); |
121 | |
122 | if (use_ssl) { |
123 | rc = SSL_set_fd(ssl, s); |
124 | if (rc < 0) errx(1, "failed to set ssl s"); |
125 | |
126 | rc = SSL_connect(ssl); |
127 | if (rc < 0) errx(1, "ssl_connect failed"); |
128 | } |
129 | |
130 | char buf[8192]; |
131 | int len = snprintf(buf, sizeof(buf)-1, "{\"id\":0,\"method\":\"%s\"%s%s}\n", |
132 | method, |
133 | params ? ",\"params\":" : "", |
134 | params ? params : ""); |
135 | if (len < 0 || len == (int)sizeof(buf)) errx(1, "request too long"); |
136 | |
137 | if (use_ssl) { |
138 | rc = SSL_write(ssl, buf, len); |
139 | if (rc < 0) errx(1, "ssl write failed"); |
140 | } else { |
141 | len = write_all(s, buf, len); |
142 | if (len < 0) err(1, "write"); |
143 | } |
144 | |
145 | do rc = read_response(s, ssl, use_ssl); |
146 | while (rc == 0); |
147 | if (rc < 0 && errno == 0) errx(1, "incomplete response"); |
148 | if (rc < 0) err(1, "read"); |
149 | |
150 | if (use_ssl) { |
151 | rc = SSL_shutdown(ssl); |
152 | if (rc < 0) errx(1, "ssl shutdown failed"); |
153 | } |
154 | close(s); |
155 | } |
156 |
Built with git-ssb-web