1 | #include "cpp/fanos_shared.h"
|
2 |
|
3 | #include <assert.h>
|
4 | #include <errno.h>
|
5 | #include <stdarg.h> // va_list, etc.
|
6 | #include <stdio.h> // vfprintf
|
7 | #include <stdlib.h>
|
8 | #include <string.h>
|
9 | #include <sys/socket.h>
|
10 | #include <unistd.h>
|
11 |
|
12 | #define SIZEOF_FDS (sizeof(int) * FANOS_NUM_FDS)
|
13 |
|
14 | const char* kErrTooLarge = "Message too large";
|
15 | const char* kErrSolSocket = "Expected cmsg_level SOL_SOCKET";
|
16 | const char* kErrScmRights = "Expected cmsg_type SCM_RIGHTS";
|
17 | const char* kErrUnexpectedEof = "Unexpected EOF";
|
18 | const char* kErrMissingLength = "Expected netstring length";
|
19 | const char* kErrMissingColon = "Expected : after netstring length";
|
20 | const char* kErrMissingComma = "Expected ,";
|
21 |
|
22 | void fanos_send(int sock_fd, char* blob, int blob_len, const int* fds,
|
23 | struct FanosError* err) {
|
24 | char buf[10];
|
25 | // snprintf() doesn't write more than 10 bytes, INCLUDING \0
|
26 | // It the number of bytes it would have written, EXCLUDING \0
|
27 | unsigned int full_length = snprintf(buf, 10, "%d:", blob_len);
|
28 | if (full_length > sizeof(buf)) {
|
29 | err->value_err = kErrTooLarge;
|
30 | return;
|
31 | }
|
32 |
|
33 | if (write(sock_fd, buf, full_length) < 0) { // send '3:'
|
34 | err->err_code = errno;
|
35 | return;
|
36 | }
|
37 |
|
38 | // Example code adapted from 'man CMSG_LEN' on my machine. (Is this
|
39 | // portable?)
|
40 | //
|
41 | // The APUE code only sends a single FD and doesn't use CMSG_SPACE.
|
42 |
|
43 | // Set up the blob data
|
44 | struct iovec iov = {0};
|
45 | iov.iov_base = blob;
|
46 | iov.iov_len = blob_len;
|
47 |
|
48 | struct msghdr msg = {0};
|
49 | msg.msg_iov = &iov;
|
50 | msg.msg_iovlen = 1;
|
51 |
|
52 | // This stack buffer has to live until the sendmsg() call!
|
53 | union {
|
54 | /* ancillary data buffer, wrapped in a union in order to ensure
|
55 | it is suitably aligned */
|
56 | char buf[CMSG_SPACE(SIZEOF_FDS)];
|
57 | struct cmsghdr align;
|
58 | } u;
|
59 |
|
60 | // Optionally set up file descriptor data
|
61 | if (fds[0] != -1) {
|
62 | // If one FD is passed, all should be passed
|
63 | assert(fds[1] != -1);
|
64 | assert(fds[2] != -1);
|
65 |
|
66 | msg.msg_control = u.buf;
|
67 | msg.msg_controllen = sizeof u.buf;
|
68 |
|
69 | struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
70 | cmsg->cmsg_level = SOL_SOCKET;
|
71 | cmsg->cmsg_type = SCM_RIGHTS;
|
72 | cmsg->cmsg_len = CMSG_LEN(SIZEOF_FDS);
|
73 |
|
74 | int* fd_msg = (int*)CMSG_DATA(cmsg);
|
75 | memcpy(fd_msg, fds, SIZEOF_FDS);
|
76 | }
|
77 |
|
78 | int num_bytes = sendmsg(sock_fd, &msg, 0);
|
79 | if (num_bytes < 0) {
|
80 | err->err_code = errno;
|
81 | return;
|
82 | }
|
83 |
|
84 | buf[0] = ',';
|
85 | if (write(sock_fd, buf, 1) < 0) {
|
86 | err->err_code = errno;
|
87 | return;
|
88 | }
|
89 | }
|
90 |
|
91 | static int recv_fds_once(int sock_fd, int num_bytes, char* buf, int* buf_len,
|
92 | int* fd_out, struct FanosError* err) {
|
93 | // Where to put data
|
94 | struct iovec iov = {0};
|
95 | iov.iov_base = buf;
|
96 | iov.iov_len = num_bytes; // number of bytes REQUESTED
|
97 |
|
98 | struct msghdr msg = {0};
|
99 | msg.msg_iov = &iov;
|
100 | msg.msg_iovlen = 1;
|
101 |
|
102 | union {
|
103 | char control[CMSG_SPACE(SIZEOF_FDS)];
|
104 | struct cmsghdr align;
|
105 | } u;
|
106 | msg.msg_control = u.control;
|
107 | msg.msg_controllen = sizeof u.control;
|
108 |
|
109 | size_t bytes_read = recvmsg(sock_fd, &msg, 0);
|
110 | if (bytes_read < 0) {
|
111 | err->err_code = errno;
|
112 | return -1;
|
113 | }
|
114 | *buf_len = bytes_read;
|
115 |
|
116 | struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
117 | if (cmsg && cmsg->cmsg_len == CMSG_LEN(SIZEOF_FDS)) {
|
118 | if (cmsg->cmsg_level != SOL_SOCKET) {
|
119 | err->value_err = kErrSolSocket;
|
120 | return -1;
|
121 | }
|
122 | if (cmsg->cmsg_type != SCM_RIGHTS) {
|
123 | err->value_err = kErrScmRights;
|
124 | return -1;
|
125 | }
|
126 |
|
127 | int* fd_list = (int*)CMSG_DATA(cmsg);
|
128 |
|
129 | // Append the descriptors received
|
130 | fd_out[0] = fd_list[0];
|
131 | fd_out[1] = fd_list[1];
|
132 | fd_out[2] = fd_list[2];
|
133 | }
|
134 |
|
135 | return 0;
|
136 | }
|
137 |
|
138 | void fanos_recv(int sock_fd, int* fd_out, struct FanosResult* result_out,
|
139 | struct FanosError* err) {
|
140 | // Receive with netstring encoding
|
141 | char buf[10]; // up to 9 digits, then :
|
142 | char* p = buf;
|
143 | int n;
|
144 | for (int i = 0; i < 10; ++i) {
|
145 | n = read(sock_fd, p, 1);
|
146 | if (n < 0) {
|
147 | err->err_code = errno;
|
148 | return;
|
149 | }
|
150 | if (n != 1) {
|
151 | if (i == 0) {
|
152 | // read() returned 0 bytes, which means we got EOF at a message
|
153 | // boundary. This is part of the protocol and the caller should handle
|
154 | // it.
|
155 | result_out->len = FANOS_EOF;
|
156 | return;
|
157 | } else {
|
158 | err->value_err = kErrUnexpectedEof;
|
159 | return;
|
160 | }
|
161 | }
|
162 |
|
163 | if ('0' <= *p && *p <= '9') {
|
164 | ; // added to the buffer
|
165 | } else {
|
166 | break;
|
167 | }
|
168 |
|
169 | p++;
|
170 | }
|
171 | if (p == buf) {
|
172 | err->value_err = kErrMissingLength;
|
173 | return;
|
174 | }
|
175 | if (*p != ':') {
|
176 | err->value_err = kErrMissingColon;
|
177 | return;
|
178 | }
|
179 |
|
180 | *p = '\0'; // change : to NUL terminator
|
181 | int expected_bytes = atoi(buf);
|
182 |
|
183 | char* data_buf = (char*)malloc(expected_bytes + 1);
|
184 | data_buf[expected_bytes] = '\0';
|
185 |
|
186 | n = 0;
|
187 | while (n < expected_bytes) {
|
188 | int bytes_read;
|
189 | if (recv_fds_once(sock_fd, expected_bytes - n, data_buf + n, &bytes_read,
|
190 | fd_out, err) < 0) {
|
191 | return;
|
192 | }
|
193 | n += bytes_read;
|
194 | break;
|
195 | }
|
196 |
|
197 | assert(n == expected_bytes);
|
198 |
|
199 | n = read(sock_fd, buf, 1);
|
200 | if (n < 0) {
|
201 | err->err_code = errno;
|
202 | return;
|
203 | }
|
204 | if (n != 1) {
|
205 | err->value_err = kErrUnexpectedEof;
|
206 | return;
|
207 | }
|
208 | if (buf[0] != ',') {
|
209 | err->value_err = kErrMissingComma;
|
210 | return;
|
211 | }
|
212 |
|
213 | result_out->data = data_buf;
|
214 | result_out->len = expected_bytes;
|
215 | }
|