#include #include #include #include #include #include #include #include #include #include #include int main(int argc, char **argv) { if (argc < 4) { fprintf(stderr, "Usage:\n"); fprintf(stderr, " TCP mode: %s
[args...]\n", argv[0]); fprintf(stderr, " UDP mode: %s --udp [args...]\n", argv[0]); exit(EXIT_FAILURE); } // Check for UDP mode if (strcmp(argv[1], "--udp") == 0) { // UDP mode: argv[2] is the port, argv[3] is the command int port = atoi(argv[2]); // Create UDP socket int udp_sock; if ((udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("UDP socket creation failed"); exit(EXIT_FAILURE); } int opt = 1; if (setsockopt(udp_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("setsockopt SO_REUSEADDR failed"); close(udp_sock); exit(EXIT_FAILURE); } struct sockaddr_in udp_addr; memset(&udp_addr, 0, sizeof(udp_addr)); udp_addr.sin_family = AF_INET; udp_addr.sin_addr.s_addr = INADDR_ANY; // Bind to 0.0.0.0 udp_addr.sin_port = htons(port); if (bind(udp_sock, (struct sockaddr *)&udp_addr, sizeof(udp_addr)) < 0) { perror("UDP bind failed"); close(udp_sock); exit(EXIT_FAILURE); } fprintf(stderr, "Listening for UDP packets on 0.0.0.0:%d\n", port); // Create pipes for redirecting child's stdin and capturing child's stdout. // pipe_in: parent writes UDP data -> child's stdin. // pipe_out: child writes stdout -> parent reads and prints. int pipe_in[2], pipe_out[2]; if (pipe(pipe_in) < 0) { perror("pipe for stdin failed"); close(udp_sock); exit(EXIT_FAILURE); } if (pipe(pipe_out) < 0) { perror("pipe for stdout failed"); close(udp_sock); exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid < 0) { perror("fork failed"); close(udp_sock); exit(EXIT_FAILURE); } else if (pid == 0) { // Child process // Close the parent's ends of the pipes. close(pipe_in[1]); close(pipe_out[0]); // Replace child's stdin with pipe_in[0] if (dup2(pipe_in[0], STDIN_FILENO) < 0) { perror("dup2 for stdin failed"); exit(EXIT_FAILURE); } // Replace child's stdout with pipe_out[1] if (dup2(pipe_out[1], STDOUT_FILENO) < 0) { perror("dup2 for stdout failed"); exit(EXIT_FAILURE); } // Optionally also send stderr to stdout. dup2(pipe_out[1], STDERR_FILENO); // Close the now duplicated descriptors. close(pipe_in[0]); close(pipe_out[1]); // Execute the requested command. // In UDP mode, the command starts at argv[3]. execvp(argv[3], &argv[3]); perror("execvp failed"); exit(EXIT_FAILURE); } // Parent process: close the child's ends of the pipes. close(pipe_in[0]); close(pipe_out[1]); // Use select() to multiplex between the UDP socket and the child's output. fd_set readfds; int maxfd = (udp_sock > pipe_out[0]) ? udp_sock : pipe_out[0]; while (1) { FD_ZERO(&readfds); FD_SET(udp_sock, &readfds); FD_SET(pipe_out[0], &readfds); int ret = select(maxfd + 1, &readfds, NULL, NULL, NULL); if (ret < 0) { if (errno == EINTR) continue; perror("select failed"); break; } // Check for incoming UDP packets. if (FD_ISSET(udp_sock, &readfds)) { char buffer[4096]; struct sockaddr_in sender_addr; socklen_t sender_len = sizeof(sender_addr); ssize_t recv_len = recvfrom(udp_sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&sender_addr, &sender_len); if (recv_len < 0) { perror("recvfrom failed"); } else { // Forward the UDP data to the child's stdin. ssize_t written = write(pipe_in[1], buffer, recv_len); if (written < 0) { perror("write to child's stdin failed"); } } } // Check if there is output from the child. if (FD_ISSET(pipe_out[0], &readfds)) { char outbuf[4096]; ssize_t count = read(pipe_out[0], outbuf, sizeof(outbuf)); if (count < 0) { perror("read from child's stdout failed"); } else if (count == 0) { // End-of-file: child closed its stdout. break; } else { // Print child's output to stdout. if (write(STDOUT_FILENO, outbuf, count) < 0) { perror("write to stdout failed"); } } } } // Cleanup: close file descriptors and wait for child termination. close(pipe_in[1]); close(pipe_out[0]); close(udp_sock); wait(NULL); exit(EXIT_SUCCESS); } else { // TCP mode: Expect arguments:
[args...] if (argc < 4) { fprintf(stderr, "Usage: %s
[args...]\n", argv[0]); exit(EXIT_FAILURE); } int port = atoi(argv[1]); int sock_fd, conn_fd; struct sockaddr_in server_addr, peer_addr; socklen_t peer_addr_len = sizeof(peer_addr); if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } int opt = 1; setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(argv[2]); server_addr.sin_port = htons(port); if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("bind failed"); close(sock_fd); exit(EXIT_FAILURE); } if (listen(sock_fd, 1) < 0) { perror("listen failed"); close(sock_fd); exit(EXIT_FAILURE); } fprintf(stderr, "Waiting for connection on %s:%d\n", argv[2], port); if ((conn_fd = accept(sock_fd, (struct sockaddr *)&peer_addr, &peer_addr_len)) < 0) { perror("accept failed"); exit(EXIT_FAILURE); } fprintf(stderr, "Connection accepted\n"); close(sock_fd); // Replace stdin and stdout with the accepted connection. dup2(conn_fd, STDIN_FILENO); dup2(conn_fd, STDOUT_FILENO); close(conn_fd); execvp(argv[3], argv + 3); perror("execvp failed"); exit(EXIT_FAILURE); } }