1 /*
2
3 Copyright (c) 2018 Martin Sustrik
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom
10 the Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included
13 in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 IN THE SOFTWARE.
22
23 */
24
25 #include <errno.h>
26 #include <stddef.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #define DILL_DISABLE_RAW_NAMES
33 #include "libdillimpl.h"
34 #include "utils.h"
35
36 dill_unique_id(dill_http_type);
37
38 static void *dill_http_hquery(struct dill_hvfs *hvfs, const void *type);
39 static void dill_http_hclose(struct dill_hvfs *hvfs);
40
41 struct dill_http_sock {
42 struct dill_hvfs hvfs;
43 /* Underlying SUFFIX socket. */
44 int u;
45 unsigned int mem : 1;
46 struct dill_suffix_storage suffix_mem;
47 struct dill_term_storage term_mem;
48 char rxbuf[1024];
49 };
50
DILL_CHECK_STORAGE(dill_http_sock,dill_http_storage)51 DILL_CHECK_STORAGE(dill_http_sock, dill_http_storage)
52
53 static void *dill_http_hquery(struct dill_hvfs *hvfs, const void *type) {
54 struct dill_http_sock *obj = (struct dill_http_sock*)hvfs;
55 if(type == dill_http_type) return obj;
56 errno = ENOTSUP;
57 return NULL;
58 }
59
dill_http_attach_mem(int s,struct dill_http_storage * mem)60 int dill_http_attach_mem(int s, struct dill_http_storage *mem) {
61 int err;
62 struct dill_http_sock *obj = (struct dill_http_sock*)mem;
63 if(dill_slow(!mem)) {err = EINVAL; goto error;}
64 /* Check whether underlying socket is a bytestream. */
65 if(dill_slow(!dill_hquery(s, dill_bsock_type))) {err = errno; goto error;}
66 /* Take the ownership of the underlying socket. */
67 s = dill_hown(s);
68 if(dill_slow(s < 0)) {err = errno; goto error;}
69 /* Wrap the underlying socket into SUFFIX and TERM protocol. */
70 s = dill_suffix_attach_mem(s, "\r\n", 2, &obj->suffix_mem);
71 if(dill_slow(s < 0)) {err = errno; goto error;}
72 s = dill_term_attach_mem(s, NULL, 0, &obj->term_mem);
73 if(dill_slow(s < 0)) {err = errno; goto error;}
74 /* Create the object. */
75 obj->hvfs.query = dill_http_hquery;
76 obj->hvfs.close = dill_http_hclose;
77 obj->u = s;
78 obj->mem = 1;
79 /* Create the handle. */
80 int h = dill_hmake(&obj->hvfs);
81 if(dill_slow(h < 0)) {err = errno; goto error;}
82 return h;
83 error:
84 if(s >= 0) dill_hclose(s);
85 errno = err;
86 return -1;
87 }
88
dill_http_attach(int s)89 int dill_http_attach(int s) {
90 int err;
91 struct dill_http_sock *obj = malloc(sizeof(struct dill_http_sock));
92 if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
93 s = dill_http_attach_mem(s, (struct dill_http_storage*)obj);
94 if(dill_slow(s < 0)) {err = errno; goto error2;}
95 obj->mem = 0;
96 return s;
97 error2:
98 free(obj);
99 error1:
100 if(s >= 0) dill_hclose(s);
101 errno = err;
102 return -1;
103 }
104
dill_http_done(int s,int64_t deadline)105 int dill_http_done(int s, int64_t deadline) {
106 struct dill_http_sock *obj = dill_hquery(s, dill_http_type);
107 if(dill_slow(!obj)) return -1;
108 return dill_term_done(obj->u, deadline);
109 }
110
dill_http_detach(int s,int64_t deadline)111 int dill_http_detach(int s, int64_t deadline) {
112 int err;
113 struct dill_http_sock *obj = dill_hquery(s, dill_http_type);
114 if(dill_slow(!obj)) return -1;
115 int u = dill_term_detach(obj->u, deadline);
116 if(dill_slow(u < 0)) {err = errno; goto error;}
117 u = dill_suffix_detach(u, deadline);
118 if(dill_slow(u < 0)) {err = errno; goto error;}
119 error:
120 if(!obj->mem) free(obj);
121 errno = err;
122 return u;
123 }
124
dill_http_sendrequest(int s,const char * command,const char * resource,int64_t deadline)125 int dill_http_sendrequest(int s, const char *command, const char *resource,
126 int64_t deadline) {
127 struct dill_http_sock *obj = dill_hquery(s, dill_http_type);
128 if(dill_slow(!obj)) return -1;
129 if (strpbrk(command, " \t\n") != NULL) {errno = EINVAL; return -1;}
130 if (strpbrk(resource, " \t\n") != NULL) {errno = EINVAL; return -1;}
131 struct dill_iolist iol[4];
132 iol[0].iol_base = (void*)command;
133 iol[0].iol_len = strlen(command);
134 iol[0].iol_next = &iol[1];
135 iol[0].iol_rsvd = 0;
136 iol[1].iol_base = (void*)" ";
137 iol[1].iol_len = 1;
138 iol[1].iol_next = &iol[2];
139 iol[1].iol_rsvd = 0;
140 iol[2].iol_base = (void*)resource;
141 iol[2].iol_len = strlen(resource);
142 iol[2].iol_next = &iol[3];
143 iol[2].iol_rsvd = 0;
144 iol[3].iol_base = (void*)" HTTP/1.1";
145 iol[3].iol_len = 9;
146 iol[3].iol_next = NULL;
147 iol[3].iol_rsvd = 0;
148 return dill_msendl(obj->u, &iol[0], &iol[3], deadline);
149 }
150
dill_http_recvrequest(int s,char * command,size_t commandlen,char * resource,size_t resourcelen,int64_t deadline)151 int dill_http_recvrequest(int s, char *command, size_t commandlen,
152 char *resource, size_t resourcelen, int64_t deadline) {
153 struct dill_http_sock *obj = dill_hquery(s, dill_http_type);
154 if(dill_slow(!obj)) return -1;
155 ssize_t sz = dill_mrecv(obj->u, obj->rxbuf, sizeof(obj->rxbuf) - 1,
156 deadline);
157 if(dill_slow(sz < 0)) return -1;
158 obj->rxbuf[sz] = 0;
159 size_t pos = 0;
160 while(obj->rxbuf[pos] == ' ') ++pos;
161 /* Command. */
162 size_t start = pos;
163 while(obj->rxbuf[pos] != 0 && obj->rxbuf[pos] != ' ') ++pos;
164 if(dill_slow(obj->rxbuf[pos] == 0)) {errno = EPROTO; return -1;}
165 if(dill_slow(pos - start > commandlen - 1)) {errno = EMSGSIZE; return -1;}
166 memcpy(command, obj->rxbuf + start, pos - start);
167 command[pos - start] = 0;
168 while(obj->rxbuf[pos] == ' ') ++pos;
169 /* Resource. */
170 start = pos;
171 while(obj->rxbuf[pos] != 0 && obj->rxbuf[pos] != ' ') ++pos;
172 if(dill_slow(obj->rxbuf[pos] == 0)) {errno = EPROTO; return -1;}
173 if(dill_slow(pos - start > resourcelen - 1)) {errno = EMSGSIZE; return -1;}
174 memcpy(resource, obj->rxbuf + start, pos - start);
175 resource[pos - start] = 0;
176 while(obj->rxbuf[pos] == ' ') ++pos;
177 /* Protocol. */
178 start = pos;
179 while(obj->rxbuf[pos] != 0 && obj->rxbuf[pos] != ' ') ++pos;
180 if(dill_slow(pos - start != 8 &&
181 memcmp(obj->rxbuf + start, "HTTP/1.1", 8) != 0)) {
182 errno = EPROTO; return -1;}
183 while(obj->rxbuf[pos] == ' ') ++pos;
184 if(dill_slow(obj->rxbuf[pos] != 0)) {errno = EPROTO; return -1;}
185 return 0;
186 }
187
dill_http_sendstatus(int s,int status,const char * reason,int64_t deadline)188 int dill_http_sendstatus(int s, int status, const char *reason, int64_t deadline) {
189 struct dill_http_sock *obj = dill_hquery(s, dill_http_type);
190 if(dill_slow(!obj)) return -1;
191 if(dill_slow(status < 100 || status > 599)) {errno = EINVAL; return -1;}
192 char buf[4];
193 buf[0] = (status / 100) + '0';
194 status %= 100;
195 buf[1] = (status / 10) + '0';
196 status %= 10;
197 buf[2] = status + '0';
198 buf[3] = ' ';
199 struct dill_iolist iol[3];
200 iol[0].iol_base = (void*)"HTTP/1.1 ";
201 iol[0].iol_len = 9;
202 iol[0].iol_next = &iol[1];
203 iol[0].iol_rsvd = 0;
204 iol[1].iol_base = buf;
205 iol[1].iol_len = 4;
206 iol[1].iol_next = &iol[2];
207 iol[1].iol_rsvd = 0;
208 iol[2].iol_base = (void*)reason;
209 iol[2].iol_len = strlen(reason);
210 iol[2].iol_next = NULL;
211 iol[2].iol_rsvd = 0;
212 return dill_msendl(obj->u, &iol[0], &iol[2], deadline);
213 }
214
dill_http_recvstatus(int s,char * reason,size_t reasonlen,int64_t deadline)215 int dill_http_recvstatus(int s, char *reason, size_t reasonlen,
216 int64_t deadline) {
217 struct dill_http_sock *obj = dill_hquery(s, dill_http_type);
218 if(dill_slow(!obj)) return -1;
219 ssize_t sz = dill_mrecv(obj->u, obj->rxbuf, sizeof(obj->rxbuf) - 1,
220 deadline);
221 if(dill_slow(sz < 0)) return -1;
222 obj->rxbuf[sz] = 0;
223 size_t pos = 0;
224 while(obj->rxbuf[pos] == ' ') ++pos;
225 /* Protocol. */
226 size_t start = pos;
227 while(obj->rxbuf[pos] != 0 && obj->rxbuf[pos] != ' ') ++pos;
228 if(dill_slow(obj->rxbuf[pos] == 0)) {errno = EPROTO; return -1;}
229 if(dill_slow(pos - start != 8 &&
230 memcmp(obj->rxbuf + start, "HTTP/1.1", 8) != 0)) {
231 errno = EPROTO; return -1;}
232 while(obj->rxbuf[pos] == ' ') ++pos;
233 /* Status code. */
234 start = pos;
235 while(obj->rxbuf[pos] != 0 && obj->rxbuf[pos] != ' ') ++pos;
236 if(dill_slow(obj->rxbuf[pos] == 0)) {errno = EPROTO; return -1;}
237 if(dill_slow(pos - start != 3)) {errno = EPROTO; return -1;}
238 if(dill_slow(obj->rxbuf[start] < '0' || obj->rxbuf[start] > '9' ||
239 obj->rxbuf[start + 1] < '0' || obj->rxbuf[start + 1] > '9' ||
240 obj->rxbuf[start + 2] < '0' || obj->rxbuf[start + 2] > '9')) {
241 errno = EPROTO; return -1;}
242 int status = (obj->rxbuf[start] - '0') * 100 +
243 (obj->rxbuf[start + 1] - '0') * 10 +
244 (obj->rxbuf[start + 2] - '0');
245 while(obj->rxbuf[pos] == ' ') ++pos;
246 /* Reason. */
247 if(sz - pos > reasonlen - 1) {errno = EMSGSIZE; return -1;}
248 memcpy(reason, obj->rxbuf + pos, sz - pos);
249 reason[sz - pos] = 0;
250 return status;
251 }
252
dill_http_sendfield(int s,const char * name,const char * value,int64_t deadline)253 int dill_http_sendfield(int s, const char *name, const char *value,
254 int64_t deadline) {
255 struct dill_http_sock *obj = dill_hquery(s, dill_http_type);
256 if(dill_slow(!obj)) return -1;
257 /* Check whether name contains only valid characters. */
258 if (strpbrk(name, "(),/:;<=>?@[\\]{}\" \t\n") != NULL) {
259 errno = EINVAL; return -1;}
260 if (strlen(value) == 0) {errno = EPROTO; return -1;}
261 struct dill_iolist iol[3];
262 iol[0].iol_base = (void*)name;
263 iol[0].iol_len = strlen(name);
264 iol[0].iol_next = &iol[1];
265 iol[0].iol_rsvd = 0;
266 iol[1].iol_base = (void*)": ";
267 iol[1].iol_len = 2;
268 iol[1].iol_next = &iol[2];
269 iol[1].iol_rsvd = 0;
270 const char *start = dill_lstrip(value, ' ');
271 const char *end = dill_rstrip(start, ' ');
272 dill_assert(start < end);
273 iol[2].iol_base = (void*)start;
274 iol[2].iol_len = end - start;
275 iol[2].iol_next = NULL;
276 iol[2].iol_rsvd = 0;
277 return dill_msendl(obj->u, &iol[0], &iol[2], deadline);
278 }
279
dill_http_recvfield(int s,char * name,size_t namelen,char * value,size_t valuelen,int64_t deadline)280 int dill_http_recvfield(int s, char *name, size_t namelen,
281 char *value, size_t valuelen, int64_t deadline) {
282 struct dill_http_sock *obj = dill_hquery(s, dill_http_type);
283 if(dill_slow(!obj)) return -1;
284 ssize_t sz = dill_mrecv(obj->u, obj->rxbuf, sizeof(obj->rxbuf) - 1,
285 deadline);
286 if(dill_slow(sz < 0)) return -1;
287 obj->rxbuf[sz] = 0;
288 size_t pos = 0;
289 while(obj->rxbuf[pos] == ' ') ++pos;
290 /* Name. */
291 size_t start = pos;
292 while(obj->rxbuf[pos] != 0 &&
293 obj->rxbuf[pos] != ' ' &&
294 obj->rxbuf[pos] != ':')
295 ++pos;
296 if(dill_slow(obj->rxbuf[pos] == 0)) {errno = EPROTO; return -1;}
297 if(dill_slow(pos - start > namelen - 1)) {errno = EMSGSIZE; return -1;}
298 memcpy(name, obj->rxbuf + start, pos - start);
299 name[pos - start] = 0;
300 while(obj->rxbuf[pos] == ' ') ++pos;
301 if(dill_slow(obj->rxbuf[pos] != ':')) {errno = EPROTO; return -1;}
302 ++pos;
303 while(obj->rxbuf[pos] == ' ') ++pos;
304 /* Value. */
305 start = pos;
306 pos = dill_rstrip(obj->rxbuf + sz, ' ') - obj->rxbuf;
307 if(dill_slow(pos - start > valuelen - 1)) {errno = EMSGSIZE; return -1;}
308 memcpy(value, obj->rxbuf + start, pos - start);
309 value[pos - start] = 0;
310 while(obj->rxbuf[pos] == ' ') ++pos;
311 if(dill_slow(obj->rxbuf[pos] != 0)) {errno = EPROTO; return -1;}
312 return 0;
313 }
314
dill_http_hclose(struct dill_hvfs * hvfs)315 static void dill_http_hclose(struct dill_hvfs *hvfs) {
316 struct dill_http_sock *obj = (struct dill_http_sock*)hvfs;
317 if(dill_fast(obj->u >= 0)) {
318 int rc = dill_hclose(obj->u);
319 dill_assert(rc == 0);
320 }
321 if(!obj->mem) free(obj);
322 }
323
324