/* * Copyright (c) 2014, Franck Cuny * * Permission to use, copy, modify, and/or distribute this software for any purpose * with or without fee is hereby granted, provided that the above copyright notice * and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF * THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include struct settings { int verbose; int localport; int daemon; char *remoteport; char *tag; char *remotehost; char *template; }; struct peersock { int socket; socklen_t salen; struct sockaddr *sa; }; struct settings settings; static void settings_init() { settings.verbose = 0; settings.daemon = 0; settings.localport = 8126; settings.remoteport = "8125"; settings.remotehost = NULL; settings.tag = "PROD"; settings.template = "%s.hosts.%s."; } static void usage(char *prog) { printf("usage: %s [option]\n\n", prog); printf("-p local UDP port to listen on (default: %d)\n", settings.localport); printf("-P remote UDP port to send to (default: %s)\n", settings.remoteport); printf("-H remote host to send to (required)\n"); printf("-t tag to use (default: %s)\n", settings.tag); printf("-T template to apply to the received line (default :%s)\n", settings.template); printf("-h print this help and exit\n"); printf("-d run as a daemon\n"); printf("--verbose verbose\n"); return; } static int localsock(int port) { int sock; struct sockaddr_in servaddr; if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return -1; } memset((char *)&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(port); if(bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { perror("bind"); return -1; } return sock; } static int get_peersock(struct peersock *remote, char *host, char *port) { int sock, err; struct addrinfo hints, *result, *rp; struct sockaddr *sa; socklen_t salen; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; if ((err = getaddrinfo(host, port, &hints, &result)) != 0) { perror("getaddrinfo"); freeaddrinfo(result); return -1; } for (rp = result; rp != NULL; rp = rp->ai_next) { sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sock >= 0) { break; } } sa = malloc(result->ai_addrlen); memcpy(sa, result->ai_addr, result->ai_addrlen); salen = result->ai_addrlen; remote->socket = sock; remote->salen = salen; remote->sa = sa; freeaddrinfo(result); return 0; } static int daemonize() { switch(fork()) { case -1: return -1; case 0: break; default: exit(EXIT_SUCCESS); } if (settings.verbose == 1) { fprintf(stdout, "Daemon is starting ...\n"); } if (setsid() == -1) { return -1; } if(chdir("/") != 0) { perror("chdir"); return -1; } int fd; if ((fd = open("/dev/null", O_RDWR, 0)) != -1) { if(dup2(fd, STDIN_FILENO) < 0) { perror("dup2 stdin"); return (-1); } if(dup2(fd, STDOUT_FILENO) < 0) { perror("dup2 stdout"); return (-1); } if(dup2(fd, STDERR_FILENO) < 0) { perror("dup2 stderr"); return (-1); } if (fd > STDERR_FILENO) { if(close(fd) < 0) { perror("close"); return (-1); } } } return 0; } static char *getlinecontent(char mesg[], char template[], int ii, int start) { char *line; size_t len; len = strlen(template) + (ii - start) + 1; line = (char *)malloc(len); memcpy(line, template, strlen(template) + 1); strncat(line, &mesg[start], (ii - start)); line[len - 1] = '\0'; return (char *)line; } static void proxy_line(struct peersock *p_socket, char mesg[], char template[], int ii, int start) { char *line; line = getlinecontent(mesg, template, ii, start); if (settings.verbose == 1) { fprintf(stdout, "Forwarding %s\n", line); } sendto(p_socket->socket, line, strlen(line), 0, p_socket->sa, p_socket->salen); free(line); return; } int main(int argc, char *argv[]) { static struct option long_options[] = { {"local-port", required_argument, NULL, 'p'}, {"remote-port", required_argument, NULL, 'P'}, {"remote-host", required_argument, NULL, 'H'}, {"tag", required_argument, NULL, 't'}, {"template", required_argument, NULL, 'T'}, {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'V'}, {"daemon", no_argument, NULL, 'd'}, { NULL, 0, NULL, 0 } }; settings_init(); while (1) { int option_index = 0; int c = getopt_long(argc, argv, "p:P:H:t:T:hvd", long_options, &option_index); if (c == -1) break; switch(c) { case 'h': usage(argv[0]); exit(EXIT_SUCCESS); case 'd': settings.daemon = 1; break; case 'p': settings.localport = atoi(optarg); break; case 't': settings.tag = optarg; break; case 'H': settings.remotehost = optarg; break; case 'P': settings.remoteport = optarg; break; case 'T': settings.template = optarg; break; case 'V': settings.verbose = 1; break; default: exit(EXIT_FAILURE); } } if (settings.remotehost == NULL) { usage(argv[0]); exit(EXIT_FAILURE); } char hostname[255]; int l_socket; struct peersock *p_socket = malloc(sizeof(struct peersock)); if (gethostname(hostname, sizeof(hostname) - 1) == -1) { if (settings.verbose) { fprintf(stderr, "Error while discovering hostname.\n"); } hostname[0] = '\0'; } if ((l_socket = localsock(settings.localport)) == -1) { exit(EXIT_FAILURE); } if ((get_peersock(p_socket, settings.remotehost, settings.remoteport)) == -1) { fprintf(stderr, "Failed to created a socket to remote host.\n"); exit(EXIT_FAILURE); } if (settings.daemon) { if ((daemonize()) == -1) { fprintf(stderr, "Failed to daemonize()\n"); exit(EXIT_FAILURE); } } char mesg[64 * 1024]; char template[255]; int n; sprintf(template, settings.template, settings.tag, hostname); while((n = recvfrom(l_socket, mesg, sizeof(mesg), 0, NULL, NULL))) { mesg[n] = '\0'; size_t start = 0; int ii; for (ii = 0; ii < n; ii++) { if (mesg[ii] == '\n') { proxy_line(p_socket, mesg, template, ii, start); start = ii + 1; } } if ((ii - start) > 0) { proxy_line(p_socket, mesg, template, ii, start); } } close(l_socket); close(p_socket->socket); free(p_socket); return EXIT_SUCCESS; }