grub_size_t current_line_len;
int headers_recv;
int first_line_recv;
+ int size_recv;
grub_net_tcp_socket_t sock;
+ char *filename;
+ grub_err_t err;
+ char *errmsg;
} *http_data_t;
static grub_err_t
{
case 200:
break;
+ case 404:
+ data->err = GRUB_ERR_FILE_NOT_FOUND;
+ data->errmsg = grub_xasprintf ("file `%s' not found", data->filename);
+ return GRUB_ERR_NONE;
default:
- return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR,
- "unsupported HTTP error %d: %s",
- code, ptr);
+ data->err = GRUB_ERR_NET_UNKNOWN_ERROR;
+ data->errmsg = grub_xasprintf ("unsupported HTTP error %d: %s",
+ code, ptr);
+ return GRUB_ERR_NONE;
}
data->first_line_recv = 1;
return GRUB_ERR_NONE;
}
if (grub_memcmp (ptr, "Content-Length: ", sizeof ("Content-Length: ") - 1)
- == 0)
+ == 0 && !data->size_recv)
{
ptr += sizeof ("Content-Length: ") - 1;
data->file_size = grub_strtoull (ptr, &ptr, 10);
+ data->size_recv = 1;
return GRUB_ERR_NONE;
}
return GRUB_ERR_NONE;
http_data_t data = file->data;
if (data->sock)
- grub_net_tcp_close (data->sock);
- grub_free (data);
+ grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
if (data->current_line)
grub_free (data->current_line);
+ grub_free (data);
file->device->net->eof = 1;
}
if (!t)
{
grub_netbuff_free (nb);
- grub_net_tcp_close (data->sock);
+ grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
return grub_errno;
}
data->current_line_len = 0;
if (err)
{
- grub_net_tcp_close (data->sock);
+ grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
grub_netbuff_free (nb);
return err;
}
if (!data->current_line)
{
grub_netbuff_free (nb);
- grub_net_tcp_close (data->sock);
+ grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
return grub_errno;
}
data->current_line_len = (char *) nb->tail - ptr;
err = parse_line (data, ptr, ptr2 - ptr);
if (err)
{
- grub_net_tcp_close (data->sock);
+ grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
grub_netbuff_free (nb);
return err;
}
err = grub_netbuff_pull (nb, ptr - (char *) nb->data);
if (err)
{
- grub_net_tcp_close (data->sock);
+ grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
grub_netbuff_free (nb);
return err;
}
}
static grub_err_t
-http_open (struct grub_file *file, const char *filename)
+http_establish (struct grub_file *file, grub_off_t offset, int initial)
{
- struct grub_net_buff *nb;
- http_data_t data;
- grub_err_t err;
+ http_data_t data = file->data;
grub_uint8_t *ptr;
int i;
-
- data = grub_zalloc (sizeof (*data));
- if (!data)
- return grub_errno;
+ struct grub_net_buff *nb;
+ grub_err_t err;
nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE
+ sizeof ("GET ") - 1
- + grub_strlen (filename)
+ + grub_strlen (data->filename)
+ sizeof (" HTTP/1.1\r\nHost: ") - 1
+ grub_strlen (file->device->net->server)
+ sizeof ("\r\nUser-Agent: " PACKAGE_STRING
- "\r\n\r\n") - 1);
+ "\r\n") - 1
+ + sizeof ("Content-Range: bytes XXXXXXXXXXXXXXXXXXXX"
+ "-XXXXXXXXXXXXXXXXXXXX/"
+ "XXXXXXXXXXXXXXXXXXXX\r\n\r\n"));
if (!nb)
- {
- grub_free (data);
- return grub_errno;
- }
+ return grub_errno;
grub_netbuff_reserve (nb, GRUB_NET_TCP_RESERVE_SIZE);
ptr = nb->tail;
err = grub_netbuff_put (nb, sizeof ("GET ") - 1);
if (err)
{
- grub_free (data);
grub_netbuff_free (nb);
return err;
}
grub_memcpy (ptr, "GET ", sizeof ("GET ") - 1);
ptr = nb->tail;
- err = grub_netbuff_put (nb, grub_strlen (filename));
+
+ err = grub_netbuff_put (nb, grub_strlen (data->filename));
if (err)
{
- grub_free (data);
grub_netbuff_free (nb);
return err;
}
- grub_memcpy (ptr, filename, grub_strlen (filename));
+ grub_memcpy (ptr, data->filename, grub_strlen (data->filename));
ptr = nb->tail;
err = grub_netbuff_put (nb, sizeof (" HTTP/1.1\r\nHost: ") - 1);
if (err)
{
- grub_free (data);
grub_netbuff_free (nb);
return err;
}
err = grub_netbuff_put (nb, grub_strlen (file->device->net->server));
if (err)
{
- grub_free (data);
grub_netbuff_free (nb);
return err;
}
ptr = nb->tail;
err = grub_netbuff_put (nb,
- sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n")
+ sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n")
- 1);
if (err)
{
- grub_free (data);
grub_netbuff_free (nb);
return err;
}
- grub_memcpy (ptr, "\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n",
- sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n") - 1);
-
- file->not_easily_seekable = 1;
- file->data = data;
+ grub_memcpy (ptr, "\r\nUser-Agent: " PACKAGE_STRING "\r\n",
+ sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n") - 1);
+ if (!initial)
+ {
+ ptr = nb->tail;
+ grub_snprintf ((char *) ptr,
+ sizeof ("Content-Range: bytes XXXXXXXXXXXXXXXXXXXX-"
+ "XXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXX\r\n"
+ "\r\n"),
+ "Content-Range: bytes %" PRIuGRUB_UINT64_T "-%"
+ PRIuGRUB_UINT64_T "/%" PRIuGRUB_UINT64_T "\r\n\r\n",
+ offset, data->file_size - 1, data->file_size);
+ grub_netbuff_put (nb, grub_strlen ((char *) ptr));
+ }
+ ptr = nb->tail;
+ grub_netbuff_put (nb, 2);
+ grub_memcpy (ptr, "\r\n", 2);
data->sock = grub_net_tcp_open (file->device->net->server,
HTTP_PORT, http_receive,
file);
if (!data->sock)
{
- grub_free (data);
grub_netbuff_free (nb);
return grub_errno;
}
err = grub_net_send_tcp_packet (data->sock, nb, 1);
if (err)
{
- grub_free (data);
- grub_net_tcp_close (data->sock);
+ grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
return err;
}
if (!data->headers_recv)
{
- grub_net_tcp_close (data->sock);
+ grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
+ if (data->err)
+ {
+ char *str = data->errmsg;
+ err = grub_error (data->err, "%s", str);
+ grub_free (str);
+ return data->err;
+ }
+ return grub_error (GRUB_ERR_TIMEOUT, "timeout opening http");
+ }
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+http_seek (struct grub_file *file, grub_off_t off)
+{
+ struct http_data *old_data, *data;
+ grub_err_t err;
+ old_data = file->data;
+ /* FIXME: Reuse socket? */
+ grub_net_tcp_close (old_data->sock, GRUB_NET_TCP_ABORT);
+
+ while (file->device->net->packs.first)
+ grub_net_remove_packet (file->device->net->packs.first);
+
+ data = grub_zalloc (sizeof (*data));
+ if (!data)
+ return grub_errno;
+
+ data->file_size = old_data->file_size;
+ data->size_recv = 1;
+ data->filename = old_data->filename;
+ if (!data->filename)
+ {
grub_free (data);
- return grub_error (GRUB_ERR_TIMEOUT, "Time out opening http.");
+ return grub_errno;
+ }
+ grub_free (old_data);
+
+ err = http_establish (file, off, 0);
+ if (err)
+ {
+ grub_free (data->filename);
+ grub_free (data);
+ return err;
+ }
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+http_open (struct grub_file *file, const char *filename)
+{
+ grub_err_t err;
+ struct http_data *data;
+
+ data = grub_zalloc (sizeof (*data));
+ if (!data)
+ return grub_errno;
+
+ data->filename = grub_strdup (filename);
+ if (!data->filename)
+ {
+ grub_free (data);
+ return grub_errno;
+ }
+
+ file->not_easily_seekable = 0;
+ file->data = data;
+
+ err = http_establish (file, 0, 1);
+ if (err)
+ {
+ grub_free (data->filename);
+ grub_free (data);
+ return err;
}
file->size = data->file_size;
http_data_t data = file->data;
if (data->sock)
- grub_net_tcp_close (data->sock);
- grub_free (data);
+ grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
if (data->current_line)
grub_free (data->current_line);
+ grub_free (data);
return GRUB_ERR_NONE;
}
{
.name = "http",
.open = http_open,
- .close = http_close
+ .close = http_close,
+ .seek = http_seek
};
GRUB_MOD_INIT (http)
static grub_ssize_t
grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len)
{
- grub_net_t sock = file->device->net;
+ grub_net_t net = file->device->net;
struct grub_net_buff *nb;
char *ptr = buf;
grub_size_t amount, total = 0;
int try = 0;
while (try <= 3)
{
- while (sock->packs.first)
+ while (net->packs.first)
{
try = 0;
- nb = sock->packs.first->nb;
+ nb = net->packs.first->nb;
amount = nb->tail - nb->data;
if (amount > len)
amount = len;
if (amount == (grub_size_t) (nb->tail - nb->data))
{
grub_netbuff_free (nb);
- grub_net_remove_packet (sock->packs.first);
+ grub_net_remove_packet (net->packs.first);
}
else
nb->data += amount;
if (!len)
return total;
}
- if (!sock->eof)
+ if (!net->eof)
{
try++;
grub_net_poll_cards (200);
else
return total;
}
- return total;
+ grub_error (GRUB_ERR_TIMEOUT, "timeout reading '%s'", net->name);
+ return -1;
+}
+
+static grub_off_t
+have_ahead (struct grub_file *file)
+{
+ grub_net_t net = file->device->net;
+ grub_off_t ret = net->offset;
+ struct grub_net_packet *pack;
+ for (pack = net->packs.first; pack; pack = pack->next)
+ ret += pack->nb->tail - pack->nb->data;
+ return ret;
}
static grub_err_t
grub_net_seek_real (struct grub_file *file, grub_off_t offset)
{
- grub_size_t len = offset - file->device->net->offset;
-
- if (!len)
+ if (offset == file->device->net->offset)
return GRUB_ERR_NONE;
- if (file->device->net->offset > offset)
+ if (offset > file->device->net->offset)
{
- grub_err_t err;
- while (file->device->net->packs.first)
+ if (!file->device->net->protocol->seek || have_ahead (file) >= offset)
{
- grub_netbuff_free (file->device->net->packs.first->nb);
- grub_net_remove_packet (file->device->net->packs.first);
+ grub_net_fs_read_real (file, NULL,
+ offset - file->device->net->offset);
+ return grub_errno;
}
- file->device->net->protocol->close (file);
-
- file->device->net->packs.first = NULL;
- file->device->net->packs.last = NULL;
- file->device->net->offset = 0;
- file->device->net->eof = 0;
- err = file->device->net->protocol->open (file, file->device->net->name);
- if (err)
- return err;
- len = offset;
+ return file->device->net->protocol->seek (file, offset);
}
- grub_net_fs_read_real (file, NULL, len);
- return GRUB_ERR_NONE;
+ {
+ grub_err_t err;
+ if (file->device->net->protocol->seek)
+ return file->device->net->protocol->seek (file, offset);
+ while (file->device->net->packs.first)
+ {
+ grub_netbuff_free (file->device->net->packs.first->nb);
+ grub_net_remove_packet (file->device->net->packs.first);
+ }
+ file->device->net->protocol->close (file);
+
+ file->device->net->packs.first = NULL;
+ file->device->net->packs.last = NULL;
+ file->device->net->offset = 0;
+ file->device->net->eof = 0;
+ err = file->device->net->protocol->open (file, file->device->net->name);
+ if (err)
+ return err;
+ grub_net_fs_read_real (file, NULL, offset);
+ return grub_errno;
+ }
}
static grub_ssize_t
int in_port;
int out_port;
int errors;
- int reseted;
+ int they_reseted;
+ int i_reseted;
grub_uint32_t my_start_seq;
grub_uint32_t my_cur_seq;
grub_uint32_t their_start_seq;
{
struct unacked *unack, *next;
- if (sock->established && sock->error_hook)
+ if (sock->error_hook)
sock->error_hook (sock, sock->hook_data);
for (unack = sock->unack_first; unack; unack = next)
}
void
-grub_net_tcp_close (grub_net_tcp_socket_t sock)
+grub_net_tcp_close (grub_net_tcp_socket_t sock,
+ int discard_received)
{
struct grub_net_buff *nb_fin;
struct tcphdr *tcph_fin;
sock->i_closed = 1;
+ if (discard_received != GRUB_NET_TCP_CONTINUE_RECEIVING)
+ sock->recv_hook = NULL;
+
nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin)
+ GRUB_NET_OUR_IPV4_HEADER_SIZE
+ GRUB_NET_MAX_LINK_HEADER_SIZE);
tcph_fin->window = grub_cpu_to_be16 (0);
tcph_fin->urgent = 0;
err = tcp_send (nb_fin, sock);
+ if (discard_received == GRUB_NET_TCP_ABORT)
+ sock->i_reseted = 1;
if (err)
{
grub_netbuff_free (nb_fin);
}
static void
-ack (grub_net_tcp_socket_t sock)
+ack_real (grub_net_tcp_socket_t sock, int res)
{
struct grub_net_buff *nb_ack;
struct tcphdr *tcph_ack;
return;
}
tcph_ack = (void *) nb_ack->data;
- tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq);
- tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_ACK);
- tcph_ack->window = grub_cpu_to_be16 (sock->my_window);
+ if (res)
+ {
+ tcph_ack->ack = grub_cpu_to_be32 (0);
+ tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_RST);
+ tcph_ack->window = grub_cpu_to_be16 (0);
+ }
+ else
+ {
+ tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq);
+ tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_ACK);
+ tcph_ack->window = grub_cpu_to_be16 (sock->my_window);
+ }
tcph_ack->urgent = 0;
tcph_ack->src = grub_cpu_to_be16 (sock->in_port);
tcph_ack->dst = grub_cpu_to_be16 (sock->out_port);
}
}
+static void
+ack (grub_net_tcp_socket_t sock)
+{
+ ack_real (sock, 0);
+}
+
+static void
+reset (grub_net_tcp_socket_t sock)
+{
+ ack_real (sock, 1);
+}
+
void
grub_net_tcp_retransmit (void)
{
tcph->flags = grub_cpu_to_be16 ((5 << 12) | TCP_SYN | TCP_ACK);
tcph->window = grub_cpu_to_be16 (sock->my_window);
tcph->urgent = 0;
+ sock->established = 1;
tcp_socket_register (sock);
err = tcp_send (nb_ack, sock);
if (err)
{
grub_list_remove (GRUB_AS_LIST_P (&tcp_sockets),
GRUB_AS_LIST (socket));
- if (socket->reseted)
+ if (socket->they_reseted)
grub_error (GRUB_ERR_NET_PORT_CLOSED, "port closed");
else
grub_error (GRUB_ERR_NET_NO_ANSWER, "no answer");
if (grub_be_to_cpu16 (tcph->flags) & TCP_RST)
{
- sock->reseted = 1;
+ sock->they_reseted = 1;
error (sock);
grub_free (unack);
}
sock->unack_first = unack;
- if (!sock->unack_last)
+ if (!sock->unack_first)
sock->unack_last = NULL;
}
grub_netbuff_free (nb);
return GRUB_ERR_NONE;
}
+ if (sock->i_reseted)
+ {
+ reset (sock);
+ grub_netbuff_free (nb);
+ return GRUB_ERR_NONE;
+ }
err = grub_priority_queue_push (sock->pq, &nb);
if (err)