1 /* $Id: line.c 7837 2008-05-19 17:14:15Z iulius $
3 ** Line by line reading support from sockets/pipes
5 ** Written by Alex Kiernan (alex.kiernan@thus.net)
7 ** This code implements a infinitely (well size_t) long single line
8 ** read routine, to protect against eating all available memory it
9 ** actually starts discarding characters if you try to send more than
10 ** the maximum article size in a single line.
17 #ifdef HAVE_SYS_SELECT_H
18 # include <sys/select.h>
21 #include "inn/messages.h"
25 #include <openssl/ssl.h>
31 ** free a previously allocated line structure
34 line_free(struct line *line)
36 static const struct line nullline = {0, 0, 0, 0};
38 if (line && line->start) {
46 ** Alarm signal handler for client timeout.
51 SSL_shutdown(tls_conn);
58 ** initialise a new line structure
61 line_init(struct line *line)
64 line->allocated = NNTP_STRLEN;
65 line->where = line->start = xmalloc(line->allocated);
70 line_doread(void *p, size_t len, int timeout)
77 xsignal(SIGALRM, alarmHandler);
80 n = SSL_read(tls_conn, p, len);
82 if (tls_conn == NULL) {
85 err = SSL_get_error(tls_conn, n);
87 case SSL_ERROR_SYSCALL:
91 SSL_shutdown(tls_conn);
96 } while (err == SSL_ERROR_WANT_READ);
97 xsignal(SIGALRM, SIG_DFL);
101 n = read(STDIN_FILENO, p, len);
102 } while (n == -1 && errno == EINTR);
110 line_read(struct line *line, int timeout, const char **p, size_t *len)
116 assert(line != NULL);
117 assert(line->start != NULL);
118 /* shuffle any trailing portion not yet processed to the start of
120 if (line->remaining != 0) {
121 if (line->start != line->where) {
122 memmove(line->start, line->where, line->remaining);
124 lf = memchr(line->start, '\n', line->remaining);
126 where = line->start + line->remaining;
128 /* if we found a line terminator in the data we have we don't need
129 * to ask for any more */
136 /* if we've filled the line buffer, double the size,
137 * reallocate the buffer and try again */
138 if (where == line->start + line->allocated) {
139 size_t newsize = line->allocated * 2;
141 /* don't grow the buffer bigger than the maximum
142 * article size we'll accept */
143 if (PERMaccessconf->localmaxartsize > NNTP_STRLEN)
144 if (newsize > (unsigned)PERMaccessconf->localmaxartsize)
145 newsize = PERMaccessconf->localmaxartsize;
147 /* if we're trying to grow from the same size, to the
148 * same size, we must have hit the localmaxartsize
149 * buffer for a second (or subsequent) time - the user
150 * is likely trying to DOS us, so don't double the
151 * size any more, just overwrite characters until they
152 * stop, then discard the whole thing */
153 if (newsize == line->allocated) {
154 warn("%s overflowed our line buffer (%ld), "
155 "discarding further input", ClientHost,
156 PERMaccessconf->localmaxartsize);
160 line->start = xrealloc(line->start, newsize);
161 where = line->start + line->allocated;
162 line->allocated = newsize;
167 /* It seems that the SSL_read cannot be mixed with select()
168 * as in the current code. SSL communicates in its own data
169 * blocks and hand shaking. The do_readline using SSL_read
170 * could return, but still with a partial line in the SSL_read
171 * buffer. Then the server SSL routine would sit there waiting
172 * for completion of that data block while nnrpd sat at the
173 * select() routine waiting for more data from the server.
175 * Here, we decide to just bypass the select() wait. Unlike
176 * innd with multiple threads, the select on nnrpd is just
177 * waiting on a single file descriptor, so it is not really
178 * essential with blocked read like SSL_read. Using an alarm
179 * signal around SSL_read for non active timeout, SSL works
180 * without dead locks. However, without the select() wait,
181 * the IDLE timer stat won't be collected...
183 if (tls_conn == NULL) {
185 /* Wait for activity on stdin, updating timer stats as we
191 FD_SET(STDIN_FILENO, &rmask);
195 i = select(STDIN_FILENO + 1, &rmask, NULL, NULL, &t);
197 if (i == -1 && errno != EINTR) {
198 syswarn("%s can't select", ClientHost);
203 /* If stdin didn't select, we must have timed out. */
204 if (i == 0 || !FD_ISSET(STDIN_FILENO, &rmask))
209 count = line_doread(where,
210 line->allocated - (where - line->start),
213 /* give timeout for read errors */
215 sysnotice("%s can't read", ClientHost);
218 /* if we hit EOF, terminate the string and send it back */
220 assert((where + count) < (line->start + line->allocated));
224 /* search for `\n' in what we just read, if we find it we'll
225 * drop out and return the line for processing */
226 lf = memchr(where, '\n', count);
228 } while (lf == NULL);
231 /* remember where we've processed up to so we can start off there
233 line->where = lf + 1;
234 line->remaining = where - line->where;
237 /* if we see a full CRLF pair strip them both off before
238 * returning the line to our caller, if we just get an LF
239 * we'll accept that too */
240 if (lf > line->start && lf[-1] == '\r') {
244 *len = lf - line->start;