haproxy/src/channel.c

508 lines
14 KiB
C
Raw Normal View History

/*
* Channel management functions.
*
* Copyright 2000-2014 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <haproxy/api.h>
#include <haproxy/buf.h>
#include <haproxy/channel.h>
CLEANUP: channel: use "channel" instead of "buffer" in function names This is a massive rename of most functions which should make use of the word "channel" instead of the word "buffer" in their names. In concerns the following ones (new names) : unsigned long long channel_forward(struct channel *buf, unsigned long long bytes); static inline void channel_init(struct channel *buf) static inline int channel_input_closed(struct channel *buf) static inline int channel_output_closed(struct channel *buf) static inline void channel_check_timeouts(struct channel *b) static inline void channel_erase(struct channel *buf) static inline void channel_shutr_now(struct channel *buf) static inline void channel_shutw_now(struct channel *buf) static inline void channel_abort(struct channel *buf) static inline void channel_stop_hijacker(struct channel *buf) static inline void channel_auto_connect(struct channel *buf) static inline void channel_dont_connect(struct channel *buf) static inline void channel_auto_close(struct channel *buf) static inline void channel_dont_close(struct channel *buf) static inline void channel_auto_read(struct channel *buf) static inline void channel_dont_read(struct channel *buf) unsigned long long channel_forward(struct channel *buf, unsigned long long bytes) Some functions provided by channel.[ch] have kept their "buffer" name because they are really designed to act on the buffer according to some information gathered from the channel. They have been moved together to the same place in the file for better readability but they were not changed at all. The "buffer" memory pool was also renamed "channel".
2012-08-27 22:06:31 +00:00
/* Schedule up to <bytes> more bytes to be forwarded via the channel without
* notifying the owner task. Any data pending in the buffer are scheduled to be
BUG/MEDIUM: channel: fix inconsistent handling of 4GB-1 transfers In 1.4-dev3, commit 31971e5 ("[MEDIUM] add support for infinite forwarding") made it possible to configure the lower layer to forward data indefinitely by setting the forward size to CHN_INFINITE_FORWARD (4GB-1). By then larger chunk sizes were not supported so there was no confusion in the usage of the function. Since 1.5 we support 64-bit content-lengths and chunk sizes and the function has grown to support 64-bit arguments, though it still limits a single pass to 32-bit quantities (what fit in the channel's to_forward field). The issue now becomes that a 4GB-1 content-length can be confused with infinite forwarding (in fact it's 4GB-1+what was already in the buffer). It causes a visible effect when transferring this exact size because the transfer rate is lower than with other sizes due in part to the disabling of the Nagle algorithm on the sendto() call. In theory with keep-alive it should prevent a second request from being processed after such a transfer, but since the analysers are still present, the forwarding analyser properly counts down the remaining size to transfer and ultimately the transaction gets correctly reset so there is no visible effect. Since the root cause of the issue is an API problem (lack of distinction between a real valid length and a magic value), this patch modifies the API to have a new dedicated function called channel_forward_forever() to program a permanent forwarding. The existing function __channel_forward() was modified to properly take care of the requested sizes and ensure it 1) never overflows and 2) never reaches CHN_INFINITE_FORWARD by accident. It is worth noting that the function used to have a bug causing a 2GB forward to be scheduled if it was called with less data than what is present in buf->i. Fortunately this bug couldn't be triggered with existing code. This fix should be backported to 1.6 and 1.5. While it also theorically affects 1.4, it's better not to backport it there, as the risk of breaking large object transfers due to significant API differences is high, compared to the fact that the largest supported objects (4GB-1) are just slower to transfer.
2016-05-04 12:05:58 +00:00
* sent as well, within the limit of the number of bytes to forward. This must
* be the only method to use to schedule bytes to be forwarded. If the requested
CLEANUP: channel: use "channel" instead of "buffer" in function names This is a massive rename of most functions which should make use of the word "channel" instead of the word "buffer" in their names. In concerns the following ones (new names) : unsigned long long channel_forward(struct channel *buf, unsigned long long bytes); static inline void channel_init(struct channel *buf) static inline int channel_input_closed(struct channel *buf) static inline int channel_output_closed(struct channel *buf) static inline void channel_check_timeouts(struct channel *b) static inline void channel_erase(struct channel *buf) static inline void channel_shutr_now(struct channel *buf) static inline void channel_shutw_now(struct channel *buf) static inline void channel_abort(struct channel *buf) static inline void channel_stop_hijacker(struct channel *buf) static inline void channel_auto_connect(struct channel *buf) static inline void channel_dont_connect(struct channel *buf) static inline void channel_auto_close(struct channel *buf) static inline void channel_dont_close(struct channel *buf) static inline void channel_auto_read(struct channel *buf) static inline void channel_dont_read(struct channel *buf) unsigned long long channel_forward(struct channel *buf, unsigned long long bytes) Some functions provided by channel.[ch] have kept their "buffer" name because they are really designed to act on the buffer according to some information gathered from the channel. They have been moved together to the same place in the file for better readability but they were not changed at all. The "buffer" memory pool was also renamed "channel".
2012-08-27 22:06:31 +00:00
* number is too large, it is automatically adjusted. The number of bytes taken
* into account is returned. Directly touching ->to_forward will cause lockups
* when buf->o goes down to zero if nobody is ready to push the remaining data.
*/
unsigned long long __channel_forward(struct channel *chn, unsigned long long bytes)
{
BUG/MEDIUM: channel: fix inconsistent handling of 4GB-1 transfers In 1.4-dev3, commit 31971e5 ("[MEDIUM] add support for infinite forwarding") made it possible to configure the lower layer to forward data indefinitely by setting the forward size to CHN_INFINITE_FORWARD (4GB-1). By then larger chunk sizes were not supported so there was no confusion in the usage of the function. Since 1.5 we support 64-bit content-lengths and chunk sizes and the function has grown to support 64-bit arguments, though it still limits a single pass to 32-bit quantities (what fit in the channel's to_forward field). The issue now becomes that a 4GB-1 content-length can be confused with infinite forwarding (in fact it's 4GB-1+what was already in the buffer). It causes a visible effect when transferring this exact size because the transfer rate is lower than with other sizes due in part to the disabling of the Nagle algorithm on the sendto() call. In theory with keep-alive it should prevent a second request from being processed after such a transfer, but since the analysers are still present, the forwarding analyser properly counts down the remaining size to transfer and ultimately the transaction gets correctly reset so there is no visible effect. Since the root cause of the issue is an API problem (lack of distinction between a real valid length and a magic value), this patch modifies the API to have a new dedicated function called channel_forward_forever() to program a permanent forwarding. The existing function __channel_forward() was modified to properly take care of the requested sizes and ensure it 1) never overflows and 2) never reaches CHN_INFINITE_FORWARD by accident. It is worth noting that the function used to have a bug causing a 2GB forward to be scheduled if it was called with less data than what is present in buf->i. Fortunately this bug couldn't be triggered with existing code. This fix should be backported to 1.6 and 1.5. While it also theorically affects 1.4, it's better not to backport it there, as the risk of breaking large object transfers due to significant API differences is high, compared to the fact that the largest supported objects (4GB-1) are just slower to transfer.
2016-05-04 12:05:58 +00:00
unsigned int budget;
unsigned int forwarded;
BUG/MEDIUM: channel: fix inconsistent handling of 4GB-1 transfers In 1.4-dev3, commit 31971e5 ("[MEDIUM] add support for infinite forwarding") made it possible to configure the lower layer to forward data indefinitely by setting the forward size to CHN_INFINITE_FORWARD (4GB-1). By then larger chunk sizes were not supported so there was no confusion in the usage of the function. Since 1.5 we support 64-bit content-lengths and chunk sizes and the function has grown to support 64-bit arguments, though it still limits a single pass to 32-bit quantities (what fit in the channel's to_forward field). The issue now becomes that a 4GB-1 content-length can be confused with infinite forwarding (in fact it's 4GB-1+what was already in the buffer). It causes a visible effect when transferring this exact size because the transfer rate is lower than with other sizes due in part to the disabling of the Nagle algorithm on the sendto() call. In theory with keep-alive it should prevent a second request from being processed after such a transfer, but since the analysers are still present, the forwarding analyser properly counts down the remaining size to transfer and ultimately the transaction gets correctly reset so there is no visible effect. Since the root cause of the issue is an API problem (lack of distinction between a real valid length and a magic value), this patch modifies the API to have a new dedicated function called channel_forward_forever() to program a permanent forwarding. The existing function __channel_forward() was modified to properly take care of the requested sizes and ensure it 1) never overflows and 2) never reaches CHN_INFINITE_FORWARD by accident. It is worth noting that the function used to have a bug causing a 2GB forward to be scheduled if it was called with less data than what is present in buf->i. Fortunately this bug couldn't be triggered with existing code. This fix should be backported to 1.6 and 1.5. While it also theorically affects 1.4, it's better not to backport it there, as the risk of breaking large object transfers due to significant API differences is high, compared to the fact that the largest supported objects (4GB-1) are just slower to transfer.
2016-05-04 12:05:58 +00:00
/* This is more of a safety measure as it's not supposed to happen in
* regular code paths.
*/
BUG/MEDIUM: channel: fix inconsistent handling of 4GB-1 transfers In 1.4-dev3, commit 31971e5 ("[MEDIUM] add support for infinite forwarding") made it possible to configure the lower layer to forward data indefinitely by setting the forward size to CHN_INFINITE_FORWARD (4GB-1). By then larger chunk sizes were not supported so there was no confusion in the usage of the function. Since 1.5 we support 64-bit content-lengths and chunk sizes and the function has grown to support 64-bit arguments, though it still limits a single pass to 32-bit quantities (what fit in the channel's to_forward field). The issue now becomes that a 4GB-1 content-length can be confused with infinite forwarding (in fact it's 4GB-1+what was already in the buffer). It causes a visible effect when transferring this exact size because the transfer rate is lower than with other sizes due in part to the disabling of the Nagle algorithm on the sendto() call. In theory with keep-alive it should prevent a second request from being processed after such a transfer, but since the analysers are still present, the forwarding analyser properly counts down the remaining size to transfer and ultimately the transaction gets correctly reset so there is no visible effect. Since the root cause of the issue is an API problem (lack of distinction between a real valid length and a magic value), this patch modifies the API to have a new dedicated function called channel_forward_forever() to program a permanent forwarding. The existing function __channel_forward() was modified to properly take care of the requested sizes and ensure it 1) never overflows and 2) never reaches CHN_INFINITE_FORWARD by accident. It is worth noting that the function used to have a bug causing a 2GB forward to be scheduled if it was called with less data than what is present in buf->i. Fortunately this bug couldn't be triggered with existing code. This fix should be backported to 1.6 and 1.5. While it also theorically affects 1.4, it's better not to backport it there, as the risk of breaking large object transfers due to significant API differences is high, compared to the fact that the largest supported objects (4GB-1) are just slower to transfer.
2016-05-04 12:05:58 +00:00
if (unlikely(chn->to_forward == CHN_INFINITE_FORWARD)) {
c_adv(chn, ci_data(chn));
return bytes;
}
BUG/MEDIUM: channel: fix inconsistent handling of 4GB-1 transfers In 1.4-dev3, commit 31971e5 ("[MEDIUM] add support for infinite forwarding") made it possible to configure the lower layer to forward data indefinitely by setting the forward size to CHN_INFINITE_FORWARD (4GB-1). By then larger chunk sizes were not supported so there was no confusion in the usage of the function. Since 1.5 we support 64-bit content-lengths and chunk sizes and the function has grown to support 64-bit arguments, though it still limits a single pass to 32-bit quantities (what fit in the channel's to_forward field). The issue now becomes that a 4GB-1 content-length can be confused with infinite forwarding (in fact it's 4GB-1+what was already in the buffer). It causes a visible effect when transferring this exact size because the transfer rate is lower than with other sizes due in part to the disabling of the Nagle algorithm on the sendto() call. In theory with keep-alive it should prevent a second request from being processed after such a transfer, but since the analysers are still present, the forwarding analyser properly counts down the remaining size to transfer and ultimately the transaction gets correctly reset so there is no visible effect. Since the root cause of the issue is an API problem (lack of distinction between a real valid length and a magic value), this patch modifies the API to have a new dedicated function called channel_forward_forever() to program a permanent forwarding. The existing function __channel_forward() was modified to properly take care of the requested sizes and ensure it 1) never overflows and 2) never reaches CHN_INFINITE_FORWARD by accident. It is worth noting that the function used to have a bug causing a 2GB forward to be scheduled if it was called with less data than what is present in buf->i. Fortunately this bug couldn't be triggered with existing code. This fix should be backported to 1.6 and 1.5. While it also theorically affects 1.4, it's better not to backport it there, as the risk of breaking large object transfers due to significant API differences is high, compared to the fact that the largest supported objects (4GB-1) are just slower to transfer.
2016-05-04 12:05:58 +00:00
/* Bound the transferred size to a 32-bit count since all our values
* are 32-bit, and we don't want to reach CHN_INFINITE_FORWARD.
*/
budget = MIN(bytes, CHN_INFINITE_FORWARD - 1);
BUG/MEDIUM: channel: fix inconsistent handling of 4GB-1 transfers In 1.4-dev3, commit 31971e5 ("[MEDIUM] add support for infinite forwarding") made it possible to configure the lower layer to forward data indefinitely by setting the forward size to CHN_INFINITE_FORWARD (4GB-1). By then larger chunk sizes were not supported so there was no confusion in the usage of the function. Since 1.5 we support 64-bit content-lengths and chunk sizes and the function has grown to support 64-bit arguments, though it still limits a single pass to 32-bit quantities (what fit in the channel's to_forward field). The issue now becomes that a 4GB-1 content-length can be confused with infinite forwarding (in fact it's 4GB-1+what was already in the buffer). It causes a visible effect when transferring this exact size because the transfer rate is lower than with other sizes due in part to the disabling of the Nagle algorithm on the sendto() call. In theory with keep-alive it should prevent a second request from being processed after such a transfer, but since the analysers are still present, the forwarding analyser properly counts down the remaining size to transfer and ultimately the transaction gets correctly reset so there is no visible effect. Since the root cause of the issue is an API problem (lack of distinction between a real valid length and a magic value), this patch modifies the API to have a new dedicated function called channel_forward_forever() to program a permanent forwarding. The existing function __channel_forward() was modified to properly take care of the requested sizes and ensure it 1) never overflows and 2) never reaches CHN_INFINITE_FORWARD by accident. It is worth noting that the function used to have a bug causing a 2GB forward to be scheduled if it was called with less data than what is present in buf->i. Fortunately this bug couldn't be triggered with existing code. This fix should be backported to 1.6 and 1.5. While it also theorically affects 1.4, it's better not to backport it there, as the risk of breaking large object transfers due to significant API differences is high, compared to the fact that the largest supported objects (4GB-1) are just slower to transfer.
2016-05-04 12:05:58 +00:00
/* transfer as much as we can of buf->i */
forwarded = MIN(ci_data(chn), budget);
c_adv(chn, forwarded);
BUG/MEDIUM: channel: fix inconsistent handling of 4GB-1 transfers In 1.4-dev3, commit 31971e5 ("[MEDIUM] add support for infinite forwarding") made it possible to configure the lower layer to forward data indefinitely by setting the forward size to CHN_INFINITE_FORWARD (4GB-1). By then larger chunk sizes were not supported so there was no confusion in the usage of the function. Since 1.5 we support 64-bit content-lengths and chunk sizes and the function has grown to support 64-bit arguments, though it still limits a single pass to 32-bit quantities (what fit in the channel's to_forward field). The issue now becomes that a 4GB-1 content-length can be confused with infinite forwarding (in fact it's 4GB-1+what was already in the buffer). It causes a visible effect when transferring this exact size because the transfer rate is lower than with other sizes due in part to the disabling of the Nagle algorithm on the sendto() call. In theory with keep-alive it should prevent a second request from being processed after such a transfer, but since the analysers are still present, the forwarding analyser properly counts down the remaining size to transfer and ultimately the transaction gets correctly reset so there is no visible effect. Since the root cause of the issue is an API problem (lack of distinction between a real valid length and a magic value), this patch modifies the API to have a new dedicated function called channel_forward_forever() to program a permanent forwarding. The existing function __channel_forward() was modified to properly take care of the requested sizes and ensure it 1) never overflows and 2) never reaches CHN_INFINITE_FORWARD by accident. It is worth noting that the function used to have a bug causing a 2GB forward to be scheduled if it was called with less data than what is present in buf->i. Fortunately this bug couldn't be triggered with existing code. This fix should be backported to 1.6 and 1.5. While it also theorically affects 1.4, it's better not to backport it there, as the risk of breaking large object transfers due to significant API differences is high, compared to the fact that the largest supported objects (4GB-1) are just slower to transfer.
2016-05-04 12:05:58 +00:00
budget -= forwarded;
BUG/MEDIUM: channel: fix inconsistent handling of 4GB-1 transfers In 1.4-dev3, commit 31971e5 ("[MEDIUM] add support for infinite forwarding") made it possible to configure the lower layer to forward data indefinitely by setting the forward size to CHN_INFINITE_FORWARD (4GB-1). By then larger chunk sizes were not supported so there was no confusion in the usage of the function. Since 1.5 we support 64-bit content-lengths and chunk sizes and the function has grown to support 64-bit arguments, though it still limits a single pass to 32-bit quantities (what fit in the channel's to_forward field). The issue now becomes that a 4GB-1 content-length can be confused with infinite forwarding (in fact it's 4GB-1+what was already in the buffer). It causes a visible effect when transferring this exact size because the transfer rate is lower than with other sizes due in part to the disabling of the Nagle algorithm on the sendto() call. In theory with keep-alive it should prevent a second request from being processed after such a transfer, but since the analysers are still present, the forwarding analyser properly counts down the remaining size to transfer and ultimately the transaction gets correctly reset so there is no visible effect. Since the root cause of the issue is an API problem (lack of distinction between a real valid length and a magic value), this patch modifies the API to have a new dedicated function called channel_forward_forever() to program a permanent forwarding. The existing function __channel_forward() was modified to properly take care of the requested sizes and ensure it 1) never overflows and 2) never reaches CHN_INFINITE_FORWARD by accident. It is worth noting that the function used to have a bug causing a 2GB forward to be scheduled if it was called with less data than what is present in buf->i. Fortunately this bug couldn't be triggered with existing code. This fix should be backported to 1.6 and 1.5. While it also theorically affects 1.4, it's better not to backport it there, as the risk of breaking large object transfers due to significant API differences is high, compared to the fact that the largest supported objects (4GB-1) are just slower to transfer.
2016-05-04 12:05:58 +00:00
if (!budget)
return forwarded;
/* Now we must ensure chn->to_forward sats below CHN_INFINITE_FORWARD,
* which also implies it won't overflow. It's less operations in 64-bit.
*/
bytes = (unsigned long long)chn->to_forward + budget;
if (bytes >= CHN_INFINITE_FORWARD)
bytes = CHN_INFINITE_FORWARD - 1;
budget = bytes - chn->to_forward;
chn->to_forward += budget;
forwarded += budget;
return forwarded;
}
CLEANUP: channel: use "channel" instead of "buffer" in function names This is a massive rename of most functions which should make use of the word "channel" instead of the word "buffer" in their names. In concerns the following ones (new names) : unsigned long long channel_forward(struct channel *buf, unsigned long long bytes); static inline void channel_init(struct channel *buf) static inline int channel_input_closed(struct channel *buf) static inline int channel_output_closed(struct channel *buf) static inline void channel_check_timeouts(struct channel *b) static inline void channel_erase(struct channel *buf) static inline void channel_shutr_now(struct channel *buf) static inline void channel_shutw_now(struct channel *buf) static inline void channel_abort(struct channel *buf) static inline void channel_stop_hijacker(struct channel *buf) static inline void channel_auto_connect(struct channel *buf) static inline void channel_dont_connect(struct channel *buf) static inline void channel_auto_close(struct channel *buf) static inline void channel_dont_close(struct channel *buf) static inline void channel_auto_read(struct channel *buf) static inline void channel_dont_read(struct channel *buf) unsigned long long channel_forward(struct channel *buf, unsigned long long bytes) Some functions provided by channel.[ch] have kept their "buffer" name because they are really designed to act on the buffer according to some information gathered from the channel. They have been moved together to the same place in the file for better readability but they were not changed at all. The "buffer" memory pool was also renamed "channel".
2012-08-27 22:06:31 +00:00
/* writes <len> bytes from message <msg> to the channel's buffer. Returns -1 in
* case of success, -2 if the message is larger than the buffer size, or the
* number of bytes available otherwise. The send limit is automatically
* adjusted to the amount of data written. FIXME-20060521: handle unaligned
* data. Note: this function appends data to the buffer's output and possibly
* overwrites any pending input data which are assumed not to exist.
*/
int co_inject(struct channel *chn, const char *msg, int len)
{
int max;
if (len == 0)
return -1;
if (len < 0 || len > c_size(chn)) {
/* we can't write this chunk and will never be able to, because
* it is larger than the buffer. This must be reported as an
* error. Then we return -2 so that writers that don't care can
* ignore it and go on, and others can check for this value.
*/
return -2;
}
c_realign_if_empty(chn);
max = b_contig_space(&chn->buf);
if (len > max)
return max;
memcpy(co_tail(chn), msg, len);
b_add(&chn->buf, len);
c_adv(chn, len);
chn->total += len;
return -1;
}
CLEANUP: channel: use "channel" instead of "buffer" in function names This is a massive rename of most functions which should make use of the word "channel" instead of the word "buffer" in their names. In concerns the following ones (new names) : unsigned long long channel_forward(struct channel *buf, unsigned long long bytes); static inline void channel_init(struct channel *buf) static inline int channel_input_closed(struct channel *buf) static inline int channel_output_closed(struct channel *buf) static inline void channel_check_timeouts(struct channel *b) static inline void channel_erase(struct channel *buf) static inline void channel_shutr_now(struct channel *buf) static inline void channel_shutw_now(struct channel *buf) static inline void channel_abort(struct channel *buf) static inline void channel_stop_hijacker(struct channel *buf) static inline void channel_auto_connect(struct channel *buf) static inline void channel_dont_connect(struct channel *buf) static inline void channel_auto_close(struct channel *buf) static inline void channel_dont_close(struct channel *buf) static inline void channel_auto_read(struct channel *buf) static inline void channel_dont_read(struct channel *buf) unsigned long long channel_forward(struct channel *buf, unsigned long long bytes) Some functions provided by channel.[ch] have kept their "buffer" name because they are really designed to act on the buffer according to some information gathered from the channel. They have been moved together to the same place in the file for better readability but they were not changed at all. The "buffer" memory pool was also renamed "channel".
2012-08-27 22:06:31 +00:00
/* Tries to copy character <c> into the channel's buffer after some length
* controls. The chn->o and to_forward pointers are updated. If the channel
CLEANUP: channel: use "channel" instead of "buffer" in function names This is a massive rename of most functions which should make use of the word "channel" instead of the word "buffer" in their names. In concerns the following ones (new names) : unsigned long long channel_forward(struct channel *buf, unsigned long long bytes); static inline void channel_init(struct channel *buf) static inline int channel_input_closed(struct channel *buf) static inline int channel_output_closed(struct channel *buf) static inline void channel_check_timeouts(struct channel *b) static inline void channel_erase(struct channel *buf) static inline void channel_shutr_now(struct channel *buf) static inline void channel_shutw_now(struct channel *buf) static inline void channel_abort(struct channel *buf) static inline void channel_stop_hijacker(struct channel *buf) static inline void channel_auto_connect(struct channel *buf) static inline void channel_dont_connect(struct channel *buf) static inline void channel_auto_close(struct channel *buf) static inline void channel_dont_close(struct channel *buf) static inline void channel_auto_read(struct channel *buf) static inline void channel_dont_read(struct channel *buf) unsigned long long channel_forward(struct channel *buf, unsigned long long bytes) Some functions provided by channel.[ch] have kept their "buffer" name because they are really designed to act on the buffer according to some information gathered from the channel. They have been moved together to the same place in the file for better readability but they were not changed at all. The "buffer" memory pool was also renamed "channel".
2012-08-27 22:06:31 +00:00
* input is closed, -2 is returned. If there is not enough room left in the
* buffer, -1 is returned. Otherwise the number of bytes copied is returned
* (1). Channel flag READ_PARTIAL is updated if some data can be transferred.
*/
int ci_putchr(struct channel *chn, char c)
{
if (unlikely(channel_input_closed(chn)))
return -2;
if (!channel_may_recv(chn))
return -1;
*ci_tail(chn) = c;
b_add(&chn->buf, 1);
chn->flags |= CF_READ_PARTIAL;
if (chn->to_forward >= 1) {
if (chn->to_forward != CHN_INFINITE_FORWARD)
chn->to_forward--;
c_adv(chn, 1);
}
chn->total++;
return 1;
}
CLEANUP: channel: use "channel" instead of "buffer" in function names This is a massive rename of most functions which should make use of the word "channel" instead of the word "buffer" in their names. In concerns the following ones (new names) : unsigned long long channel_forward(struct channel *buf, unsigned long long bytes); static inline void channel_init(struct channel *buf) static inline int channel_input_closed(struct channel *buf) static inline int channel_output_closed(struct channel *buf) static inline void channel_check_timeouts(struct channel *b) static inline void channel_erase(struct channel *buf) static inline void channel_shutr_now(struct channel *buf) static inline void channel_shutw_now(struct channel *buf) static inline void channel_abort(struct channel *buf) static inline void channel_stop_hijacker(struct channel *buf) static inline void channel_auto_connect(struct channel *buf) static inline void channel_dont_connect(struct channel *buf) static inline void channel_auto_close(struct channel *buf) static inline void channel_dont_close(struct channel *buf) static inline void channel_auto_read(struct channel *buf) static inline void channel_dont_read(struct channel *buf) unsigned long long channel_forward(struct channel *buf, unsigned long long bytes) Some functions provided by channel.[ch] have kept their "buffer" name because they are really designed to act on the buffer according to some information gathered from the channel. They have been moved together to the same place in the file for better readability but they were not changed at all. The "buffer" memory pool was also renamed "channel".
2012-08-27 22:06:31 +00:00
/* Tries to copy block <blk> at once into the channel's buffer after length
* controls. The chn->o and to_forward pointers are updated. If the channel
CLEANUP: channel: use "channel" instead of "buffer" in function names This is a massive rename of most functions which should make use of the word "channel" instead of the word "buffer" in their names. In concerns the following ones (new names) : unsigned long long channel_forward(struct channel *buf, unsigned long long bytes); static inline void channel_init(struct channel *buf) static inline int channel_input_closed(struct channel *buf) static inline int channel_output_closed(struct channel *buf) static inline void channel_check_timeouts(struct channel *b) static inline void channel_erase(struct channel *buf) static inline void channel_shutr_now(struct channel *buf) static inline void channel_shutw_now(struct channel *buf) static inline void channel_abort(struct channel *buf) static inline void channel_stop_hijacker(struct channel *buf) static inline void channel_auto_connect(struct channel *buf) static inline void channel_dont_connect(struct channel *buf) static inline void channel_auto_close(struct channel *buf) static inline void channel_dont_close(struct channel *buf) static inline void channel_auto_read(struct channel *buf) static inline void channel_dont_read(struct channel *buf) unsigned long long channel_forward(struct channel *buf, unsigned long long bytes) Some functions provided by channel.[ch] have kept their "buffer" name because they are really designed to act on the buffer according to some information gathered from the channel. They have been moved together to the same place in the file for better readability but they were not changed at all. The "buffer" memory pool was also renamed "channel".
2012-08-27 22:06:31 +00:00
* input is closed, -2 is returned. If the block is too large for this buffer,
* -3 is returned. If there is not enough room left in the buffer, -1 is
* returned. Otherwise the number of bytes copied is returned (0 being a valid
* number). Channel flag READ_PARTIAL is updated if some data can be
* transferred.
*/
int ci_putblk(struct channel *chn, const char *blk, int len)
{
int max;
if (unlikely(channel_input_closed(chn)))
return -2;
if (len < 0)
return -3;
max = channel_recv_limit(chn);
if (unlikely(len > max - c_data(chn))) {
/* we can't write this chunk right now because the buffer is
* almost full or because the block is too large. Returns
* -3 if block is too large for this buffer. Or -1 if the
* room left is not large enough.
*/
if (len > max)
return -3;
return -1;
}
if (unlikely(len == 0))
return 0;
/* OK so the data fits in the buffer in one or two blocks */
max = b_contig_space(&chn->buf);
memcpy(ci_tail(chn), blk, MIN(len, max));
if (len > max)
memcpy(c_orig(chn), blk + max, len - max);
b_add(&chn->buf, len);
channel_add_input(chn, len);
return len;
}
/* Gets one text word out of a channel's buffer from a stream interface.
* Return values :
* >0 : number of bytes read. Includes the sep if present before len or end.
* =0 : no sep before end found. <str> is left undefined.
* <0 : no more bytes readable because output is shut.
* The channel status is not changed. The caller must call co_skip() to
* update it. The line separator is waited for as long as neither the buffer
* nor the output are full. If either of them is full, the string may be
* returned as is, without the line separator.
*/
int co_getword(const struct channel *chn, char *str, int len, char sep)
{
int ret, max;
char *p;
ret = 0;
max = len;
/* closed or empty + imminent close = -1; empty = 0 */
if (unlikely((chn->flags & CF_SHUTW) || channel_is_empty(chn))) {
if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
ret = -1;
goto out;
}
p = co_head(chn);
if (max > co_data(chn)) {
max = co_data(chn);
str[max-1] = 0;
}
while (max) {
*str++ = *p;
ret++;
max--;
if (*p == sep)
break;
p = b_next(&chn->buf, p);
}
if (ret > 0 && ret < len &&
(ret < co_data(chn) || channel_may_recv(chn)) &&
*(str-1) != sep &&
!(chn->flags & (CF_SHUTW|CF_SHUTW_NOW)))
ret = 0;
out:
if (max)
*str = 0;
return ret;
}
CLEANUP: channel: use "channel" instead of "buffer" in function names This is a massive rename of most functions which should make use of the word "channel" instead of the word "buffer" in their names. In concerns the following ones (new names) : unsigned long long channel_forward(struct channel *buf, unsigned long long bytes); static inline void channel_init(struct channel *buf) static inline int channel_input_closed(struct channel *buf) static inline int channel_output_closed(struct channel *buf) static inline void channel_check_timeouts(struct channel *b) static inline void channel_erase(struct channel *buf) static inline void channel_shutr_now(struct channel *buf) static inline void channel_shutw_now(struct channel *buf) static inline void channel_abort(struct channel *buf) static inline void channel_stop_hijacker(struct channel *buf) static inline void channel_auto_connect(struct channel *buf) static inline void channel_dont_connect(struct channel *buf) static inline void channel_auto_close(struct channel *buf) static inline void channel_dont_close(struct channel *buf) static inline void channel_auto_read(struct channel *buf) static inline void channel_dont_read(struct channel *buf) unsigned long long channel_forward(struct channel *buf, unsigned long long bytes) Some functions provided by channel.[ch] have kept their "buffer" name because they are really designed to act on the buffer according to some information gathered from the channel. They have been moved together to the same place in the file for better readability but they were not changed at all. The "buffer" memory pool was also renamed "channel".
2012-08-27 22:06:31 +00:00
/* Gets one text line out of a channel's buffer from a stream interface.
* Return values :
* >0 : number of bytes read. Includes the \n if present before len or end.
* =0 : no '\n' before end found. <str> is left undefined.
* <0 : no more bytes readable because output is shut.
* The channel status is not changed. The caller must call co_skip() to
* update it. The '\n' is waited for as long as neither the buffer nor the
* output are full. If either of them is full, the string may be returned
* as is, without the '\n'.
*/
int co_getline(const struct channel *chn, char *str, int len)
{
int ret, max;
char *p;
ret = 0;
max = len;
/* closed or empty + imminent close = -1; empty = 0 */
if (unlikely((chn->flags & CF_SHUTW) || channel_is_empty(chn))) {
if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
ret = -1;
goto out;
}
p = co_head(chn);
if (max > co_data(chn)) {
max = co_data(chn);
str[max-1] = 0;
}
while (max) {
*str++ = *p;
ret++;
max--;
if (*p == '\n')
break;
p = b_next(&chn->buf, p);
}
if (ret > 0 && ret < len &&
(ret < co_data(chn) || channel_may_recv(chn)) &&
*(str-1) != '\n' &&
!(chn->flags & (CF_SHUTW|CF_SHUTW_NOW)))
ret = 0;
out:
if (max)
*str = 0;
return ret;
}
/* Gets one char of data from a channel's buffer,
* Return values :
* 1 : number of bytes read, equal to requested size.
* =0 : not enough data available. <c> is left undefined.
* <0 : no more bytes readable because output is shut.
* The channel status is not changed. The caller must call co_skip() to
* update it.
*/
int co_getchar(const struct channel *chn, char *c)
{
if (chn->flags & CF_SHUTW)
return -1;
if (unlikely(co_data(chn) == 0)) {
if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
return -1;
return 0;
}
*c = *(co_head(chn));
return 1;
}
CLEANUP: channel: use "channel" instead of "buffer" in function names This is a massive rename of most functions which should make use of the word "channel" instead of the word "buffer" in their names. In concerns the following ones (new names) : unsigned long long channel_forward(struct channel *buf, unsigned long long bytes); static inline void channel_init(struct channel *buf) static inline int channel_input_closed(struct channel *buf) static inline int channel_output_closed(struct channel *buf) static inline void channel_check_timeouts(struct channel *b) static inline void channel_erase(struct channel *buf) static inline void channel_shutr_now(struct channel *buf) static inline void channel_shutw_now(struct channel *buf) static inline void channel_abort(struct channel *buf) static inline void channel_stop_hijacker(struct channel *buf) static inline void channel_auto_connect(struct channel *buf) static inline void channel_dont_connect(struct channel *buf) static inline void channel_auto_close(struct channel *buf) static inline void channel_dont_close(struct channel *buf) static inline void channel_auto_read(struct channel *buf) static inline void channel_dont_read(struct channel *buf) unsigned long long channel_forward(struct channel *buf, unsigned long long bytes) Some functions provided by channel.[ch] have kept their "buffer" name because they are really designed to act on the buffer according to some information gathered from the channel. They have been moved together to the same place in the file for better readability but they were not changed at all. The "buffer" memory pool was also renamed "channel".
2012-08-27 22:06:31 +00:00
/* Gets one full block of data at once from a channel's buffer, optionally from
* a specific offset. Return values :
* >0 : number of bytes read, equal to requested size.
* =0 : not enough data available. <blk> is left undefined.
* <0 : no more bytes readable because output is shut.
* The channel status is not changed. The caller must call co_skip() to
* update it.
*/
int co_getblk(const struct channel *chn, char *blk, int len, int offset)
{
if (chn->flags & CF_SHUTW)
return -1;
if (len + offset > co_data(chn)) {
if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
return -1;
return 0;
}
return b_getblk(&chn->buf, blk, len, offset);
}
/* Gets one or two blocks of data at once from a channel's output buffer.
* Return values :
* >0 : number of blocks filled (1 or 2). blk1 is always filled before blk2.
* =0 : not enough data available. <blk*> are left undefined.
* <0 : no more bytes readable because output is shut.
* The channel status is not changed. The caller must call co_skip() to
* update it. Unused buffers are left in an undefined state.
*/
int co_getblk_nc(const struct channel *chn, const char **blk1, size_t *len1, const char **blk2, size_t *len2)
{
if (unlikely(co_data(chn) == 0)) {
if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
return -1;
return 0;
}
return b_getblk_nc(&chn->buf, blk1, len1, blk2, len2, 0, co_data(chn));
}
/* Gets one text line out of a channel's output buffer from a stream interface.
* Return values :
* >0 : number of blocks returned (1 or 2). blk1 is always filled before blk2.
* =0 : not enough data available.
* <0 : no more bytes readable because output is shut.
* The '\n' is waited for as long as neither the buffer nor the output are
* full. If either of them is full, the string may be returned as is, without
* the '\n'. Unused buffers are left in an undefined state.
*/
int co_getline_nc(const struct channel *chn,
const char **blk1, size_t *len1,
const char **blk2, size_t *len2)
{
int retcode;
int l;
retcode = co_getblk_nc(chn, blk1, len1, blk2, len2);
BUG/MAJOR: channel: Fix crash when trying to read from a closed socket When haproxy is compiled using GCC <= 3.x or >= 5.x the `unlikely` macro performs a comparison with zero: `(x) != 0`, thus returning either 0 or 1. In `int co_getline_nc()` this macro was accidentally applied to the variable `retcode` itself, instead of the result of the comparison `retcode <= 0`. As a result any negative `retcode` is converted to `1` for purposes of the comparison. Thus never taking the branch (and exiting the function) for negative values. This in turn leads to reads of uninitialized memory in the for-loop below: ==12141== Conditional jump or move depends on uninitialised value(s) ==12141== at 0x4EB6B4: co_getline_nc (channel.c:346) ==12141== by 0x421CA4: hlua_socket_receive_yield (hlua.c:1713) ==12141== by 0x421F6F: hlua_socket_receive (hlua.c:1896) ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529B497: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529711A: lua_pcallk (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x52ABDF0: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529A9F1: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529B523: lua_resume (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== ==12141== Use of uninitialised value of size 8 ==12141== at 0x4EB6B9: co_getline_nc (channel.c:346) ==12141== by 0x421CA4: hlua_socket_receive_yield (hlua.c:1713) ==12141== by 0x421F6F: hlua_socket_receive (hlua.c:1896) ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529B497: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529711A: lua_pcallk (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x52ABDF0: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529A9F1: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529B523: lua_resume (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== ==12141== Invalid read of size 1 ==12141== at 0x4EB6B9: co_getline_nc (channel.c:346) ==12141== by 0x421CA4: hlua_socket_receive_yield (hlua.c:1713) ==12141== by 0x421F6F: hlua_socket_receive (hlua.c:1896) ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529B497: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529711A: lua_pcallk (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x52ABDF0: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529A9F1: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== by 0x529B523: lua_resume (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) ==12141== Address 0x8637171e928bb500 is not stack'd, malloc'd or (recently) free'd Fix this bug by correctly applying the `unlikely` macro to the result of the comparison. This bug exists as of commit ca16b038132444dea06e6d83953034128a812bce which is the first commit adding this function. v1.6-dev1 is the first tag containing this commit, the fix should be backported to haproxy 1.6 and newer.
2018-04-24 17:20:43 +00:00
if (unlikely(retcode <= 0))
return retcode;
for (l = 0; l < *len1 && (*blk1)[l] != '\n'; l++);
if (l < *len1 && (*blk1)[l] == '\n') {
*len1 = l + 1;
return 1;
}
if (retcode >= 2) {
for (l = 0; l < *len2 && (*blk2)[l] != '\n'; l++);
if (l < *len2 && (*blk2)[l] == '\n') {
*len2 = l + 1;
return 2;
}
}
if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
/* If we have found no LF and the buffer is shut, then
* the resulting string is made of the concatenation of
* the pending blocks (1 or 2).
*/
return retcode;
}
/* No LF yet and not shut yet */
return 0;
}
/* Gets one full block of data at once from a channel's input buffer.
* This function can return the data slitted in one or two blocks.
* Return values :
* >0 : number of blocks returned (1 or 2). blk1 is always filled before blk2.
* =0 : not enough data available.
* <0 : no more bytes readable because input is shut.
*/
int ci_getblk_nc(const struct channel *chn,
char **blk1, size_t *len1,
char **blk2, size_t *len2)
{
if (unlikely(ci_data(chn) == 0)) {
if (chn->flags & CF_SHUTR)
return -1;
return 0;
}
if (unlikely(ci_head(chn) + ci_data(chn) > c_wrap(chn))) {
*blk1 = ci_head(chn);
*len1 = c_wrap(chn) - ci_head(chn);
*blk2 = c_orig(chn);
*len2 = ci_data(chn) - *len1;
return 2;
}
*blk1 = ci_head(chn);
*len1 = ci_data(chn);
return 1;
}
/* Gets one text line out of a channel's input buffer from a stream interface.
* Return values :
* >0 : number of blocks returned (1 or 2). blk1 is always filled before blk2.
* =0 : not enough data available.
* <0 : no more bytes readable because output is shut.
* The '\n' is waited for as long as neither the buffer nor the input are
* full. If either of them is full, the string may be returned as is, without
* the '\n'. Unused buffers are left in an undefined state.
*/
int ci_getline_nc(const struct channel *chn,
char **blk1, size_t *len1,
char **blk2, size_t *len2)
{
int retcode;
int l;
retcode = ci_getblk_nc(chn, blk1, len1, blk2, len2);
if (unlikely(retcode <= 0))
return retcode;
for (l = 0; l < *len1 && (*blk1)[l] != '\n'; l++);
if (l < *len1 && (*blk1)[l] == '\n') {
*len1 = l + 1;
return 1;
}
if (retcode >= 2) {
for (l = 0; l < *len2 && (*blk2)[l] != '\n'; l++);
if (l < *len2 && (*blk2)[l] == '\n') {
*len2 = l + 1;
return 2;
}
}
if (chn->flags & CF_SHUTW) {
/* If we have found no LF and the buffer is shut, then
* the resulting string is made of the concatenation of
* the pending blocks (1 or 2).
*/
return retcode;
}
/* No LF yet and not shut yet */
return 0;
}
/* Inserts <str> followed by "\r\n" at position <pos> relative to channel <c>'s
* input head. The <len> argument informs about the length of string <str> so
* that we don't have to measure it. <str> must be a valid pointer and must not
* include the trailing "\r\n".
*
* The number of bytes added is returned on success. 0 is returned on failure.
*/
int ci_insert_line2(struct channel *c, int pos, const char *str, int len)
{
struct buffer *b = &c->buf;
char *dst = c_ptr(c, pos);
int delta;
delta = len + 2;
if (__b_tail(b) + delta >= b_wrap(b))
return 0; /* no space left */
if (b_data(b) &&
b_tail(b) + delta > b_head(b) &&
b_head(b) >= b_tail(b))
return 0; /* no space left before wrapping data */
/* first, protect the end of the buffer */
memmove(dst + delta, dst, b_tail(b) - dst);
/* now, copy str over dst */
memcpy(dst, str, len);
dst[len] = '\r';
dst[len + 1] = '\n';
b_add(b, delta);
return delta;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/