int link_read(struct link *link, char *data, size_t count, time_t stoptime) { ssize_t total = 0; ssize_t chunk = 0; if(count == 0) return 0; /* If this is a small read, attempt to fill the buffer */ if(count < BUFFER_SIZE) { chunk = fill_buffer(link, stoptime); if(chunk <= 0) return chunk; } /* Then, satisfy the read from the buffer, if any. */ if(link->buffer_length > 0) { chunk = MIN(link->buffer_length, count); memcpy(data, &link->buffer[link->buffer_start], chunk); data += chunk; total += chunk; count -= chunk; link->buffer_start += chunk; link->buffer_length -= chunk; } /* Otherwise, pull it all off the wire. */ while(count > 0) { chunk = read(link->fd, data, count); if(chunk < 0) { if(errno_is_temporary(errno)) { if(link_sleep(link, stoptime, 1, 0)) { continue; } else { break; } } else { break; } } else if(chunk == 0) { break; } else { total += chunk; count -= chunk; data += chunk; } } if(total > 0) { return total; } else { if(chunk == 0) { return 0; } else { return -1; } } }
struct link *link_accept(struct link *master, time_t stoptime) { struct link *link = 0; link = link_create(); if(!link) goto failure; while(1) { if(!link_sleep(master, stoptime, 1, 0)) goto failure; link->fd = accept(master->fd, 0, 0); break; } if(!link_nonblocking(link, 1)) goto failure; if(!link_address_remote(link, link->raddr, &link->rport)) goto failure; link_squelch(); debug(D_TCP, "got connection from %s:%d", link->raddr, link->rport); return link; failure: if(link) link_close(link); return 0; }
static int fill_buffer(struct link *link, time_t stoptime) { int chunk; if(link->buffer_length > 0) return link->buffer_length; while(1) { chunk = read(link->fd, link->buffer, BUFFER_SIZE); if(chunk > 0) { link->buffer_start = 0; link->buffer_length = chunk; return chunk; } else if(chunk == 0) { link->buffer_start = 0; link->buffer_length = 0; return 0; } else { if(errno_is_temporary(errno)) { if(link_sleep(link, stoptime, 1, 0)) { continue; } else { return -1; } } else { return -1; } } } }
static ssize_t fill_buffer(struct link *link, time_t stoptime) { if(link->buffer_length > 0) return link->buffer_length; while(1) { ssize_t chunk = read(link->fd, link->buffer, sizeof(link->buffer)); if(chunk > 0) { link->read += chunk; link->buffer_start = link->buffer; link->buffer_length = chunk; return chunk; } else if(chunk == 0) { link->buffer_start = link->buffer; link->buffer_length = 0; return 0; } else { if(errno_is_temporary(errno)) { if(link_sleep(link, stoptime, 1, 0)) { continue; } else { return -1; } } else { return -1; } } } }
ssize_t link_read_avail(struct link *link, char *data, size_t count, time_t stoptime) { ssize_t total = 0; ssize_t chunk = 0; /* First, satisfy anything from the buffer. */ if(link->buffer_length > 0) { chunk = MIN(link->buffer_length, count); memcpy(data, link->buffer_start, chunk); data += chunk; total += chunk; count -= chunk; link->buffer_start += chunk; link->buffer_length -= chunk; } /* Next, read what is available off the wire */ while(count > 0) { chunk = read(link->fd, data, count); if(chunk < 0) { /* ONLY BLOCK IF NOTHING HAS BEEN READ */ if(errno_is_temporary(errno) && total == 0) { if(link_sleep(link, stoptime, 1, 0)) { continue; } else { break; } } else { break; } } else if(chunk == 0) { break; } else { link->read += chunk; total += chunk; count -= chunk; data += chunk; } } if(total > 0) { return total; } else { if(chunk == 0) { return 0; } else { return -1; } } }
ssize_t link_write(struct link *link, const char *data, size_t count, time_t stoptime) { ssize_t total = 0; ssize_t chunk = 0; if (!link) return errno = EINVAL, -1; while(count > 0) { chunk = write(link->fd, data, count); if(chunk < 0) { if(errno_is_temporary(errno)) { if(link_sleep(link, stoptime, 0, 1)) { continue; } else { break; } } else { break; } } else if(chunk == 0) { break; } else { link->written += chunk; total += chunk; count -= chunk; data += chunk; } } if(total > 0) { return total; } else { if(chunk == 0) { return 0; } else { return -1; } } }
struct link *link_connect(const char *addr, int port, time_t stoptime) { struct sockaddr_in address; struct link *link = 0; int result; int save_errno; link = link_create(); if(!link) goto failure; link_squelch(); memset(&address, 0, sizeof(address)); #if defined(CCTOOLS_OPSYS_DARWIN) address.sin_len = sizeof(address); #endif address.sin_family = AF_INET; address.sin_port = htons(port); if(!string_to_ip_address(addr, (unsigned char *) &address.sin_addr)) goto failure; link->fd = socket(AF_INET, SOCK_STREAM, 0); if(link->fd < 0) goto failure; link_window_configure(link); /* sadly, cygwin does not do non-blocking connect correctly */ #ifdef CCTOOLS_OPSYS_CYGWIN if(!link_nonblocking(link, 0)) goto failure; #else if(!link_nonblocking(link, 1)) goto failure; #endif debug(D_TCP, "connecting to %s:%d", addr, port); do { result = connect(link->fd, (struct sockaddr *) &address, sizeof(address)); /* On some platforms, errno is not set correctly. */ /* If the remote address can be found, then we are really connected. */ /* Also, on bsd-derived systems, failure to connect is indicated by a second connect returning EINVAL. */ if(result < 0 && !errno_is_temporary(errno)) { if(errno == EINVAL) errno = ECONNREFUSED; break; } if(link_address_remote(link, link->raddr, &link->rport)) { debug(D_TCP, "made connection to %s:%d", link->raddr, link->rport); #ifdef CCTOOLS_OPSYS_CYGWIN link_nonblocking(link, 1); #endif return link; } } while(link_sleep(link, stoptime, 0, 1)); debug(D_TCP, "connection to %s:%d failed (%s)", addr, port, strerror(errno)); failure: save_errno = errno; if(link) link_close(link); errno = save_errno; return 0; }
struct link *link_connect(const char *addr, int port, time_t stoptime) { struct sockaddr_in address; struct link *link = 0; int result; int save_errno; link = link_create(); if(!link) goto failure; link_squelch(); memset(&address, 0, sizeof(address)); #if defined(CCTOOLS_OPSYS_DARWIN) address.sin_len = sizeof(address); #endif address.sin_family = AF_INET; address.sin_port = htons(port); if(!string_to_ip_address(addr, (unsigned char *) &address.sin_addr)) goto failure; link->fd = socket(AF_INET, SOCK_STREAM, 0); if(link->fd < 0) goto failure; link_window_configure(link); /* sadly, cygwin does not do non-blocking connect correctly */ #ifdef CCTOOLS_OPSYS_CYGWIN if(!link_nonblocking(link, 0)) goto failure; #else if(!link_nonblocking(link, 1)) goto failure; #endif debug(D_TCP, "connecting to %s:%d", addr, port); while(1) { // First attempt a non-blocking connect result = connect(link->fd, (struct sockaddr *) &address, sizeof(address)); // On many platforms, non-blocking connect sets errno in unexpected ways: // On OSX, result=-1 and errno==EISCONN indicates a successful connection. if(result<0 && errno==EISCONN) result=0; // On BSD-derived systems, failure to connect is indicated by errno = EINVAL. // Set it to something more explanatory. if(result<0 && errno==EINVAL) errno=ECONNREFUSED; // Otherwise, a non-temporary errno should cause us to bail out. if(result<0 && !errno_is_temporary(errno)) break; // If the remote address is valid, we are connected no matter what. if(link_address_remote(link, link->raddr, &link->rport)) { debug(D_TCP, "made connection to %s:%d", link->raddr, link->rport); #ifdef CCTOOLS_OPSYS_CYGWIN link_nonblocking(link, 1); #endif return link; } // if the time has expired, bail out if( time(0) >= stoptime ) { errno = ETIMEDOUT; break; } // wait for some activity on the socket. link_sleep(link, stoptime, 0, 1); // No matter how the sleep ends, we want to go back to the top // and call connect again to get a proper errno. } debug(D_TCP, "connection to %s:%d failed (%s)", addr, port, strerror(errno)); failure: save_errno = errno; if(link) link_close(link); errno = save_errno; return 0; }