int core_connect(nc_sock_t *ncsock) { assert(ncsock); if (ncsock->proto == NETCAT_PROTO_TCP) return ncsock->fd = core_tcp_connect(ncsock); else if (ncsock->proto == NETCAT_PROTO_UDP) return ncsock->fd = core_udp_connect(ncsock); else abort(); return -1; }
void http_open(struct url_info *url, int flags, struct inode *inode, const char **redir) { struct pxe_pvt_inode *socket = PVT(inode); int header_bytes; const char *next; char field_name[20]; char field_value[1024]; size_t field_name_len, field_value_len; enum state { st_httpver, st_stcode, st_skipline, st_fieldfirst, st_fieldname, st_fieldvalue, st_skip_fieldname, st_skip_fieldvalue, st_eoh, } state; static char location[FILENAME_MAX]; uint32_t content_length; /* same as inode->size */ size_t response_size; int status; int pos; int err; (void)flags; if (!header_buf) return; /* http is broken... */ /* This is a straightforward TCP connection after headers */ socket->ops = &http_conn_ops; /* Reset all of the variables */ inode->size = content_length = -1; /* Start the http connection */ err = core_tcp_open(socket); if (err) return; if (!url->port) url->port = HTTP_PORT; err = core_tcp_connect(socket, url->ip, url->port); if (err) goto fail; strcpy(header_buf, "GET /"); header_bytes = 5; header_bytes += url_escape_unsafe(header_buf+5, url->path, header_len - 5); if (header_bytes >= header_len) goto fail; /* Buffer overflow */ header_bytes += snprintf(header_buf + header_bytes, header_len - header_bytes, " HTTP/1.0\r\n" "Host: %s\r\n" "User-Agent: Syslinux/" VERSION_STR "\r\n" "Connection: close\r\n" "%s" "\r\n", url->host, cookie_buf ? cookie_buf : ""); if (header_bytes >= header_len) goto fail; /* Buffer overflow */ err = core_tcp_write(socket, header_buf, header_bytes, false); if (err) goto fail; /* Parse the HTTP header */ state = st_httpver; pos = 0; status = 0; response_size = 0; field_value_len = 0; field_name_len = 0; while (state != st_eoh) { int ch = pxe_getc(inode); /* Eof before I finish paring the header */ if (ch == -1) goto fail; #if 0 printf("%c", ch); #endif response_size++; if (ch == '\r' || ch == '\0') continue; switch (state) { case st_httpver: if (ch == ' ') { state = st_stcode; pos = 0; } break; case st_stcode: if (ch < '0' || ch > '9') goto fail; status = (status*10) + (ch - '0'); if (++pos == 3) state = st_skipline; break; case st_skipline: if (ch == '\n') state = st_fieldfirst; break; case st_fieldfirst: if (ch == '\n') state = st_eoh; else if (isspace(ch)) { /* A continuation line */ state = st_fieldvalue; goto fieldvalue; } else if (is_token(ch)) { /* Process the previous field before starting on the next one */ if (strcasecmp(field_name, "Content-Length") == 0) { next = field_value; /* Skip leading whitespace */ while (isspace(*next)) next++; content_length = 0; for (;(*next >= '0' && *next <= '9'); next++) { if ((content_length * 10) < content_length) break; content_length = (content_length * 10) + (*next - '0'); } /* In the case of overflow or other error ignore * Content-Length. */ if (*next) content_length = -1; } else if (strcasecmp(field_name, "Location") == 0) { next = field_value; /* Skip leading whitespace */ while (isspace(*next)) next++; strlcpy(location, next, sizeof location); } /* Start the field name and field value afress */ field_name_len = 1; field_name[0] = ch; field_name[1] = '\0'; field_value_len = 0; field_value[0] = '\0'; state = st_fieldname; } else /* Bogus try to recover */ state = st_skipline; break; case st_fieldname: if (ch == ':' ) { state = st_fieldvalue; } else if (is_token(ch)) { if (!append_ch(field_name, sizeof field_name, &field_name_len, ch)) state = st_skip_fieldname; } /* Bogus cases try to recover */ else if (ch == '\n') state = st_fieldfirst; else state = st_skipline; break; case st_fieldvalue: if (ch == '\n') state = st_fieldfirst; else { fieldvalue: if (!append_ch(field_value, sizeof field_value, &field_value_len, ch)) state = st_skip_fieldvalue; } break; /* For valid fields whose names are longer than I choose to support. */ case st_skip_fieldname: if (ch == ':') state = st_skip_fieldvalue; else if (is_token(ch)) state = st_skip_fieldname; /* Bogus cases try to recover */ else if (ch == '\n') state = st_fieldfirst; else state = st_skipline; break; /* For valid fields whose bodies are longer than I choose to support. */ case st_skip_fieldvalue: if (ch == '\n') state = st_fieldfirst; break; case st_eoh: break; /* Should never happen */ } } if (state != st_eoh) status = 0; switch (status) { case 200: /* * All OK, need to mark header data consumed and set up a file * structure... */ /* Treat the remainder of the bytes as data */ socket->tftp_filepos -= response_size; break; case 301: case 302: case 303: case 307: /* A redirect */ if (!location[0]) goto fail; *redir = location; goto fail; default: goto fail; break; } return; fail: inode->size = 0; core_tcp_close_file(inode); return; }