int socket_udp(const char *host, int port, void *ud) { struct socket_req req; int fd; int family; if (port != 0 || host != 0) { fd = _socket_bind(host, port, IPPROTO_UDP, &family); if (fd < 0) { return -1; } } else { family = AF_INET; fd = socket(family, SOCK_DGRAM, 0); if (fd < 0) { return -1; } } socket_nonblocking(fd); memset(&req, 0, sizeof req); req.req = SOCKET_REQ_UDP; req.u.udp.id = socket_next_id(); req.u.udp.fd = fd; req.u.udp.ud = ud; req.u.udp.family = family; socket_send_req(&req); return req.u.udp.id; }
static int socket_req_bind(struct _bind_req *req, struct socket_message *msg) { struct socket *sock; msg->id = req->id; msg->ud = req->ud; msg->size = 0; sock = socket_new(req->fd, req->id, PROTOCOL_TCP, req->ud, 1); if (sock == 0) { msg->data = "socket limit"; return SOCKET_ERR; } socket_nonblocking(req->fd); sock->type = SOCKET_TYPE_BIND; msg->data = "binding"; return SOCKET_OPEN; }
/* * 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; }
static int socket_try_accept(struct socket *sock, struct socket_message *msg) { union sockaddr_all u; socklen_t len = sizeof(u); struct socket *newsock; void * sin_addr; int sin_port; int client_fd, id; client_fd = accept(sock->fd, &u.s, &len); if (client_fd < 0) { if (errno == EMFILE || errno == ENFILE) { msg->id = sock->id; msg->ud = sock->ud; msg->data = strerror(errno); return SOCKET_ERR; } else { return -1; } } id = socket_next_id(); if (id < 0) { close(client_fd); return -1; } socket_keepalive(client_fd); socket_nonblocking(client_fd); newsock = socket_new(client_fd, id, PROTOCOL_TCP, sock->ud, 0); if (newsock == 0) { close(client_fd); return -1; } newsock->type = SOCKET_TYPE_PACCEPT; msg->size = newsock->id; msg->data = 0; sin_addr = (u.s.sa_family == AF_INET) ? (void*)&u.v4.sin_addr : (void *)&u.v6.sin6_addr; sin_port = ntohs((u.s.sa_family == AF_INET) ? u.v4.sin_port : u.v6.sin6_port); char tmp[INET6_ADDRSTRLEN]; if (inet_ntop(u.s.sa_family, sin_addr, tmp, sizeof(tmp))) { snprintf(S.buffer, sizeof(S.buffer), "%s:%d", tmp, sin_port); msg->data = S.buffer; } return SOCKET_ACCEPT; }
/* * 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; }
static int socket_req_open(struct _open_req *req, struct socket_message *msg) { struct socket *sock; int status; int fd = -1; struct addrinfo ai_hints; struct addrinfo *ai_list = 0; struct addrinfo *ai_ptr = 0; char port[16]; msg->id = req->id; msg->ud = req->ud; sprintf(port, "%d", req->port); memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_UNSPEC; ai_hints.ai_socktype = SOCK_STREAM; ai_hints.ai_protocol = IPPROTO_TCP; status = getaddrinfo(req->host, port, &ai_hints, &ai_list); if (status != 0) { msg->data = (void *)gai_strerror(status); goto _failed; } for (ai_ptr = ai_list; ai_ptr != 0; ai_ptr = ai_ptr->ai_next) { fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); if (fd < 0) { continue; } socket_keepalive(fd); socket_nonblocking(fd); status = connect(fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen); if (status != 0 && errno != EINPROGRESS) { close(fd); fd = -1; continue; } break; } if (fd < 0) { msg->data = strerror(errno); goto _failed; } sock = socket_new(fd, req->id, PROTOCOL_TCP, req->ud, 1); if (sock == 0) { close(fd); msg->data = "socket limit"; goto _failed; } if (status == 0) { sock->type = SOCKET_TYPE_OPENED; struct sockaddr *addr = ai_ptr->ai_addr; void *sin_addr = (ai_ptr->ai_family == AF_INET) ? (void *)&((struct sockaddr_in *)addr)->sin_addr : (void *)&((struct sockaddr_in6 *)addr)->sin6_addr; int sin_port = ntohs((ai_ptr->ai_family == AF_INET) ? ((struct sockaddr_in *)addr)->sin_port : ((struct sockaddr_in6 *)addr)->sin6_port); char tmp[INET6_ADDRSTRLEN]; if (inet_ntop(ai_ptr->ai_family, sin_addr, tmp, sizeof(tmp))) { snprintf(S.buffer, sizeof(S.buffer), "%s:%d", tmp, sin_port); msg->data = S.buffer; } freeaddrinfo(ai_list); return SOCKET_OPEN; } else { sock->type = SOCKET_TYPE_OPENING; event_write(S.event_fd, sock->fd, sock, 1); } freeaddrinfo(ai_list); return -1; _failed: freeaddrinfo(ai_list); S.slot[HASH_ID(req->id)].type = SOCKET_TYPE_INVALID; return SOCKET_ERR; }
/* * 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); }
/* * 主进程调用 * hp add 2012/10/18 */ int open_listening_sockets( config_t *pconfig ) { int i, opt=1; int fd; struct sockaddr_in addr; listenfd_cnt = pconfig->bindcnt; for ( i = 0; i < listenfd_cnt; ++ i ) { if ( strlen( pconfig->ips[i] ) > 15 ) { //ipv6 //... } else { //ipv4 fd = socket( AF_INET, SOCK_STREAM, 0 ); if ( fd < 0 ) { log_message( LOG_ERROR, "socket error:%s.", strerror(errno) ); return -1; } if ( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int) ) < 0 ) { log_message( LOG_ERROR, "setsockopt SO_REUSEADDR error:%s.", strerror(errno) ); close( fd ); return -1; } if ( socket_nonblocking(fd) < 0 ) { log_message( LOG_ERROR, "socket_nonblocking error." ); close( fd ); return -1; } bzero( &addr, sizeof(addr) ); addr.sin_family = AF_INET; addr.sin_port = htons( SHELLINABOXPORT ); if ( inet_pton( AF_INET, pconfig->ips[i], &addr.sin_addr ) < 1 ) { log_message( LOG_ERROR, "inet_pton %s error:%s.", pconfig->ips[i], strerror(errno) ); close( fd ); return -1; } if ( bind( fd, (struct sockaddr*)&addr, sizeof(addr) ) < 0 ) { log_message( LOG_ERROR, "bind error:%s.", strerror(errno) ); close( fd ); return -1; } if ( listen( fd, MAXLISTEN ) < 0 ) { log_message( LOG_ERROR, "listen error:%s.", strerror(errno) ); close( fd ); return -1; } listenfds[i].fd = fd; log_message( LOG_DEBUG, "create listen socket:[%s:%d]", pconfig->ips[i], SHELLINABOXPORT ); //listenfds[i].addr = (struct sockaddr*)safemalloc( sizeof(addr) ); memcpy( &listenfds[i].addr, &addr, sizeof(addr) ); listenfds[i].addrlen = sizeof(addr); } } return TRUE; }