source: bootcd/isolinux/syslinux-6.03/core/fs/pxe/http.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 8.0 KB
Line 
1#include <syslinux/sysappend.h>
2#include <ctype.h>
3#include <lwip/api.h>
4#include "pxe.h"
5#include "version.h"
6#include "url.h"
7#include "net.h"
8
9#define HTTP_PORT       80
10
11static bool is_tspecial(int ch)
12{
13    bool tspecial = false;
14    switch(ch) {
15    case '(':  case ')':  case '<':  case '>':  case '@':
16    case ',':  case ';':  case ':':  case '\\': case '"':
17    case '/':  case '[':  case ']':  case '?':  case '=':
18    case '{':  case '}':  case ' ':  case '\t':
19        tspecial = true;
20        break;
21    }
22    return tspecial;
23}
24
25static bool is_ctl(int ch)
26{
27    return ch < 0x20;
28}
29
30static bool is_token(int ch)
31{
32    /* Can by antying except a ctl character or a tspecial */
33    return !is_ctl(ch) && !is_tspecial(ch);
34}
35
36static bool append_ch(char *str, size_t size, size_t *pos, int ch)
37{
38    bool success = true;
39    if ((*pos + 1) >= size) {
40        *pos = 0;
41        success = false;
42    } else {
43        str[*pos] = ch;
44        str[*pos + 1] = '\0';
45        *pos += 1;
46    }
47    return success;
48}
49
50static size_t cookie_len, header_len;
51static char *cookie_buf, *header_buf;
52
53__export uint32_t SendCookies = -1UL; /* Send all cookies */
54
55static size_t http_do_bake_cookies(char *q)
56{
57    static const char uchexchar[16] = "0123456789ABCDEF";
58    int i;
59    size_t n = 0;
60    const char *p;
61    char c;
62    bool first = true;
63    uint32_t mask = SendCookies;
64
65    for (i = 0; i < SYSAPPEND_MAX; i++) {
66        if ((mask & 1) && (p = sysappend_strings[i])) {
67            if (first) {
68                if (q) {
69                    strcpy(q, "Cookie: ");
70                    q += 8;
71                }
72                n += 8;
73                first = false;
74            }
75            if (q) {
76                strcpy(q, "_Syslinux_");
77                q += 10;
78            }
79            n += 10;
80            /* Copy string up to and including '=' */
81            do {
82                c = *p++;
83                if (q)
84                    *q++ = c;
85                n++;
86            } while (c != '=');
87            while ((c = *p++)) {
88                if (c == ' ') {
89                    if (q)
90                        *q++ = '+';
91                    n++;
92                } else if (is_token(c)) {
93                    if (q)
94                        *q++ = c;
95                    n++;
96                } else {
97                    if (q) {
98                        *q++ = '%';
99                        *q++ = uchexchar[c >> 4];
100                        *q++ = uchexchar[c & 15];
101                    }
102                    n += 3;
103                }
104            }
105            if (q)
106                *q++ = ';';
107            n++;
108        }
109        mask >>= 1;
110    }
111    if (!first) {
112        if (q) {
113            *q++ = '\r';
114            *q++ = '\n';
115        }
116        n += 2;
117    }
118    if (q)
119        *q = '\0';
120   
121    return n;
122}
123
124__export void http_bake_cookies(void)
125{
126    if (cookie_buf)
127        free(cookie_buf);
128
129    cookie_len = http_do_bake_cookies(NULL);
130    cookie_buf = malloc(cookie_len+1);
131    if (!cookie_buf) {
132        cookie_len = 0;
133        return;
134    }
135
136    if (header_buf)
137        free(header_buf);
138
139    header_len = cookie_len + 6*FILENAME_MAX + 256;
140    header_buf = malloc(header_len);
141    if (!header_buf) {
142        header_len = 0;
143        return;                 /* Uh-oh... */
144    }
145
146    http_do_bake_cookies(cookie_buf);
147}
148
149static const struct pxe_conn_ops http_conn_ops = {
150    .fill_buffer        = core_tcp_fill_buffer,
151    .close              = core_tcp_close_file,
152    .readdir            = http_readdir,
153};
154
155void http_open(struct url_info *url, int flags, struct inode *inode,
156               const char **redir)
157{
158    struct pxe_pvt_inode *socket = PVT(inode);
159    int header_bytes;
160    const char *next;
161    char field_name[20];
162    char field_value[1024];
163    size_t field_name_len, field_value_len;
164    enum state {
165        st_httpver,
166        st_stcode,
167        st_skipline,
168        st_fieldfirst,
169        st_fieldname,
170        st_fieldvalue,
171        st_skip_fieldname,
172        st_skip_fieldvalue,
173        st_eoh,
174    } state;
175    static char location[FILENAME_MAX];
176    uint32_t content_length; /* same as inode->size */
177    size_t response_size;
178    int status;
179    int pos;
180    int err;
181
182    (void)flags;
183
184    if (!header_buf)
185        return;                 /* http is broken... */
186
187    /* This is a straightforward TCP connection after headers */
188    socket->ops = &http_conn_ops;
189
190    /* Reset all of the variables */
191    inode->size = content_length = -1;
192
193    /* Start the http connection */
194    err = core_tcp_open(socket);
195    if (err)
196        return;
197
198    if (!url->port)
199        url->port = HTTP_PORT;
200
201    err = core_tcp_connect(socket, url->ip, url->port);
202    if (err)
203        goto fail;
204
205    strcpy(header_buf, "GET /");
206    header_bytes = 5;
207    header_bytes += url_escape_unsafe(header_buf+5, url->path,
208                                      header_len - 5);
209    if (header_bytes >= header_len)
210        goto fail;              /* Buffer overflow */
211    header_bytes += snprintf(header_buf + header_bytes,
212                             header_len - header_bytes,
213                             " HTTP/1.0\r\n"
214                             "Host: %s\r\n"
215                             "User-Agent: Syslinux/" VERSION_STR "\r\n"
216                             "Connection: close\r\n"
217                             "%s"
218                             "\r\n",
219                             url->host, cookie_buf ? cookie_buf : "");
220    if (header_bytes >= header_len)
221        goto fail;              /* Buffer overflow */
222
223    err = core_tcp_write(socket, header_buf, header_bytes, false);
224    if (err)
225        goto fail;
226
227    /* Parse the HTTP header */
228    state = st_httpver;
229    pos = 0;
230    status = 0;
231    response_size = 0;
232    field_value_len = 0;
233    field_name_len = 0;
234
235    while (state != st_eoh) {
236        int ch = pxe_getc(inode);
237        /* Eof before I finish paring the header */
238        if (ch == -1)
239            goto fail;
240#if 0
241        printf("%c", ch);
242#endif
243        response_size++;
244        if (ch == '\r' || ch == '\0')
245            continue;
246        switch (state) {
247        case st_httpver:
248            if (ch == ' ') {
249                state = st_stcode;
250                pos = 0;
251            }
252            break;
253
254        case st_stcode:
255            if (ch < '0' || ch > '9')
256               goto fail;
257            status = (status*10) + (ch - '0');
258            if (++pos == 3)
259                state = st_skipline;
260            break;
261
262        case st_skipline:
263            if (ch == '\n')
264                state = st_fieldfirst;
265            break;
266
267        case st_fieldfirst:
268            if (ch == '\n')
269                state = st_eoh;
270            else if (isspace(ch)) {
271                /* A continuation line */
272                state = st_fieldvalue;
273                goto fieldvalue;
274            }
275            else if (is_token(ch)) {
276                /* Process the previous field before starting on the next one */
277                if (strcasecmp(field_name, "Content-Length") == 0) {
278                    next = field_value;
279                    /* Skip leading whitespace */
280                    while (isspace(*next))
281                        next++;
282                    content_length = 0;
283                    for (;(*next >= '0' && *next <= '9'); next++) {
284                        if ((content_length * 10) < content_length)
285                            break;
286                        content_length = (content_length * 10) + (*next - '0');
287                    }
288                    /* In the case of overflow or other error ignore
289                     * Content-Length.
290                     */
291                    if (*next)
292                        content_length = -1;
293                }
294                else if (strcasecmp(field_name, "Location") == 0) {
295                    next = field_value;
296                    /* Skip leading whitespace */
297                    while (isspace(*next))
298                        next++;
299                    strlcpy(location, next, sizeof location);
300                }
301                /* Start the field name and field value afress */
302                field_name_len = 1;
303                field_name[0] = ch;
304                field_name[1] = '\0';
305                field_value_len = 0;
306                field_value[0] = '\0';
307                state = st_fieldname;
308            }
309            else /* Bogus try to recover */
310                state = st_skipline;
311            break;
312
313        case st_fieldname:
314            if (ch == ':' ) {
315                state = st_fieldvalue;
316            }
317            else if (is_token(ch)) {
318                if (!append_ch(field_name, sizeof field_name, &field_name_len, ch))
319                    state = st_skip_fieldname;
320            }
321            /* Bogus cases try to recover */
322            else if (ch == '\n')
323                state = st_fieldfirst;
324            else
325                state = st_skipline;
326            break;
327
328         case st_fieldvalue:
329            if (ch == '\n')
330                state = st_fieldfirst;
331            else {
332            fieldvalue:
333                if (!append_ch(field_value, sizeof field_value, &field_value_len, ch))
334                    state = st_skip_fieldvalue;
335            }
336            break;
337
338        /* For valid fields whose names are longer than I choose to support. */
339        case st_skip_fieldname:
340            if (ch == ':')
341                state = st_skip_fieldvalue;
342            else if (is_token(ch))
343                state = st_skip_fieldname;
344            /* Bogus cases try to recover */
345            else if (ch == '\n')
346                state = st_fieldfirst;
347            else
348                state = st_skipline;
349            break;
350
351        /* For valid fields whose bodies are longer than I choose to support. */
352        case st_skip_fieldvalue:
353            if (ch == '\n')
354                state = st_fieldfirst;
355            break;
356
357        case st_eoh:
358           break; /* Should never happen */
359        }
360    }
361
362    if (state != st_eoh)
363        status = 0;
364
365    switch (status) {
366    case 200:
367        /*
368         * All OK, need to mark header data consumed and set up a file
369         * structure...
370         */
371        /* Treat the remainder of the bytes as data */
372        socket->tftp_filepos -= response_size;
373        break;
374    case 301:
375    case 302:
376    case 303:
377    case 307:
378        /* A redirect */
379        if (!location[0])
380            goto fail;
381        *redir = location;
382        goto fail;
383    default:
384        goto fail;
385        break;
386    }
387    return;
388fail:
389    inode->size = 0;
390    core_tcp_close_file(inode);
391    return;
392}
Note: See TracBrowser for help on using the repository browser.