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