1 #include <syslinux/sysappend.h>
11 static bool is_tspecial(int ch
)
13 bool tspecial
= false;
15 case '(': case ')': case '<': case '>': case '@':
16 case ',': case ';': case ':': case '\\': case '"':
17 case '/': case '[': case ']': case '?': case '=':
18 case '{': case '}': case ' ': case '\t':
25 static bool is_ctl(int ch
)
30 static bool is_token(int ch
)
32 /* Can by antying except a ctl character or a tspecial */
33 return !is_ctl(ch
) && !is_tspecial(ch
);
36 static bool append_ch(char *str
, size_t size
, size_t *pos
, int ch
)
39 if ((*pos
+ 1) >= size
) {
50 static size_t cookie_len
, header_len
;
51 static char *cookie_buf
, *header_buf
;
53 __export
uint32_t SendCookies
= -1UL; /* Send all cookies */
55 static size_t http_do_bake_cookies(char *q
)
57 static const char uchexchar
[16] = "0123456789ABCDEF";
63 uint32_t mask
= SendCookies
;
65 for (i
= 0; i
< SYSAPPEND_MAX
; i
++) {
66 if ((mask
& 1) && (p
= sysappend_strings
[i
])) {
69 strcpy(q
, "Cookie: ");
76 strcpy(q
, "_Syslinux_");
80 /* Copy string up to and including '=' */
92 } else if (is_token(c
)) {
99 *q
++ = uchexchar
[c
>> 4];
100 *q
++ = uchexchar
[c
& 15];
124 __export
void http_bake_cookies(void)
129 cookie_len
= http_do_bake_cookies(NULL
);
130 cookie_buf
= malloc(cookie_len
+1);
139 header_len
= cookie_len
+ 6*FILENAME_MAX
+ 256;
140 header_buf
= malloc(header_len
);
143 return; /* Uh-oh... */
146 http_do_bake_cookies(cookie_buf
);
149 static 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
,
155 void http_open(struct url_info
*url
, int flags
, struct inode
*inode
,
158 struct pxe_pvt_inode
*socket
= PVT(inode
);
162 char field_value
[1024];
163 size_t field_name_len
, field_value_len
;
175 static char location
[FILENAME_MAX
];
176 uint32_t content_length
; /* same as inode->size */
177 size_t response_size
;
185 return; /* http is broken... */
187 /* This is a straightforward TCP connection after headers */
188 socket
->ops
= &http_conn_ops
;
190 /* Reset all of the variables */
191 inode
->size
= content_length
= -1;
193 /* Start the http connection */
194 err
= core_tcp_open(socket
);
199 url
->port
= HTTP_PORT
;
201 err
= core_tcp_connect(socket
, url
->ip
, url
->port
);
205 strcpy(header_buf
, "GET /");
207 header_bytes
+= url_escape_unsafe(header_buf
+5, url
->path
,
209 if (header_bytes
>= header_len
)
210 goto fail
; /* Buffer overflow */
211 header_bytes
+= snprintf(header_buf
+ header_bytes
,
212 header_len
- header_bytes
,
215 "User-Agent: Syslinux/" VERSION_STR
"\r\n"
216 "Connection: close\r\n"
219 url
->host
, cookie_buf
? cookie_buf
: "");
220 if (header_bytes
>= header_len
)
221 goto fail
; /* Buffer overflow */
223 err
= core_tcp_write(socket
, header_buf
, header_bytes
, false);
227 /* Parse the HTTP header */
235 while (state
!= st_eoh
) {
236 int ch
= pxe_getc(inode
);
237 /* Eof before I finish paring the header */
244 if (ch
== '\r' || ch
== '\0')
255 if (ch
< '0' || ch
> '9')
257 status
= (status
*10) + (ch
- '0');
264 state
= st_fieldfirst
;
270 else if (isspace(ch
)) {
271 /* A continuation line */
272 state
= st_fieldvalue
;
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) {
279 /* Skip leading whitespace */
280 while (isspace(*next
))
283 for (;(*next
>= '0' && *next
<= '9'); next
++) {
284 if ((content_length
* 10) < content_length
)
286 content_length
= (content_length
* 10) + (*next
- '0');
288 /* In the case of overflow or other error ignore
294 else if (strcasecmp(field_name
, "Location") == 0) {
296 /* Skip leading whitespace */
297 while (isspace(*next
))
299 strlcpy(location
, next
, sizeof location
);
301 /* Start the field name and field value afress */
304 field_name
[1] = '\0';
306 field_value
[0] = '\0';
307 state
= st_fieldname
;
309 else /* Bogus try to recover */
315 state
= st_fieldvalue
;
317 else if (is_token(ch
)) {
318 if (!append_ch(field_name
, sizeof field_name
, &field_name_len
, ch
))
319 state
= st_skip_fieldname
;
321 /* Bogus cases try to recover */
323 state
= st_fieldfirst
;
330 state
= st_fieldfirst
;
333 if (!append_ch(field_value
, sizeof field_value
, &field_value_len
, ch
))
334 state
= st_skip_fieldvalue
;
338 /* For valid fields whose names are longer than I choose to support. */
339 case st_skip_fieldname
:
341 state
= st_skip_fieldvalue
;
342 else if (is_token(ch
))
343 state
= st_skip_fieldname
;
344 /* Bogus cases try to recover */
346 state
= st_fieldfirst
;
351 /* For valid fields whose bodies are longer than I choose to support. */
352 case st_skip_fieldvalue
:
354 state
= st_fieldfirst
;
358 break; /* Should never happen */
368 * All OK, need to mark header data consumed and set up a file
371 /* Treat the remainder of the bytes as data */
372 socket
->tftp_filepos
-= response_size
;
390 core_tcp_close_file(inode
);