#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "packet.h" #include "netascii.h" static int stop = 0; void stop_handler() { stop = 1; return; } void usage(char *name) { printf("USAGE: %s -d -u nobody -g nobody -a 127.0.0.1 -p 69\n", name); printf("\td: daemonize.\n"); printf("\tu: username to run as (default: nobody). Must be specified after -d.\n"); printf("\tg: group to run as (default: nobody). Must be specified after -d.\n"); printf("\ta: address to bind to (default: 127.0.0.1)\n"); printf("\tp: port to bind to (default: 69)\n"); } int main(int argc, char **argv) { int daemonize = 0; char addr[16], user[32], group[32]; memset(addr, '\0', sizeof(addr)); memset(user, '\0', sizeof(user)); memset(group, '\0', sizeof(group)); strcpy(addr, "127.0.0.1"); strcpy(user, "nobody"); strcpy(group, "nobody"); int port = 69; int ch = 0; while ((ch = getopt(argc, argv, "da:p:u:g:h")) != -1) { switch (ch) { case 'a': //TODO: this is unsafe strcpy(addr, optarg); break; case 'p': port = atoi(optarg); if ((port <= 0) || (port >= 65536)) { fprintf(stderr, "invalid port specified.\n"); return -1; } break; case 'd': daemonize = 1; break; case 'g': if (daemonize) { //TODO: this is unsafe strcpy(group, optarg); } else { fprintf(stderr, "-g requires -d\n"); return -1; } break; case 'u': if (daemonize) { //TODO: this is unsafe strcpy(user, optarg); } else { fprintf(stderr, "-u requires -d\n"); return -1; } break; case 'h': case '?': usage(argv[0]); return -1; } } setlogmask(LOG_UPTO(LOG_INFO)); openlog(argv[0], LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_FTP); syslog(LOG_INFO, "starting up..."); if (daemonize) { if (daemon(1, 0)) { syslog(LOG_ERR, "failed to daemonize as requested!"); return -1; } else { syslog(LOG_INFO, "daemonized"); } } int s = socket(AF_INET, SOCK_DGRAM, 0); if (s <= 0) { syslog(LOG_ERR, "unable to bind socket!"); return -1; } struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(port); saddr.sin_addr.s_addr = inet_addr(addr); if (bind(s, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) { switch (errno) { case EACCES: syslog(LOG_ERR, "socket bind failed: permission denied"); break; case EADDRINUSE: syslog(LOG_ERR, "socket bind failed: address already in use"); break; case EADDRNOTAVAIL: syslog(LOG_ERR, "socket bind failed: address not available"); break; default: syslog(LOG_ERR, "socket bind failed"); } return -1; } syslog(LOG_INFO, "socket bind success"); //TODO: use getpwnam_r() and getgrnam_r() struct passwd *u = getpwnam((const char *)&user); if (setuid((*u).pw_uid) == -1) { syslog(LOG_ERR, "failed to drop user privileges"); return -1; } struct group *g = getgrnam((const char *)&group); if (setgid((*g).gr_gid) == -1) { syslog(LOG_ERR, "failed to drop group privileges"); return -1; } // create persistent buffer char *buf; buf = malloc(1024); if (buf == NULL) { syslog(LOG_CRIT, "unable to allocate memory!"); close(s); return -1; } while (!stop) { struct sockaddr_in caddr; unsigned int clen = sizeof(caddr); memset(buf, '\0', 1024); if (recvfrom(s, buf, 1024, 0, (struct sockaddr*)&caddr, &clen) < 0) { syslog(LOG_ERR, "got a client connection, but unable to receive data!"); free(buf); continue; } //TODO: process packet // we will never actually join on this thread, and don't care to keep it around. //pthread_t *_thread; // since this hasn't been implemented yet, clang is complaining } // free our persistent buffer free(buf); // close socket close(s); // wait for threads to exit pthread_exit(NULL); // exit program return 0; }