/* This is like socket_buffer_read, except that it blocks until it can read all size bytes. If fewer than size bytes are available, it reads them and returns -1. */ int socket_buffer_readcount(struct socket_buffer *buf, char *out, size_t size) { size_t n = 0; int i; while (n < size) { /* Refill the buffer if necessary. */ if (buf->p >= buf->end) { buf->p = buf->buffer; do { errno = 0; i = fdinfo_recv(&buf->fdn, buf->buffer, sizeof(buf->buffer)); } while (i == -1 && errno == EINTR); if (i <= 0) return -1; buf->end = buf->buffer + i; } i = buf->end - buf->p; if (i < size - n) { memcpy(out + n, buf->p, i); buf->p += i; n += i; } else { memcpy(out + n, buf->p, size - n); buf->p += size - n; n += size - n; } } return n; }
/* Read a line thorough a stateful socket buffer. The line, including its '\n', is returned in a dynamically allocated buffer. The length of the line is returned in *n. If the length of the line exceeds maxlen, then NULL is returned and *n is greater than or equal to maxlen. On error, NULL is returned and *n is less than maxlen. The returned buffer is always null-terminated if the return value is not NULL. */ char *socket_buffer_readline(struct socket_buffer *buf, size_t *n, size_t maxlen) { char *line; char *newline; size_t count; line = NULL; *n = 0; do { /* Refill the buffer if necessary. */ if (buf->p >= buf->end) { int i; buf->p = buf->buffer; do { errno = 0; i = fdinfo_recv(&buf->fdn, buf->buffer, sizeof(buf->buffer)); } while (i == -1 && errno == EINTR); if (i <= 0) { free(line); return NULL; } buf->end = buf->buffer + i; } newline = (char *) memchr(buf->p, '\n', buf->end - buf->p); if (newline == NULL) count = buf->end - buf->p; else count = newline + 1 - buf->p; if (*n + count >= maxlen) { /* Line exceeds our maximum length. */ free(line); *n += count; return NULL; } line = (char *) safe_realloc(line, *n + count + 1); memcpy(line + *n, buf->p, count); *n += count; buf->p += count; } while (newline == NULL); line[*n] = '\0'; return line; }
/* Read from a stateful socket buffer. If there is any data in the buffer it is returned, otherwise data is read with recv. Return value is as for recv. */ int socket_buffer_read(struct socket_buffer *buf, char *out, size_t size) { int i; /* Refill the buffer if necessary. */ if (buf->p >= buf->end) { buf->p = buf->buffer; do { errno = 0; i = fdinfo_recv(&buf->fdn, buf->buffer, sizeof(buf->buffer)); } while (i == -1 && errno == EINTR); if (i <= 0) return i; buf->end = buf->buffer + i; } i = buf->end - buf->p; if (i > size) i = size; memcpy(out, buf->p, i); buf->p += i; return i; }
/* Read from a client socket into buf, returning the number of bytes read, or -1 on an error. This takes care of delays, Telnet negotiation, and logging. If there is more data pending that won't be noticed by select, a 1 is stored in *pending, otherwise 0 is stored there. The caller must loop, processing read data until *pending is false. The reason for this is the SSL_read function that this function may call, which takes data out of the socket buffer (so select may not indicate the socket is readable) and keeps it in its own buffer. *pending holds the result of calling SSL_pending. See http://www.mail-archive.com/[email protected]/msg24324.html. */ int ncat_recv(struct fdinfo *fdn, char *buf, size_t size, int *pending) { int n; *pending = 0; n = fdinfo_recv(fdn, buf, size); if (n <= 0) return n; if (o.linedelay) ncat_delay_timer(o.linedelay); if (o.telnet) dotelnet(fdn->fd, (unsigned char *) buf, n); ncat_log_recv(buf, n); /* SSL can buffer our input, so doing another select() won't necessarily work for us. Indicate to the caller that this function must be called again to get more data. */ *pending = fdinfo_pending(fdn); return n; }
static int handle_connect(struct socket_buffer *client_sock, struct http_request *request) { union sockaddr_u su; size_t sslen = sizeof(su.storage); int maxfd, s, rc; char *line; size_t len; fd_set m, r; if (request->uri.port == -1) { if (o.verbose) logdebug("No port number in CONNECT URI.\n"); return 400; } if (o.debug > 1) logdebug("CONNECT to %s:%hu.\n", request->uri.host, request->uri.port); rc = resolve(request->uri.host, request->uri.port, &su.storage, &sslen, o.af); if (rc != 0) { if (o.debug) { logdebug("Can't resolve name \"%s\": %s.\n", request->uri.host, gai_strerror(rc)); } return 504; } s = Socket(su.storage.ss_family, SOCK_STREAM, IPPROTO_TCP); if (connect(s, &su.sockaddr, sslen) == -1) { if (o.debug) logdebug("Can't connect to %s.\n", inet_socktop(&su)); Close(s); return 504; } send_string(&client_sock->fdn, http_code2str(200)); /* Clear out whatever is left in the socket buffer. The client may have already sent the first part of its request to the origin server. */ line = socket_buffer_remainder(client_sock, &len); if (send(s, line, len, 0) < 0) { if (o.debug) logdebug("Error sending %lu leftover bytes: %s.\n", (unsigned long) len, strerror(errno)); Close(s); return 0; } maxfd = client_sock->fdn.fd < s ? s : client_sock->fdn.fd; FD_ZERO(&m); FD_SET(client_sock->fdn.fd, &m); FD_SET(s, &m); errno = 0; while (!socket_errno() || socket_errno() == EINTR) { char buf[DEFAULT_TCP_BUF_LEN]; int len, rc; r = m; fselect(maxfd + 1, &r, NULL, NULL, NULL); zmem(buf, sizeof(buf)); if (FD_ISSET(client_sock->fdn.fd, &r)) { do { do { len = fdinfo_recv(&client_sock->fdn, buf, sizeof(buf)); } while (len == -1 && socket_errno() == EINTR); if (len <= 0) goto end; do { rc = send(s, buf, len, 0); } while (rc == -1 && socket_errno() == EINTR); if (rc == -1) goto end; } while (fdinfo_pending(&client_sock->fdn)); } if (FD_ISSET(s, &r)) { do { len = recv(s, buf, sizeof(buf), 0); } while (len == -1 && socket_errno() == EINTR); if (len <= 0) goto end; do { rc = fdinfo_send(&client_sock->fdn, buf, len); } while (rc == -1 && socket_errno() == EINTR); if (rc == -1) goto end; } } end: close(s); return 0; }