DECLARE_TEST(tcp, blocking) { socket_t* sock = tcp_socket_allocate(); socket_set_blocking(sock, false); EXPECT_FALSE(socket_blocking(sock)); socket_set_blocking(sock, true); EXPECT_TRUE(socket_blocking(sock)); socket_deallocate(sock); return 0; }
/* * pull_client_data is used to pull across any client data (like in a * POST) which needs to be handled before an error can be reported, or * server headers can be processed. * - rjkaes */ static int pull_client_data(struct conn_s *connptr, long int length) { char *buffer; ssize_t len; buffer = safemalloc(min(MAXBUFFSIZE, length)); if (!buffer) return -1; do { len = safe_read(connptr->client_fd, buffer, min(MAXBUFFSIZE, length)); if (len <= 0) goto ERROR_EXIT; if (!connptr->error_variables) { if (safe_write(connptr->server_fd, buffer, len) < 0) goto ERROR_EXIT; } length -= len; } while (length > 0); /* * BUG FIX: Internet Explorer will leave two bytes (carriage * return and line feed) at the end of a POST message. These * need to be eaten for tinyproxy to work correctly. */ socket_nonblocking(connptr->client_fd); len = recv(connptr->client_fd, buffer, 2, MSG_PEEK); socket_blocking(connptr->client_fd); if (len < 0 && errno != EAGAIN) goto ERROR_EXIT; if (len == 2 && CHECK_CRLF(buffer, len)) read(connptr->client_fd, buffer, 2); safefree(buffer); return 0; ERROR_EXIT: safefree(buffer); return -1; }
/* * Switch the sockets into nonblocking mode and begin relaying the bytes * between the two connections. We continue to use the buffering code * since we want to be able to buffer a certain amount for slower * connections (as this was the reason why I originally modified * tinyproxy oh so long ago...) * - rjkaes */ static void relay_connection(struct conn_s *connptr) { fd_set rset, wset; struct timeval tv; time_t last_access; int ret; double tdiff; int maxfd = max(connptr->client_fd, connptr->server_fd) + 1; ssize_t bytes_received; socket_nonblocking(connptr->client_fd); socket_nonblocking(connptr->server_fd); last_access = time(NULL); for (;;) { FD_ZERO(&rset); FD_ZERO(&wset); tv.tv_sec = config.idletimeout - difftime(time(NULL), last_access); tv.tv_usec = 0; if (buffer_size(connptr->sbuffer) > 0) FD_SET(connptr->client_fd, &wset); if (buffer_size(connptr->cbuffer) > 0) FD_SET(connptr->server_fd, &wset); if (buffer_size(connptr->sbuffer) < MAXBUFFSIZE) FD_SET(connptr->server_fd, &rset); if (buffer_size(connptr->cbuffer) < MAXBUFFSIZE) FD_SET(connptr->client_fd, &rset); ret = select(maxfd, &rset, &wset, NULL, &tv); if (ret == 0) { tdiff = difftime(time(NULL), last_access); if (tdiff > config.idletimeout) { log_message(LOG_INFO, "Idle Timeout (after select) as %g > %u.", tdiff, config.idletimeout); return; } else { continue; } } else if (ret < 0) { log_message(LOG_ERR, "relay_connection: select() error \"%s\". Closing connection (client_fd:%d, server_fd:%d)", strerror(errno), connptr->client_fd, connptr->server_fd); return; } else { /* * All right, something was actually selected so mark it. */ last_access = time(NULL); } if (FD_ISSET(connptr->server_fd, &rset)) { bytes_received = read_buffer(connptr->server_fd, connptr->sbuffer); if (bytes_received < 0) break; connptr->content_length.server -= bytes_received; if (connptr->content_length.server == 0) break; } if (FD_ISSET(connptr->client_fd, &rset) && read_buffer(connptr->client_fd, connptr->cbuffer) < 0) { break; } if (FD_ISSET(connptr->server_fd, &wset) && write_buffer(connptr->server_fd, connptr->cbuffer) < 0) { break; } if (FD_ISSET(connptr->client_fd, &wset) && write_buffer(connptr->client_fd, connptr->sbuffer) < 0) { break; } } /* * Here the server has closed the connection... write the * remainder to the client and then exit. */ socket_blocking(connptr->client_fd); while (buffer_size(connptr->sbuffer) > 0) { if (write_buffer(connptr->client_fd, connptr->sbuffer) < 0) break; } shutdown(connptr->client_fd, SHUT_WR); /* * Try to send any remaining data to the server if we can. */ socket_blocking(connptr->server_fd); while (buffer_size(connptr->cbuffer) > 0) { if (write_buffer(connptr->server_fd, connptr->cbuffer) < 0) break; } return; }
/* * This is the main (per child) loop. */ static void child_main (struct child_s *ptr) { int connfd; struct sockaddr *cliaddr; socklen_t clilen; fd_set rfds; int maxfd = 0; ssize_t i; int ret; cliaddr = (struct sockaddr *) safemalloc (sizeof(struct sockaddr_storage)); if (!cliaddr) { log_message (LOG_CRIT, "Could not allocate memory for child address."); exit (0); } ptr->connects = 0; /* * We have to wait for connections on multiple fds, * so use select. */ FD_ZERO(&rfds); for (i = 0; i < vector_length(listen_fds); i++) { int *fd = (int *) vector_getentry(listen_fds, i, NULL); ret = socket_nonblocking(*fd); if (ret != 0) { log_message(LOG_ERR, "Failed to set the listening " "socket %d to non-blocking: %s", fd, strerror(errno)); exit(1); } FD_SET(*fd, &rfds); maxfd = max(maxfd, *fd); } while (!config.quit) { int listenfd = -1; ptr->status = T_WAITING; clilen = sizeof(struct sockaddr_storage); ret = select(maxfd + 1, &rfds, NULL, NULL, NULL); if (ret == -1) { log_message (LOG_ERR, "error calling select: %s", strerror(errno)); exit(1); } else if (ret == 0) { log_message (LOG_WARNING, "Strange: select returned 0 " "but we did not specify a timeout..."); continue; } for (i = 0; i < vector_length(listen_fds); i++) { int *fd = (int *) vector_getentry(listen_fds, i, NULL); if (FD_ISSET(*fd, &rfds)) { /* * only accept the connection on the first * fd that we find readable. - fair? */ listenfd = *fd; break; } } if (listenfd == -1) { log_message(LOG_WARNING, "Strange: None of our listen " "fds was readable after select"); continue; } ret = socket_blocking(listenfd); if (ret != 0) { log_message(LOG_ERR, "Failed to set listening " "socket %d to blocking for accept: %s", listenfd, strerror(errno)); exit(1); } /* * We have a socket that is readable. * Continue handling this connection. */ connfd = accept (listenfd, cliaddr, &clilen); #ifndef NDEBUG /* * Enable the TINYPROXY_DEBUG environment variable if you * want to use the GDB debugger. */ if (getenv ("TINYPROXY_DEBUG")) { /* Pause for 10 seconds to allow us to connect debugger */ fprintf (stderr, "Process has accepted connection: %ld\n", (long int) ptr->tid); sleep (10); fprintf (stderr, "Continuing process: %ld\n", (long int) ptr->tid); } #endif /* * Make sure no error occurred... */ if (connfd < 0) { log_message (LOG_ERR, "Accept returned an error (%s) ... retrying.", strerror (errno)); continue; } ptr->status = T_CONNECTED; SERVER_DEC (); handle_connection (connfd); ptr->connects++; if (child_config.maxrequestsperchild != 0) { DEBUG2 ("%u connections so far...", ptr->connects); if (ptr->connects == child_config.maxrequestsperchild) { log_message (LOG_NOTICE, "Child has reached MaxRequestsPerChild (%u). " "Killing child.", ptr->connects); break; } } SERVER_COUNT_LOCK (); if (*servers_waiting > child_config.maxspareservers) { /* * There are too many spare children, kill ourself * off. */ log_message (LOG_NOTICE, "Waiting servers (%d) exceeds MaxSpareServers (%d). " "Killing child.", *servers_waiting, child_config.maxspareservers); SERVER_COUNT_UNLOCK (); break; } else { SERVER_COUNT_UNLOCK (); } SERVER_INC (); } ptr->status = T_EMPTY; safefree (cliaddr); exit (0); }
void hx_connect (struct htlc_conn *htlc, const char *serverstr, u_int16_t port, const char *name, u_int16_t icon, const char *login, const char *pass, int secure) { int s; struct SOCKADDR_IN saddr; char abuf[HOSTLEN+1]; #if !defined(__WIN32__) if (serverstr[0] == '|' && serverstr[1]) { int pfds[2]; int r; if (htlc->fd) hx_htlc_close(htlc); r = fd_popen(pfds, serverstr+1); if (r < 0) { hx_printf_prefix(htlc, 0, INFOPREFIX, "fd_popen(%s): %s\n", serverstr+1, strerror(errno)); return; } hx_printf_prefix(htlc, 0, INFOPREFIX, "connecting through pipe %s\n", serverstr+1); htlc->fd = pfds[0]; htlc->wfd = pfds[1]; s = htlc->fd; fd_blocking(s, 0); fd_closeonexec(s, 1); hxd_files[s].ready_write = 0; hxd_files[s].ready_read = htlc_read; hxd_files[s].conn.htlc = htlc; hxd_fd_add(s); hxd_fd_set(s, FDR); if (high_fd < s) high_fd = s; s = htlc->wfd; fd_blocking(s, 0); fd_closeonexec(s, 1); hxd_files[s].ready_read = 0; hxd_files[s].ready_write = htlc_write; hxd_files[s].conn.htlc = htlc; hxd_fd_add(s); hxd_fd_set(s, FDW); if (high_fd < s) high_fd = s; goto is_pipe; } #endif #if defined(CONFIG_UNIXSOCKETS) else if (serverstr[0] == '!' && serverstr[1]) { struct sockaddr_un usaddr; s = socket(AF_UNIX, SOCK_STREAM, 0); if (s < 0) { hx_printf_prefix(htlc, 0, INFOPREFIX, "socket: %s\n", strerror(errno)); return; } usaddr.sun_family = AF_UNIX; snprintf(usaddr.sun_path, sizeof(usaddr.sun_path), "%s", serverstr+1); if (htlc->fd) hx_htlc_close(htlc); if (connect(s, (struct sockaddr *)&usaddr, sizeof(usaddr))) { switch (errno) { case EINPROGRESS: break; default: hx_printf_prefix(htlc, 0, INFOPREFIX, "connect: %s\n", strerror(errno)); socket_close(s); return; } } htlc->usockaddr = usaddr; socket_blocking(s, 0); fd_closeonexec(s, 1); goto is_unix; } #endif s = socket(AFINET, SOCK_STREAM, IPPROTO_TCP); if (s < 0) { hx_printf_prefix(htlc, 0, INFOPREFIX, "socket: %s\n", strerror(errno)); return; } if (s >= hxd_open_max) { hx_printf_prefix(htlc, 0, INFOPREFIX, "%s:%d: %d >= hxd_open_max (%d)", __FILE__, __LINE__, s, hxd_open_max); close(s); return; } socket_blocking(s, 0); fd_closeonexec(s, 1); if (hx_hostname[0]) { #ifdef CONFIG_IPV6 if (!inet_pton(AFINET, hx_hostname, &saddr.SIN_ADDR)) { #else if (!inet_aton(hx_hostname, &saddr.SIN_ADDR)) { #endif struct hostent *he; if ((he = gethostbyname(hx_hostname))) { size_t len = (unsigned int)he->h_length > sizeof(struct IN_ADDR) ? sizeof(struct IN_ADDR) : (unsigned int)he->h_length; memcpy(&saddr.SIN_ADDR, he->h_addr, len); } else { #ifndef HAVE_HSTRERROR hx_printf_prefix(htlc, 0, INFOPREFIX, "DNS lookup for %s failed\n", hx_hostname); #else hx_printf_prefix(htlc, 0, INFOPREFIX, "DNS lookup for %s failed: %s\n", hx_hostname, hstrerror(h_errno)); #endif socket_close(s); return; } } saddr.SIN_PORT = 0; saddr.SIN_FAMILY = AFINET; if (bind(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { inaddr2str(abuf, &saddr); hx_printf_prefix(htlc, 0, INFOPREFIX, "bind %s (%s): %s\n", hx_hostname, abuf, strerror(errno)); socket_close(s); return; } } #ifdef CONFIG_IPV6 if (!inet_pton(AFINET, serverstr, &saddr.SIN_ADDR)) { #else if (!inet_aton(serverstr, &saddr.SIN_ADDR)) { #endif struct hostent *he; if ((he = gethostbyname(serverstr))) { size_t len = (unsigned int)he->h_length > sizeof(struct IN_ADDR) ? sizeof(struct IN_ADDR) : (unsigned int)he->h_length; memcpy(&saddr.SIN_ADDR, he->h_addr, len); } else { #ifndef HAVE_HSTRERROR hx_printf_prefix(htlc, 0, INFOPREFIX, "DNS lookup for %s failed\n", serverstr); #else hx_printf_prefix(htlc, 0, INFOPREFIX, "DNS lookup for %s failed: %s\n", serverstr, hstrerror(h_errno)); #endif socket_close(s); return; } } saddr.SIN_PORT = htons(port); saddr.SIN_FAMILY = AFINET; if (htlc->fd) hx_htlc_close(htlc); if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr))) { #if !defined(__WIN32__) switch (errno) { case EINPROGRESS: break; default: hx_printf_prefix(htlc, 0, INFOPREFIX, "connect: %s\n", strerror(errno)); socket_close(s); return; } #else int wsaerr; wsaerr = WSAGetLastError(); if (wsaerr != WSAEWOULDBLOCK) { hx_printf_prefix(htlc, 0, INFOPREFIX, "connect: WSA error %d\n", wsaerr); socket_close(s); return; } #endif } htlc->sockaddr = saddr; inaddr2str(abuf, &saddr); hx_printf_prefix(htlc, 0, INFOPREFIX, "connecting to %s:%u\n", abuf, ntohs(saddr.SIN_PORT)); is_unix: hxd_files[s].ready_read = htlc_read; hxd_files[s].ready_write = htlc_write_connect; hxd_files[s].conn.htlc = htlc; hxd_fd_add(s); hxd_fd_set(s, FDR|FDW); htlc->fd = s; is_pipe: hx_output.status(); if (name) strlcpy(htlc->name, name, sizeof(htlc->name)); if (icon) htlc->icon = icon; if (login) strlcpy(htlc->login, login, sizeof(htlc->login)); if (pass) strlcpy(htlc->password, pass, sizeof(htlc->password)); else htlc->password[0] = 0; htlc->secure = secure; htlc->flags.in_login = 1; htlc->rcv = hx_rcv_magic; htlc->trans = 1; memset(&htlc->in, 0, sizeof(struct qbuf)); memset(&htlc->out, 0, sizeof(struct qbuf)); qbuf_set(&htlc->in, 0, HTLS_MAGIC_LEN); qbuf_add(&htlc->out, HTLC_MAGIC, HTLC_MAGIC_LEN); } void hx_connect_finish (struct htlc_conn *htlc) { char enclogin[64], encpass[64]; char abuf[HOSTLEN+1]; u_int16_t icon16; u_int16_t llen, plen; int secure; u_int8_t *login, *pass; secure = htlc->secure; login = htlc->login; pass = htlc->password; inaddr2str(abuf, &htlc->sockaddr); #ifdef CONFIG_HOPE if (secure) { #ifdef CONFIG_CIPHER u_int8_t cipheralglist[64]; u_int16_t cipheralglistlen; u_int8_t cipherlen; #endif #ifdef CONFIG_COMPRESS u_int8_t compressalglist[64]; u_int16_t compressalglistlen; u_int8_t compresslen; #endif u_int16_t hc; u_int8_t macalglist[64]; u_int16_t macalglistlen; abuf[0] = 0; task_new(htlc, rcv_task_login, htlc, 0, "login"); strcpy(htlc->macalg, "HMAC-SHA1"); S16HTON(2, macalglist); macalglistlen = 2; macalglist[macalglistlen] = 9; macalglistlen++; memcpy(macalglist+macalglistlen, htlc->macalg, 9); macalglistlen += 9; macalglist[macalglistlen] = 8; macalglistlen++; memcpy(macalglist+macalglistlen, "HMAC-MD5", 8); macalglistlen += 8; hc = 4; #ifdef CONFIG_COMPRESS if (htlc->compressalg[0]) { compresslen = strlen(htlc->compressalg); S16HTON(1, compressalglist); compressalglistlen = 2; compressalglist[compressalglistlen] = compresslen; compressalglistlen++; memcpy(compressalglist+compressalglistlen, htlc->compressalg, compresslen); compressalglistlen += compresslen; hc++; } else compressalglistlen = 0; #endif #ifdef CONFIG_CIPHER if (htlc->cipheralg[0]) { cipherlen = strlen(htlc->cipheralg); S16HTON(1, cipheralglist); cipheralglistlen = 2; cipheralglist[cipheralglistlen] = cipherlen; cipheralglistlen++; memcpy(cipheralglist+cipheralglistlen, htlc->cipheralg, cipherlen); cipheralglistlen += cipherlen; hc++; } else cipheralglistlen = 0; #endif hlwrite(htlc, HTLC_HDR_LOGIN, 0, hc, HTLC_DATA_LOGIN, 1, abuf, HTLC_DATA_PASSWORD, 1, abuf, HTLC_DATA_MAC_ALG, macalglistlen, macalglist, #ifdef CONFIG_CIPHER HTLC_DATA_CIPHER_ALG, cipheralglistlen, cipheralglist, #endif #ifdef CONFIG_COMPRESS HTLC_DATA_COMPRESS_ALG, compressalglistlen, compressalglist, #endif HTLC_DATA_SESSIONKEY, 0, 0); return; } #endif /* HOPE */ task_new(htlc, rcv_task_login, 0, 0, "login"); icon16 = htons(htlc->icon); if (login) { llen = strlen(login); if (llen > 32) llen = 32; hl_encode(enclogin, login, llen); } else llen = 0; htlc->clientversion = g_clientversion; if (htlc->clientversion >= 150) { if (pass) { u_int16_t cv = htons(185); plen = strlen(pass); if (plen > 32) plen = 32; hl_encode(encpass, pass, plen); hlwrite(htlc, HTLC_HDR_LOGIN, 0, 3, HTLC_DATA_LOGIN, llen, enclogin, HTLC_DATA_PASSWORD, plen, encpass, HTLC_DATA_CLIENTVERSION, 2, &cv); } else { u_int16_t cv = htons(185); hlwrite(htlc, HTLC_HDR_LOGIN, 0, 2, HTLC_DATA_LOGIN, llen, enclogin, HTLC_DATA_CLIENTVERSION, 2, &cv); } } else { /* 123 */ if (pass) { plen = strlen(pass); if (plen > 32) plen = 32; hl_encode(encpass, pass, plen); hlwrite(htlc, HTLC_HDR_LOGIN, 0, 4, HTLC_DATA_ICON, 2, &icon16, HTLC_DATA_LOGIN, llen, enclogin, HTLC_DATA_PASSWORD, plen, encpass, HTLC_DATA_NAME, strlen(htlc->name), htlc->name); } else { hlwrite(htlc, HTLC_HDR_LOGIN, 0, 3, HTLC_DATA_ICON, 2, &icon16, HTLC_DATA_LOGIN, llen, enclogin, HTLC_DATA_NAME, strlen(htlc->name), htlc->name); } } memset(htlc->password, 0, sizeof(htlc->password)); }