Update copyrights to 2021, using "make update-copyright"
[tor.git] / src / lib / net / buffers_net.c
bloba485aa41c621b8d41fc5ff85f4904faebeb3277b
1 /* Copyright (c) 2001 Matej Pfajfar.
2 * Copyright (c) 2001-2004, Roger Dingledine.
3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4 * Copyright (c) 2007-2021, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
7 /**
8 * \file buffers_net.c
9 * \brief Read and write data on a buf_t object.
10 **/
12 #define BUFFERS_PRIVATE
13 #include "lib/net/buffers_net.h"
14 #include "lib/buf/buffers.h"
15 #include "lib/log/log.h"
16 #include "lib/log/util_bug.h"
17 #include "lib/net/nettypes.h"
19 #ifdef _WIN32
20 #include <winsock2.h>
21 #endif
23 #include <stdlib.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
29 #ifdef PARANOIA
30 /** Helper: If PARANOIA is defined, assert that the buffer in local variable
31 * <b>buf</b> is well-formed. */
32 #define check() STMT_BEGIN buf_assert_ok(buf); STMT_END
33 #else
34 #define check() STMT_NIL
35 #endif /* defined(PARANOIA) */
37 /** Read up to <b>at_most</b> bytes from the file descriptor <b>fd</b> into
38 * <b>chunk</b> (which must be on <b>buf</b>). If we get an EOF, set
39 * *<b>reached_eof</b> to 1. Uses <b>tor_socket_recv()</b> iff <b>is_socket</b>
40 * is true, otherwise it uses <b>read()</b>. Return -1 on error (and sets
41 * *<b>error</b> to errno), 0 on eof or blocking, and the number of bytes read
42 * otherwise. */
43 static inline int
44 read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most,
45 int *reached_eof, int *error, bool is_socket)
47 ssize_t read_result;
48 if (at_most > CHUNK_REMAINING_CAPACITY(chunk))
49 at_most = CHUNK_REMAINING_CAPACITY(chunk);
51 if (is_socket)
52 read_result = tor_socket_recv(fd, CHUNK_WRITE_PTR(chunk), at_most, 0);
53 else
54 read_result = read(fd, CHUNK_WRITE_PTR(chunk), at_most);
56 if (read_result < 0) {
57 int e = is_socket ? tor_socket_errno(fd) : errno;
59 if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */
60 #ifdef _WIN32
61 if (e == WSAENOBUFS)
62 log_warn(LD_NET, "%s() failed: WSAENOBUFS. Not enough ram?",
63 is_socket ? "recv" : "read");
64 #endif
65 if (error)
66 *error = e;
67 return -1;
69 return 0; /* would block. */
70 } else if (read_result == 0) {
71 log_debug(LD_NET,"Encountered eof on fd %d", (int)fd);
72 *reached_eof = 1;
73 return 0;
74 } else { /* actually got bytes. */
75 buf->datalen += read_result;
76 chunk->datalen += read_result;
77 log_debug(LD_NET,"Read %ld bytes. %d on inbuf.", (long)read_result,
78 (int)buf->datalen);
79 tor_assert(read_result <= BUF_MAX_LEN);
80 return (int)read_result;
84 /** Read from file descriptor <b>fd</b>, writing onto end of <b>buf</b>. Read
85 * at most <b>at_most</b> bytes, growing the buffer as necessary. If recv()
86 * returns 0 (because of EOF), set *<b>reached_eof</b> to 1 and return 0.
87 * Return -1 on error; else return the number of bytes read.
89 /* XXXX indicate "read blocked" somehow? */
90 static int
91 buf_read_from_fd(buf_t *buf, int fd, size_t at_most,
92 int *reached_eof,
93 int *socket_error,
94 bool is_socket)
96 /* XXXX It's stupid to overload the return values for these functions:
97 * "error status" and "number of bytes read" are not mutually exclusive.
99 int r = 0;
100 size_t total_read = 0;
102 check();
103 tor_assert(reached_eof);
104 tor_assert(SOCKET_OK(fd));
106 if (BUG(buf->datalen > BUF_MAX_LEN))
107 return -1;
108 if (BUG(buf->datalen > BUF_MAX_LEN - at_most))
109 return -1;
111 while (at_most > total_read) {
112 size_t readlen = at_most - total_read;
113 chunk_t *chunk;
114 if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) {
115 chunk = buf_add_chunk_with_capacity(buf, at_most, 1);
116 if (readlen > chunk->memlen)
117 readlen = chunk->memlen;
118 } else {
119 size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail);
120 chunk = buf->tail;
121 if (cap < readlen)
122 readlen = cap;
125 r = read_to_chunk(buf, chunk, fd, readlen,
126 reached_eof, socket_error, is_socket);
127 check();
128 if (r < 0)
129 return r; /* Error */
130 tor_assert(total_read+r <= BUF_MAX_LEN);
131 total_read += r;
132 if ((size_t)r < readlen) { /* eof, block, or no more to read. */
133 break;
136 return (int)total_read;
139 /** Helper for buf_flush_to_socket(): try to write <b>sz</b> bytes from chunk
140 * <b>chunk</b> of buffer <b>buf</b> onto file descriptor <b>fd</b>. Return
141 * the number of bytes written on success, 0 on blocking, -1 on failure.
143 static inline int
144 flush_chunk(tor_socket_t fd, buf_t *buf, chunk_t *chunk, size_t sz,
145 bool is_socket)
147 ssize_t write_result;
149 if (sz > chunk->datalen)
150 sz = chunk->datalen;
152 if (is_socket)
153 write_result = tor_socket_send(fd, chunk->data, sz, 0);
154 else
155 write_result = write(fd, chunk->data, sz);
157 if (write_result < 0) {
158 int e = is_socket ? tor_socket_errno(fd) : errno;
160 if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */
161 #ifdef _WIN32
162 if (e == WSAENOBUFS)
163 log_warn(LD_NET,"write() failed: WSAENOBUFS. Not enough ram?");
164 #endif
165 return -1;
167 log_debug(LD_NET,"write() would block, returning.");
168 return 0;
169 } else {
170 buf_drain(buf, write_result);
171 tor_assert(write_result <= BUF_MAX_LEN);
172 return (int)write_result;
176 /** Write data from <b>buf</b> to the file descriptor <b>fd</b>. Write at most
177 * <b>sz</b> bytes, and remove the written bytes
178 * from the buffer. Return the number of bytes written on success,
179 * -1 on failure. Return 0 if write() would block.
181 static int
182 buf_flush_to_fd(buf_t *buf, int fd, size_t sz,
183 bool is_socket)
185 /* XXXX It's stupid to overload the return values for these functions:
186 * "error status" and "number of bytes flushed" are not mutually exclusive.
188 int r;
189 size_t flushed = 0;
190 tor_assert(SOCKET_OK(fd));
191 if (BUG(sz > buf->datalen)) {
192 sz = buf->datalen;
195 check();
196 while (sz) {
197 size_t flushlen0;
198 tor_assert(buf->head);
199 if (buf->head->datalen >= sz)
200 flushlen0 = sz;
201 else
202 flushlen0 = buf->head->datalen;
204 r = flush_chunk(fd, buf, buf->head, flushlen0, is_socket);
205 check();
206 if (r < 0)
207 return r;
208 flushed += r;
209 sz -= r;
210 if (r == 0 || (size_t)r < flushlen0) /* can't flush any more now. */
211 break;
213 tor_assert(flushed <= BUF_MAX_LEN);
214 return (int)flushed;
217 /** Write data from <b>buf</b> to the socket <b>s</b>. Write at most
218 * <b>sz</b> bytes, decrement *<b>buf_flushlen</b> by
219 * the number of bytes actually written, and remove the written bytes
220 * from the buffer. Return the number of bytes written on success,
221 * -1 on failure. Return 0 if write() would block.
224 buf_flush_to_socket(buf_t *buf, tor_socket_t s, size_t sz)
226 return buf_flush_to_fd(buf, s, sz, true);
229 /** Read from socket <b>s</b>, writing onto end of <b>buf</b>. Read at most
230 * <b>at_most</b> bytes, growing the buffer as necessary. If recv() returns 0
231 * (because of EOF), set *<b>reached_eof</b> to 1 and return 0. Return -1 on
232 * error; else return the number of bytes read.
235 buf_read_from_socket(buf_t *buf, tor_socket_t s, size_t at_most,
236 int *reached_eof,
237 int *socket_error)
239 return buf_read_from_fd(buf, s, at_most, reached_eof, socket_error, true);
242 /** Write data from <b>buf</b> to the pipe <b>fd</b>. Write at most
243 * <b>sz</b> bytes, decrement *<b>buf_flushlen</b> by
244 * the number of bytes actually written, and remove the written bytes
245 * from the buffer. Return the number of bytes written on success,
246 * -1 on failure. Return 0 if write() would block.
249 buf_flush_to_pipe(buf_t *buf, int fd, size_t sz)
251 return buf_flush_to_fd(buf, fd, sz, false);
254 /** Read from pipe <b>fd</b>, writing onto end of <b>buf</b>. Read at most
255 * <b>at_most</b> bytes, growing the buffer as necessary. If read() returns 0
256 * (because of EOF), set *<b>reached_eof</b> to 1 and return 0. Return -1 on
257 * error; else return the number of bytes read.
260 buf_read_from_pipe(buf_t *buf, int fd, size_t at_most,
261 int *reached_eof,
262 int *socket_error)
264 return buf_read_from_fd(buf, fd, at_most, reached_eof, socket_error, false);