/* * vim: fileencoding=utf8 noexpandtab sw=4 ts=4 */ #ifdef PUMA6_ARM #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" static int set_source_ipv6_addr(int server_fd) { struct sockaddr_storage addr; int ok = 0; FILE *fp = popen("avmipc_state ARM_IPV6_ULA ARM_IPV6_GUA", "r"); if (!fp) return -1; char buf[80]; while(!ok && buf == fgets(buf, sizeof(buf)-1, fp)) { buf[sizeof(buf)-1] = '\0'; char *p = &buf[strlen(buf)-1]; while(p >= buf && (*p == '\r' || *p == '\n')) { *p-- = '\0'; } if (0 == string_to_sockaddr_storage(buf, &addr)) { struct sockaddr_in6 *in6; in6 = &addr; in6->sin6_port = 0; if (0 == bind(server_fd, (struct sockaddr *)in6, sizeof(*in6))) { ok = 1; } else { int errn = errno; Log(("%s bind %s failed with errno=%d", __FUNCTION__, buf, errn)); } } } pclose(fp); return ok ? 0 : -1; } int do_proxy(int client_fd, struct sockaddr_storage *server_addr, struct sockaddr_storage *client_addr) { int server_fd; int state = 0; int ret_val = -1; char out_buffer[2][128]; short out_buffer_size[2] = { 0, 0 }; short input_eof[2] = { 0, 0 }; server_fd = socket(server_addr->ss_family, SOCK_STREAM, 0); if (server_fd < 0) { Log(("%s socket SOCK_STREAM for family=%d failed", __FUNCTION__, server_addr->ss_family)); return -2; } if (fcntl(client_fd, F_SETFL, O_NONBLOCK) != 0) { Log(("%s fcntl O_NONBLOCK for client_fd failed", __FUNCTION__)); goto end; } if (fcntl(server_fd, F_SETFL, O_NONBLOCK) != 0) { Log(("%s fcntl O_NONBLOCK for server_fd failed", __FUNCTION__)); goto end; } // for IPv6 avoid using the WAN IPv6 as source address of proxy // the ATOM ftp server only accepts the ARM ULA or ARM GUA (on 'lan') as proxy source if (AF_INET6 == server_addr->ss_family) { if (0 != set_source_ipv6_addr(server_fd)) { Log(("%s set_source_ipv6_addr failed", __FUNCTION__)); goto end; } } int rc = connect(server_fd, (struct sockaddr *)server_addr, sizeof(*server_addr)); int error = errno; if (0 == rc) { Log(("%s connect connected!", __FUNCTION__)); state = 2; // connected } else { if (EINPROGRESS != error) { // error Log(("%s connect failed rc=%d error=%d", __FUNCTION__, rc, error)); goto end; } state = 1; // connecting Log(("%s connectin progress", __FUNCTION__)); } do { struct pollfd fds[2]; fds[0].fd = client_fd; fds[0].events = 0; if (0 != out_buffer_size[0]) fds[0].events |= POLLOUT; if (!input_eof[0] && state >= 2 && 0 == out_buffer_size[1]) fds[0].events |= POLLIN; fds[0].revents = 0; fds[1].fd = server_fd; fds[1].events = 0; if (state < 2 || 0 != out_buffer_size[1]) fds[1].events |= POLLOUT; if (!input_eof[1] && state >= 2 && 0 == out_buffer_size[0]) fds[1].events |= POLLIN; fds[1].revents = 0; nfds_t nfds = 2; rc = poll(fds, nfds, (state < 2) ? 20000 /* 20 sec connect timeout */ : -1); if (rc < 0) { error = errno; Log(("%s poll failed rc=%d error=%d", __FUNCTION__, rc, error)); goto end; } if (0 == rc && state < 2) { Log(("%s poll timeout", __FUNCTION__)); goto end; // timeout -> connect failed } nfds = rc; int nhandled = 0; int i; for (i = 0; i < 2 && nhandled < nfds; i++) { if (0 == fds[i].revents) continue; nhandled++; if (fds[i].revents & (POLLRDHUP | POLLERR | POLLHUP | POLLNVAL)) { Log(("%s poll errors for i=%d revents=0x%x", __FUNCTION__, i, fds[i].revents)); state = 0; // error break; } if (fds[i].revents & POLLOUT) { if (i == 1 && state < 2) { // server connected - tell the server the addr of the real client char buf[INET6_ADDRSTRLEN+1]; void *addr = 0; switch(client_addr->ss_family) { case AF_INET: addr = (void *)&((struct sockaddr_in *)(client_addr))->sin_addr; break; case AF_INET6: addr = (void *)&((struct sockaddr_in6 *)(client_addr))->sin6_addr; break; } if (addr) inet_ntop(client_addr->ss_family, addr, buf, sizeof(buf)); else buf[0] = '\0'; snprintf(out_buffer[1], sizeof(out_buffer[1])-1, "AVMCLIENT %s\r\n", buf); out_buffer[1][sizeof(out_buffer[1]) - 1] = '\0'; out_buffer_size[1] = strlen(out_buffer[1]); state = 2; Log(("%s connected - sending AVMCLIENT command", __FUNCTION__)); continue; } if (0 != out_buffer_size[i]) { // write out buffer ssize_t n2 = write(fds[i].fd, out_buffer[i], out_buffer_size[i]); if (n2 > 0) { out_buffer_size[i] -= n2; memmove(out_buffer[i], &out_buffer[i][n2], out_buffer_size[i]); } } } if (state < 2) continue; // transfer data if any if ((fds[i].revents & POLLIN) /* && (fds[1-i].revents & POLLOUT) */) { char buf[128]; ssize_t n; short loop = 0; do { n = read(fds[i].fd, buf, sizeof(buf)); if (n > 0) { ssize_t n2 = write(fds[1-i].fd, buf, n); if (-1 == n2) { if (error == EAGAIN || error == EWOULDBLOCK) { // buffer everything memcpy(out_buffer[1-i], buf, n); out_buffer_size[1-i] = n; } else break; // error } else if (n2 != n) { // buffer the rest memcpy(out_buffer[1-i], &buf[n2], n - n2); out_buffer_size[1-i] = n - n2; } } else if (0 == n && 0 == loop) { // eof input_eof[i] = 1; } loop = 1; } while(n > 0); } } } while(state > 0 && !input_eof[0] && !input_eof[1]); Log(("%s loop ended state=%d input_eof[0]=%d input_eof[1]=%d out_buffer_size[0]=%u out_buffer_size[1]=%u", __FUNCTION__, state, input_eof[0], input_eof[1], out_buffer_size[0], out_buffer_size[1])); ret_val = 0; end: close(server_fd); Log(("%s complete ret_val=%d", __FUNCTION__, ret_val)); return ret_val; } #endif // PUMA6_ARM