Line data Source code
1 : /**
2 : * The MIT License (MIT)
3 : *
4 : * Copyright (c) 2021 RSK Labs Ltd
5 : *
6 : * Permission is hereby granted, free of charge, to any person obtaining a copy
7 : * of this software and associated documentation files (the "Software"), to
8 : * deal in the Software without restriction, including without limitation the
9 : * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 : * sell copies of the Software, and to permit persons to whom the Software is
11 : * furnished to do so, subject to the following conditions:
12 : *
13 : * The above copyright notice and this permission notice shall be included in
14 : * all copies or substantial portions of the Software.
15 : *
16 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 : * IN THE SOFTWARE.
23 : */
24 :
25 : #include <stdbool.h>
26 : #include <string.h>
27 : #include <unistd.h>
28 : #include <netdb.h>
29 : #include <netinet/tcp.h>
30 : #include <sys/ioctl.h>
31 :
32 : #include "io.h"
33 : #include "log.h"
34 :
35 : /**
36 : * APDU buffer
37 : */
38 : unsigned char io_apdu_buffer[APDU_BUFFER_SIZE];
39 :
40 : /**
41 : * For the TCP server
42 : */
43 : int serverfd;
44 : int connfd;
45 : struct sockaddr_in servaddr, cliaddr;
46 :
47 28 : static void close_and_reset_fd(int *fd) {
48 28 : if (fd && (*fd != -1)) {
49 28 : close(*fd);
50 28 : *fd = -1;
51 : }
52 28 : }
53 :
54 13 : static int start_server(int port, const char *host) {
55 : int sockfd;
56 : struct hostent *hostinfo;
57 13 : hostinfo = gethostbyname(host);
58 :
59 13 : if (hostinfo == NULL) {
60 0 : LOG("Host not found.\n");
61 0 : return -1;
62 : }
63 :
64 : // socket create and verification
65 13 : sockfd = socket(AF_INET, SOCK_STREAM, 0);
66 13 : if (sockfd == -1) {
67 0 : LOG("Socket creation failed...\n");
68 0 : return -1;
69 : }
70 :
71 13 : explicit_bzero(&servaddr, sizeof(servaddr));
72 :
73 13 : if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) <
74 : 0) {
75 0 : LOG("Socket option setting failed failed\n");
76 0 : return -1;
77 : }
78 :
79 13 : if (setsockopt(sockfd, SOL_TCP, TCP_NODELAY, &(int){1}, sizeof(int)) < 0) {
80 0 : LOG("Socket option setting failed failed\n");
81 0 : return -1;
82 : }
83 :
84 : // Set address and port
85 13 : servaddr.sin_family = AF_INET;
86 13 : memcpy(&servaddr.sin_addr, hostinfo->h_addr_list[0], hostinfo->h_length);
87 13 : servaddr.sin_port = htons(port);
88 :
89 : // Binding newly created socket to given IP and verification
90 13 : if ((bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) != 0) {
91 0 : LOG("Socket bind failed...\n");
92 0 : return -1;
93 : }
94 :
95 : // Now server is ready to listen and verification
96 13 : if ((listen(sockfd, 5)) != 0) {
97 0 : LOG("Listen failed...\n");
98 0 : return -1;
99 : }
100 :
101 13 : LOG("Server listening...\n");
102 13 : return sockfd;
103 : }
104 :
105 15 : static bool accept_connection() {
106 15 : socklen_t len = sizeof(cliaddr);
107 15 : connfd = accept(serverfd, (struct sockaddr *)&cliaddr, &len);
108 15 : if (connfd == -1) {
109 0 : LOG("Client connection failed...\n");
110 0 : return 0;
111 : }
112 :
113 15 : LOG("Client connected...\n");
114 15 : return connfd != -1;
115 : }
116 :
117 13 : bool io_init(int port, const char *host) {
118 13 : connfd = -1;
119 13 : serverfd = start_server(port, host);
120 13 : return (serverfd != -1);
121 : }
122 :
123 13 : void io_finalise() {
124 13 : close_and_reset_fd(&connfd);
125 13 : close_and_reset_fd(&serverfd);
126 13 : }
127 :
128 : #define CHECK_READ_STATUS(read_result, err_prefix) \
129 : { \
130 : if ((read_result) == 0) { \
131 : LOG("%s: connection closed by the client\n", err_prefix); \
132 : close_and_reset_fd(&connfd); \
133 : goto end_readloop; \
134 : } else if ((read_result) == -1) { \
135 : LOG("%s: error reading from socket. Disconnected\n", err_prefix); \
136 : close_and_reset_fd(&connfd); \
137 : goto end_readloop; \
138 : } \
139 : }
140 :
141 25 : unsigned short io_exchange(unsigned short tx) {
142 : enum {
143 : READ_LENGTH,
144 : READ_PLOAD,
145 : SKIP_PLOAD,
146 : } read_state;
147 :
148 2 : while (true) {
149 27 : if (connfd == -1) {
150 15 : if (!accept_connection()) {
151 0 : LOG("Error accepting client connection\n");
152 25 : return 0;
153 : }
154 15 : tx = 0;
155 : }
156 :
157 : // Write len (Compatibility with LedgerBlue commTCP.py)
158 27 : if (tx > 0) {
159 : // Write APDU length minus two bytes of the sw
160 : // (encoded in 4 bytes network byte-order)
161 : // (compatibility with LedgerBlue commTCP.py)
162 11 : uint32_t tx_net = tx - 2;
163 11 : tx_net = htonl(tx_net);
164 11 : if (send(connfd, &tx_net, sizeof(tx_net), MSG_NOSIGNAL) == -1) {
165 0 : LOG("Connection closed by the client\n");
166 0 : close_and_reset_fd(&connfd);
167 0 : continue;
168 : }
169 : // Write APDU
170 11 : if (send(connfd, io_apdu_buffer, tx, MSG_NOSIGNAL) == -1) {
171 0 : LOG("Connection closed by the client\n");
172 0 : close_and_reset_fd(&connfd);
173 0 : continue;
174 : }
175 11 : LOG_HEX("I/O =>", io_apdu_buffer, tx);
176 : }
177 :
178 : // Read from buffer until whole APDU is read or something happens
179 27 : read_state = READ_LENGTH;
180 : ssize_t readlen;
181 : uint32_t rx_net;
182 : uint8_t len_buf[sizeof(rx_net)];
183 : char discard_buffer[1024 * 1024];
184 : unsigned int rx;
185 27 : unsigned int offset = 0;
186 : while (true) {
187 46 : switch (read_state) {
188 30 : case READ_LENGTH:
189 : readlen =
190 30 : read(connfd, len_buf + offset, sizeof(len_buf) - offset);
191 30 : CHECK_READ_STATUS(readlen, "Error while reading APDU length");
192 29 : offset += readlen;
193 29 : if (offset == sizeof(len_buf)) {
194 26 : memcpy(&rx_net, len_buf, sizeof(rx_net));
195 26 : rx = ntohl(rx_net);
196 : // Empty packet?
197 26 : if (rx == 0) {
198 13 : LOG("I/O <= <EMPTY MESSAGE>\n");
199 13 : return 0;
200 13 : } else if (rx > sizeof(io_apdu_buffer) ||
201 12 : rx != (unsigned short)rx) {
202 1 : LOG("Client tried to send a message "
203 : "that was too big (%u bytes). "
204 : "Skipping payload.\n",
205 : rx);
206 : // Move onto skipping the APDU payload
207 1 : read_state = SKIP_PLOAD;
208 1 : offset = 0;
209 : } else {
210 : // Move onto reading the APDU payload
211 12 : read_state = READ_PLOAD;
212 12 : offset = 0;
213 : }
214 : }
215 16 : break;
216 15 : case READ_PLOAD:
217 15 : readlen = read(connfd, io_apdu_buffer + offset, rx - offset);
218 15 : CHECK_READ_STATUS(readlen, "Error while reading APDU payload");
219 14 : offset += readlen;
220 14 : if (offset == rx) {
221 11 : LOG("I/O <= (%u bytes)", rx);
222 11 : LOG_HEX("", io_apdu_buffer, rx);
223 11 : return (unsigned short)rx;
224 : }
225 3 : break;
226 1 : case SKIP_PLOAD:
227 1 : readlen = read(connfd, discard_buffer, sizeof(discard_buffer));
228 1 : CHECK_READ_STATUS(readlen, "Error while skipping APDU payload");
229 1 : offset += readlen;
230 1 : if (offset == rx) {
231 1 : LOG("I/O <= (%u bytes) <SKIPPED PAYLOAD>\n", rx);
232 1 : memset(io_apdu_buffer, 0, sizeof(io_apdu_buffer));
233 1 : return rx != (unsigned short)rx ? (unsigned short)0xFFFFFFFF
234 1 : : (unsigned short)rx;
235 : }
236 0 : break;
237 : }
238 : }
239 2 : end_readloop:;
240 : }
241 : }
|