ret_t cherokee_socket_connect (cherokee_socket_t *sock) { int r; int err; TRACE (ENTRIES",connect", "connect type=%s\n", SOCKET_AF(sock) == AF_INET ? "AF_INET" : SOCKET_AF(sock) == AF_INET6 ? "AF_INET6" : SOCKET_AF(sock) == AF_UNIX ? "AF_UNIX" : "Unknown"); do { switch (SOCKET_AF(sock)) { case AF_INET: r = connect (SOCKET_FD(sock), (struct sockaddr *) &SOCKET_ADDR(sock), sizeof(struct sockaddr_in)); break; #ifdef HAVE_IPV6 case AF_INET6: r = connect (SOCKET_FD(sock), (struct sockaddr *) &SOCKET_ADDR(sock), sizeof(struct sockaddr_in6)); break; #endif #ifdef HAVE_SOCKADDR_UN case AF_UNIX: r = connect (SOCKET_FD(sock), (struct sockaddr *) &SOCKET_ADDR(sock), SUN_LEN (SOCKET_ADDR_UNIX(sock))); break; #endif default: SHOULDNT_HAPPEN; return ret_no_sys; } } while ((r == -1) && (errno == EINTR)); if (r < 0) { err = SOCK_ERRNO(); TRACE (ENTRIES",connect", "connect error=%d '%s'\n", err, strerror(err)); switch (err) { case EISCONN: break; case EINVAL: case ENOENT: case ECONNRESET: case ECONNREFUSED: case EADDRNOTAVAIL: return ret_deny; case ETIMEDOUT: return ret_error; case EAGAIN: case EALREADY: case EINPROGRESS: #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif return ret_eagain; default: LOG_ERRNO_S (errno, cherokee_err_error, CHEROKEE_ERROR_SOCKET_CONNECT); return ret_error; } } TRACE (ENTRIES",connect", "succeed. fd=%d\n", SOCKET_FD(sock)); sock->status = socket_reading; return ret_ok; }
ret_t cherokee_socket_sendfile (cherokee_socket_t *socket, int fd, size_t size, off_t *offset, ssize_t *sent) { int re; off_t _sent = size; static cherokee_boolean_t no_sys = false; /* Exit if there is no sendfile() function in the system */ if (unlikely (no_sys)) return ret_no_sys; /* If there is nothing to send then return now, this may be * needed in some systems (i.e. *BSD) because value 0 may have * special meanings or trigger occasional hidden bugs. */ if (unlikely (size == 0)) return ret_ok; /* Limit size of data that has to be sent. */ if (size > MAX_SF_BLK_SIZE2) size = MAX_SF_BLK_SIZE; #if defined(LINUX_BROKEN_SENDFILE_API) UNUSED(socket); UNUSED(fd); UNUSED(offset); UNUSED(sent); /* Large file support is set but native Linux 2.2 or 2.4 sendfile() * does not support _FILE_OFFSET_BITS 64 */ return ret_no_sys; #elif defined(LINUX_SENDFILE_API) /* Linux sendfile * * ssize_t * sendfile (int out_fd, int in_fd, off_t *offset, size_t *count); * * ssize_t * sendfile64 (int out_fd, int in_fd, off64_t *offset, size_t *count); */ *sent = sendfile (SOCKET_FD(socket), /* int out_fd */ fd, /* int in_fd */ offset, /* off_t *offset */ size); /* size_t count */ if (*sent < 0) { switch (errno) { case EINTR: case EAGAIN: #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif return ret_eagain; case EINVAL: /* maybe sendfile is not supported by this FS (no mmap available), * since more than one FS can be used (ext2, ext3, ntfs, etc.) * we should retry with emulated sendfile (read+write). */ return ret_no_sys; case ENOSYS: /* This kernel does not support sendfile at all. */ no_sys = true; return ret_no_sys; } return ret_error; } else if (*sent == 0) { /* It isn't an error, but it wrote nothing */ return ret_error; } #elif DARWIN_SENDFILE_API /* MacOS X: BSD-like System Call * * int * sendfile (int fd, int s, off_t offset, off_t *len, * struct sf_hdtr *hdtr, int flags); */ re = sendfile (fd, /* int fd */ SOCKET_FD(socket), /* int s */ *offset, /* off_t offset */ &_sent, /* off_t *len */ NULL, /* struct sf_hdtr *hdtr */ 0); /* int flags */ if (re == -1) { switch (errno) { case EINTR: case EAGAIN: /* It might have sent some information */ if (_sent <= 0) return ret_eagain; break; case ENOSYS: no_sys = true; return ret_no_sys; default: return ret_error; } } else if (_sent == 0) { /* It wrote nothing. Most likely the file was * truncated and the fd offset is off-limits. */ return ret_error; } *sent = _sent; *offset = *offset + _sent; #elif SOLARIS_SENDFILE_API *sent = sendfile (SOCKET_FD(socket), /* int out_fd */ fd, /* int in_fd */ offset, /* off_t *off */ size); /* size_t len */ if (*sent < 0) { switch (errno) { case EINTR: case EAGAIN: #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif return ret_eagain; case ENOSYS: /* This kernel does not support sendfile at all. */ no_sys = true; return ret_no_sys; case EAFNOSUPPORT: return ret_no_sys; } return ret_error; } else if (*sent == 0) { /* It isn't an error, but it wrote nothing */ return ret_error; } #elif FREEBSD_SENDFILE_API struct sf_hdtr hdr; struct iovec hdtrl; hdr.headers = &hdtrl; hdr.hdr_cnt = 1; hdr.trailers = NULL; hdr.trl_cnt = 0; hdtrl.iov_base = NULL; hdtrl.iov_len = 0; *sent = 0; /* FreeBSD sendfile: in_fd and out_fd are reversed * * int * sendfile (int fd, int s, off_t offset, size_t nbytes, * struct sf_hdtr *hdtr, off_t *sbytes, int flags); */ re = sendfile (fd, /* int fd */ SOCKET_FD(socket), /* int s */ *offset, /* off_t offset */ size, /* size_t nbytes */ &hdr, /* struct sf_hdtr *hdtr */ sent, /* off_t *sbytes */ 0); /* int flags */ if (re == -1) { switch (errno) { case EINTR: case EAGAIN: #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif if (*sent < 1) return ret_eagain; /* else it's ok, something has been sent. */ break; case ENOSYS: no_sys = true; return ret_no_sys; default: return ret_error; } } else if (*sent == 0) { /* It isn't an error, but it wrote nothing */ return ret_error; } *offset = *offset + *sent; #elif HPUX_SENDFILE_API /* HP-UX: * * sbsize_t sendfile(int s, int fd, off_t offset, bsize_t nbytes, * const struct iovec *hdtrl, int flags); * * HPUX guarantees that if any data was written before * a signal interrupt then sendfile returns the number of * bytes written (which may be less than requested) not -1. * nwritten includes the header data sent. */ *sent = sendfile (SOCKET_FD(socket), /* socket */ fd, /* fd to send */ *offset, /* where to start */ size, /* bytes to send */ NULL, /* Headers/footers */ 0); /* flags */ if (*sent < 0) { switch (errno) { case EINTR: case EAGAIN: #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif return ret_eagain; case ENOSYS: no_sys = true; return ret_no_sys; } return ret_error; } else if (*sent == 0) { /* It isn't an error, but it wrote nothing */ return ret_error; } *offset = *offset + *sent; #else UNUSED(socket); UNUSED(fd); UNUSED(offset); UNUSED(sent); SHOULDNT_HAPPEN; return ret_error; #endif return ret_ok; }
/* WARNING: all parameters MUST be valid, * NULL pointers lead to a crash. */ ret_t cherokee_socket_read (cherokee_socket_t *socket, char *buf, int buf_size, size_t *pcnt_read) { ret_t ret; int err; ssize_t len; *pcnt_read = 0; /* There must be something to read, otherwise behaviour is undefined * and as we don't want this case, we have to enforce assertions. */ return_if_fail (buf != NULL && buf_size > 0, ret_error); if (unlikely (socket->status == socket_closed)) { TRACE(ENTRIES, "Reading a closed socket: fd=%d (TLS=%d)\n", SOCKET_FD(socket), (socket->is_tls == TLS)); return ret_eof; } if (likely (socket->is_tls != TLS)) { /* Plain read */ do { len = recv (SOCKET_FD(socket), buf, buf_size, 0); } while ((len < 0) && (errno == EINTR)); if (likely (len > 0)) { *pcnt_read = len; return ret_ok; } if (len == 0) { socket->status = socket_closed; return ret_eof; } /* Error handling */ err = SOCK_ERRNO(); TRACE(ENTRIES",read", "Socket read error fd=%d: '%s'\n", SOCKET_FD(socket), strerror(errno)); switch (err) { #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case EAGAIN: return ret_eagain; case EPIPE: #ifdef ENOTCONN case ENOTCONN: #endif case ECONNRESET: socket->status = socket_closed; case ETIMEDOUT: case EHOSTUNREACH: return ret_error; } LOG_ERRNO (errno, cherokee_err_error, CHEROKEE_ERROR_SOCKET_READ, SOCKET_FD(socket)); return ret_error; } else if (socket->cryptor != NULL) { ret = cherokee_cryptor_socket_read (socket->cryptor, buf, buf_size, pcnt_read); switch (ret) { case ret_ok: case ret_error: case ret_eagain: return ret; case ret_eof: socket->status = socket_closed; return ret_eof; default: RET_UNKNOWN(ret); return ret; } } return ret_error; }
/* WARNING: all parameters MUST be valid, * NULL pointers lead to a crash. */ ret_t cherokee_socket_writev (cherokee_socket_t *socket, const struct iovec *vector, uint16_t vector_len, size_t *pcnt_written) { int re; int i; ret_t ret; size_t cnt; *pcnt_written = 0; /* There must be something to send, otherwise behaviour is undefined * and as we don't want this case, we have to enforce assertions. */ return_if_fail (vector != NULL && vector_len > 0, ret_error); if (likely (socket->is_tls != TLS)) { do { re = writev (SOCKET_FD(socket), vector, vector_len); } while ((re == -1) && (errno == EINTR)); if (likely (re > 0)) { *pcnt_written = (size_t) re; return ret_ok; } if (re == 0) { int i; /* Find out whether there was something to send or not. */ for (i = 0; i < vector_len; i++) { if (vector[i].iov_base != NULL && vector[i].iov_len > 0) break; } if (i < vector_len) return ret_eagain; /* No, nothing to send, so return ok. */ return ret_ok; } if (re < 0) { int err = SOCK_ERRNO(); switch (err) { #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case EAGAIN: return ret_eagain; case EPIPE: #ifdef ENOTCONN case ENOTCONN: #endif case ECONNRESET: socket->status = socket_closed; case ETIMEDOUT: case EHOSTUNREACH: return ret_error; } LOG_ERRNO (errno, cherokee_err_error, CHEROKEE_ERROR_SOCKET_WRITEV, SOCKET_FD(socket)); } return ret_error; } /* TLS connection: Here we don't worry about sparing a few CPU * cycles, so we reuse the single send case for TLS. */ for (i = 0; i < vector_len; i++) { if ((vector[i].iov_len == 0) || (vector[i].iov_base == NULL)) continue; cnt = 0; ret = cherokee_socket_write (socket, vector[i].iov_base, vector[i].iov_len, &cnt); if (ret != ret_ok) { return ret; } *pcnt_written += cnt; if (cnt == vector[i].iov_len) continue; /* Unfinished */ return ret_ok; } /* Did send everything */ return ret_ok; }
/* WARNING: all parameters MUST be valid, * NULL pointers lead to a crash. */ ret_t cherokee_socket_write (cherokee_socket_t *socket, const char *buf, int buf_len, size_t *pcnt_written) { ret_t ret; int err; ssize_t len; *pcnt_written = 0; /* There must be something to send, otherwise behaviour is undefined * and as we don't want this case, we have to enforce assertions. */ return_if_fail (buf != NULL && buf_len > 0, ret_error); if (likely (socket->is_tls != TLS)) { do { len = send (SOCKET_FD(socket), buf, buf_len, 0); } while ((len < 0) && (errno == EINTR)); if (likely (len > 0) ) { /* Return n. of bytes sent. */ *pcnt_written = len; return ret_ok; } if (len == 0) { /* Very strange, socket is ready but nothing * has been written, retry later. */ return ret_eagain; } /* Error handling */ err = SOCK_ERRNO(); switch (err) { #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case EAGAIN: return ret_eagain; case EPIPE: case ECONNRESET: #ifdef ENOTCONN case ENOTCONN: #endif socket->status = socket_closed; case ETIMEDOUT: case EHOSTUNREACH: return ret_error; } LOG_ERRNO (errno, cherokee_err_error, CHEROKEE_ERROR_SOCKET_WRITE, SOCKET_FD(socket)); return ret_error; } else if (socket->cryptor != NULL) { ret = cherokee_cryptor_socket_write (socket->cryptor, (char *)buf, buf_len, pcnt_written); switch (ret) { case ret_ok: case ret_error: case ret_eagain: return ret; case ret_eof: socket->status = socket_closed; return ret_eof; default: RET_UNKNOWN(ret); return ret; } } return ret_error; }