diff options
author | Ren Kararou <[email protected]> | 2025-01-05 02:29:15 -0600 |
---|---|---|
committer | Ren Kararou <[email protected]> | 2025-01-05 02:29:15 -0600 |
commit | 5605a5ddf3c77232b04c002a82b91e227c0f27da (patch) | |
tree | 30d5cf75759aa4bd2e8e75d5a7c852931e114066 | |
parent | 70a46fab050c3fdf38b9c72453f6e678cc8341be (diff) | |
download | nbtpd-5605a5ddf3c77232b04c002a82b91e227c0f27da.tar.gz nbtpd-5605a5ddf3c77232b04c002a82b91e227c0f27da.tar.bz2 nbtpd-5605a5ddf3c77232b04c002a82b91e227c0f27da.zip |
builds on illumos, freebsd, and linux
-rw-r--r-- | inc/handlers.h | 7 | ||||
-rw-r--r-- | inc/netascii.h | 3 | ||||
-rw-r--r-- | makefile | 8 | ||||
-rw-r--r-- | src/handlers.c | 140 | ||||
-rw-r--r-- | src/main.c | 211 | ||||
-rw-r--r-- | src/netascii.c | 3 |
6 files changed, 246 insertions, 126 deletions
diff --git a/inc/handlers.h b/inc/handlers.h index c174b6a..6be05da 100644 --- a/inc/handlers.h +++ b/inc/handlers.h @@ -4,9 +4,12 @@ #include <netinet/in.h> #include "packet.h" +#define NBD_NBTPD_ARGS_PATH_MAX 768 +#define NBD_NBTPD_ARGS_MODE_MAX 32 + typedef struct { - char path[768]; - char mode[32]; + char path[NBD_NBTPD_ARGS_PATH_MAX]; + char mode[NBD_NBTPD_ARGS_MODE_MAX]; nbd_tftp_ecode err; struct sockaddr_in client; } nbd_nbtpd_args; diff --git a/inc/netascii.h b/inc/netascii.h index ada2051..ae80bae 100644 --- a/inc/netascii.h +++ b/inc/netascii.h @@ -1,9 +1,10 @@ #ifndef NBD_NETASCII_H #define NBD_NETASCII_H #include <stdint.h> +#include <stdlib.h> uint8_t is_netascii_char(char c); uint8_t is_netascii_str(char *str); -uint8_t is_netascii_buf(char *buf, uint64_t len); +uint8_t is_netascii_buf(char *buf, size_t len); #endif diff --git a/makefile b/makefile index 696ac71..c7d9caf 100644 --- a/makefile +++ b/makefile @@ -2,6 +2,14 @@ CC:=clang CFLAGS:=-march=native -O3 -funroll-loops -Wall -Wextra -Werror LDFLAGS:=-flto=thin -lpthread +ifeq ($(shell uname),SunOS) +CC = gcc +LDFLAGS += -lsocket +CFLAGS += -std=gnu11 +else +CFLAGS += -std=c11 +endif + INCLUDES=-Iinc/ OBJECTS=obj/main.o obj/packet.o obj/netascii.o obj/handlers.o diff --git a/src/handlers.c b/src/handlers.c index 7c2b768..10599a3 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1,8 +1,19 @@ +#ifdef __linux__ +#define _POSIX_C_SOURCE 200809L +#define _DEFAULT_SOURCE +#endif + #include <stdlib.h> #include <stdio.h> #include <sys/socket.h> #include <string.h> #include <syslog.h> +#include <limits.h> +#include <errno.h> +#include <unistd.h> +#ifdef __FreeBSD__ +#include <sys/param.h> +#endif #include "handlers.h" #include "packet.h" @@ -18,11 +29,11 @@ typedef enum opmode { nbd_opmode get_mode(char *mode) { nbd_opmode opmode; - if (strncmp((const char *)mode, (const char *)&"netascii", 32) == 0) { + if (strncmp((const char *)mode, (const char *)&"netascii", NBD_NBTPD_ARGS_MODE_MAX) == 0) { opmode = NETASCII; - } else if (strncmp((const char *)mode, (const char *)&"octal", 32) == 0) { + } else if (strncmp((const char *)mode, (const char *)&"octal", NBD_NBTPD_ARGS_MODE_MAX) == 0) { opmode = OCTAL; - } else if (strncmp((const char *)mode, (const char *)&"mail", 32) == 0) { + } else if (strncmp((const char *)mode, (const char *)&"mail", NBD_NBTPD_ARGS_MODE_MAX) == 0) { opmode = MAIL; } else { opmode = NOT_FOUND; @@ -30,83 +41,96 @@ nbd_opmode get_mode(char *mode) { return opmode; } -char *add_dotslash(char *fname) { - char *ret = malloc(768); - if (ret == NULL) { - return ret; +void *read_req_resp(void *args) { + char *fname = NULL, *wd = NULL, *buf = NULL; + FILE *fp = NULL; + nbd_nbtpd_args *argptr = (nbd_nbtpd_args *)args; + if (!is_netascii_str((char *)&argptr->path)) { + argptr->err = 1; + goto nbd_nbtpd_thread_read_cleanup_ps; } - memset(ret, '\0', 768); - ret[0] = '.'; - ret[1] = '/'; - for (int i = 0; (fname[i] != 0) && (i < 768); i++) { - ret[i + 2] = fname[i]; + fname = realpath((char *)&argptr->path, NULL); + if (fname == NULL) { + syslog(LOG_ERR, "unable to get real path: %s", strerror(errno)); + argptr->err = 1; + goto nbd_nbtpd_thread_read_cleanup_ps; } - return ret; -} - -void *read_req_resp(void *args) { - char fname[768]; - memset(fname, '\0', sizeof(fname)); - int dots = 0; - for (int i = 0; ((*(nbd_nbtpd_args *)args).path[i] != 0) && (i < 768); i++) { - if (is_netascii_char((*(nbd_nbtpd_args *)args).path[i])) { - if (i > 0) { - if ((*(nbd_nbtpd_args *)args).path[i] == '.' && - (*(nbd_nbtpd_args *)args).path[i - 1] == '.') { - dots++; - continue; - } - } - fname[i - dots] = (*(nbd_nbtpd_args *)args).path[i]; - } else { - (*(nbd_nbtpd_args *)args).err = 1; - return nbd_nbtpd_resp_error(args); - } + /* + * POSIX does not define what happens when getcwd() is given NULL. + * This means that illumos libc and GNU libc both return a newly + * malloc()'d string, while FreeBSD libc does not. Also, FreeBSD uses + * constant MAXPATHLEN defined in <sys/param.h> rather than PATH_MAX. + */ +#ifdef __FreeBSD__ + wd = malloc(MAXPATHLEN); + if (wd == NULL) { + syslog(LOG_ERR, "unable to allocate PATH_MAX memory: %s", strerror(errno)); + argptr->err = 0; + goto nbd_nbtpd_thread_read_cleanup_ps; + } + if (getcwd(wd, MAXPATHLEN) == NULL) { + argptr->err = 0; + goto nbd_nbtpd_thread_read_cleanup_ps; + } +#else + if ((wd = getcwd(NULL, PATH_MAX)) == NULL) { + argptr->err = 0; + goto nbd_nbtpd_thread_read_cleanup_ps; } - char mode[32]; - memset(mode, '\0', sizeof(mode)); - for (int i = 0; ((*(nbd_nbtpd_args *)args).mode[i] != 0) && (i < 32); i++) { - if (is_netascii_char((*(nbd_nbtpd_args *)args).mode[i])) { - mode[i] = (*(nbd_nbtpd_args *)args).mode[i]; - } else { - (*(nbd_nbtpd_args *)args).err = 4; - return nbd_nbtpd_resp_error(args); - } +#endif + if (!strncmp(wd, fname, sizeof(wd) - 1)) { + argptr->err = 2; + goto nbd_nbtpd_thread_read_cleanup_ps; } - FILE *fp; - nbd_opmode opmode = get_mode((char *)&mode); + if (!is_netascii_str((char *)&argptr->mode)) { + argptr->err = 4; + goto nbd_nbtpd_thread_read_cleanup_ps; + } + nbd_opmode opmode = get_mode((char *)&argptr->mode); switch (opmode) { case NETASCII: break; case OCTAL: break; default: - (*(nbd_nbtpd_args *)args).err = 4; - return nbd_nbtpd_resp_error(args); + argptr->err = 4; + goto nbd_nbtpd_thread_read_cleanup_ps; } - fp = fopen(add_dotslash((char *)&fname), "r"); + fp = fopen(fname, "r"); - char *buf = malloc(512); + buf = malloc(512); if (buf == NULL) { - syslog(LOG_CRIT, "we failed to allocated 512 bytes of memory"); - (*(nbd_nbtpd_args *)args).err = 0; - return nbd_nbtpd_resp_error(args); + syslog( + LOG_CRIT, + "failed to allocate memory: %s", + strerror(errno) + ); + argptr->err = 0; + goto nbd_nbtpd_thread_read_cleanup_ps; } memset(buf, '\0', 512); int s = socket(AF_INET, SOCK_DGRAM, 0); if (s <= 0) { - syslog(LOG_ERR, "unable to define socket!"); - fclose(fp); - free(buf); - free(args); - return (void *)NULL; + syslog(LOG_ERR, "unable to define socket: %s", strerror(errno)); + goto nbd_nbtpd_thread_read_cleanup; } - - //TODO: make new socket and go into main loop + + close(s); +nbd_nbtpd_thread_read_cleanup: fclose(fp); free(buf); + free(fname); + free(wd); free(args); return (void *)NULL; +nbd_nbtpd_thread_read_cleanup_ps: + if (fp != NULL) { + fclose(fp); + } + free(buf); + free(fname); + free(wd); + return nbd_nbtpd_resp_error(args); } void *write_req_resp(void *args) { diff --git a/src/main.c b/src/main.c index 302f6bf..31d8aec 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,8 @@ +#ifdef __linux__ +#define _POSIX_C_SOURCE 2 +#define _DEFAULT_SOURCE +#endif + #include <unistd.h> #include <stdlib.h> #include <stdint.h> @@ -8,13 +13,19 @@ #include <arpa/inet.h> #include <netinet/in.h> #include <netdb.h> -#include <pthread.h> #include <sys/types.h> #include <sys/time.h> #include <pwd.h> #include <grp.h> #include <errno.h> #include <signal.h> +//TODO: use threads.h instead +#include <pthread.h> + +// illumos-only headers +#ifdef __illumos__ +#include <priv.h> +#endif #include "packet.h" #include "netascii.h" @@ -23,7 +34,7 @@ int nbtpd_stop = 0; void stop_handler(int i) { - syslog(LOG_ERR, "caught shutdown signal, cleaning up."); + syslog(LOG_ERR, "caught shutdown signal, cleaning up and awaiting threads"); if (i != 0) { nbtpd_stop = i; } else { @@ -33,17 +44,65 @@ void stop_handler(int i) { } 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"); + //TODO: print to stderr; cleanup to be compliant + fprintf(stderr, + "usage: %s -h [-d [-u USER] [-g GROUP]] [-a INET_ADDRESS] [-p PORT] [-l LEVEL]\n", + name + ); + fprintf(stderr, "\td: daemonize.\n"); + fprintf(stderr, + "\tu: username to run as (default: nobody). Must be specified after -d.\n" + ); + fprintf(stderr, + "\tg: group to run as (default: nobody). Must be specified after -d.\n" + ); + fprintf(stderr, "\ta: address to bind to (default: 127.0.0.1)\n"); + fprintf(stderr, "\tp: port to bind to (default: 69)\n"); + fprintf(stderr, "\tl: log level; one of:\n"); + fprintf(stderr, "\t\temerg\n\t\talert\n\t\tcrit\n\t\terr\n\t\twarn\n"); + fprintf(stderr, "\t\tnotice\n\t\tinfo (default)\n\t\tdebug\n"); +} + +int set_loglevel(char *level) { + if (strncmp("emerg", level, 6) != 0) { + setlogmask(LOG_UPTO(LOG_EMERG)); + return 0; + } + if (strncmp("alert", level, 6) != 0) { + setlogmask(LOG_UPTO(LOG_ALERT)); + return 0; + } + if (strncmp("crit", level, 5) != 0) { + setlogmask(LOG_UPTO(LOG_CRIT)); + return 0; + } + if (strncmp("err", level, 4) != 0) { + setlogmask(LOG_UPTO(LOG_ERR)); + return 0; + } + if (strncmp("warn", level, 5) != 0) { + setlogmask(LOG_UPTO(LOG_WARNING)); + return 0; + } + if (strncmp("notice", level, 7) != 0) { + setlogmask(LOG_UPTO(LOG_NOTICE)); + return 0; + } + if (strncmp("info", level, 5) != 0) { + setlogmask(LOG_UPTO(LOG_INFO)); + return 0; + } + if (strncmp("debug", level, 6) != 0) { + setlogmask(LOG_UPTO(LOG_DEBUG)); + return 0; + } + return -1; } int main(int argc, char **argv) { - int daemonize = 0; - char addr[16], user[32], group[32]; + int retme = 0; + int daemonize = 0, loglevset = 0; + char addr[17], user[32], group[32]; memset(addr, '\0', sizeof(addr)); memset(user, '\0', sizeof(user)); memset(group, '\0', sizeof(group)); @@ -51,13 +110,11 @@ int main(int argc, char **argv) { strcpy(user, "nobody"); strcpy(group, "nobody"); int port = 69; - int ch = 0; - while ((ch = getopt(argc, argv, "da:p:u:g:h")) != -1) { + while ((ch = getopt(argc, argv, "da:p:u:g:l:h")) != -1) { switch (ch) { case 'a': - //TODO: this is unsafe - strcpy(addr, optarg); + strncpy(addr, optarg, sizeof(addr) - 1); break; case 'p': port = atoi(optarg); @@ -71,8 +128,7 @@ int main(int argc, char **argv) { break; case 'g': if (daemonize) { - //TODO: this is unsafe - strcpy(group, optarg); + strncpy(group, optarg, sizeof(group) - 1); } else { fprintf(stderr, "-g requires -d\n"); return -1; @@ -80,116 +136,143 @@ int main(int argc, char **argv) { break; case 'u': if (daemonize) { - //TODO: this is unsafe - strcpy(user, optarg); + strncpy(user, optarg, sizeof(user) - 1); } else { fprintf(stderr, "-u requires -d\n"); return -1; } break; + case 'l': + loglevset = 1; + if (!set_loglevel(optarg)) { + fprintf(stderr, "invalid option for -l\n"); + usage(argv[0]); + return -1; + } + break; case 'h': case '?': usage(argv[0]); return -1; } } - - setlogmask(LOG_UPTO(LOG_INFO)); + if (!loglevset) { + setlogmask(LOG_UPTO(LOG_INFO)); + } +#ifdef __illumos__ + openlog(argv[0], LOG_PID | LOG_NDELAY, LOG_FTP); +#else openlog(argv[0], LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_FTP); +#endif syslog(LOG_INFO, "starting up..."); signal(SIGTERM, &stop_handler); signal(SIGINT, &stop_handler); - + char *buf = NULL; + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s <= 0) { + syslog(LOG_ERR, "unable to bind socket!"); + return -1; + } if (daemonize) { if (daemon(1, 0)) { - syslog(LOG_ERR, "failed to daemonize as requested!"); - return -1; + syslog(LOG_ERR, "failed to daemonize: %s", strerror(errno)); + retme = -1; + goto nbd_nbtpd_cleanup_main; } 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 timeval timeout = { 1, 0 }; if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) { syslog(LOG_ERR, "unable to set socket timeout: %s", strerror(errno)); - close(s); - return -1; + retme = -1; + goto nbd_nbtpd_cleanup_main; } 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) { syslog(LOG_ERR, "socket bind failed: %s", strerror(errno)); - return -1; + retme = -1; + goto nbd_nbtpd_cleanup_main; } syslog(LOG_INFO, "socket bind success"); - if (daemonize) { //TODO: use getpwnam_r() and getgrnam_r() struct group *g = getgrnam((const char *)&group); if (setgid((*g).gr_gid) == -1) { - syslog(LOG_ERR, "failed to drop group privileges"); - return -1; + syslog( + LOG_ERR, + "failed to drop group privileges: %s", + strerror(errno) + ); + retme = -1; + goto nbd_nbtpd_cleanup_main; } struct passwd *u = getpwnam((const char *)&user); if (setuid((*u).pw_uid) == -1) { - syslog(LOG_ERR, "failed to drop user privileges"); - return -1; + syslog( + LOG_ERR, + "failed to drop user privileges: %s", + strerror(errno) + ); + retme = -1; + goto nbd_nbtpd_cleanup_main; } } - - // create persistent buffer - char *buf; buf = malloc(1024); if (buf == NULL) { - syslog(LOG_CRIT, "unable to allocate memory!"); - close(s); - return -1; + syslog(LOG_CRIT, "unable to allocate memory: %s", strerror(errno)); + retme = -1; + goto nbd_nbtpd_cleanup_main; } while (!nbtpd_stop) { struct sockaddr_in caddr; unsigned int clen = sizeof(caddr); memset(buf, '\0', 1024); ssize_t recvd_bytes = 0; - if ((recvd_bytes = recvfrom(s, buf, 1024, 0, + if ((recvd_bytes = recvfrom(s, buf, 1024, 0, (struct sockaddr *)&caddr, &clen)) < 0) { continue; } if (recvd_bytes < 4) { - syslog(LOG_ERR, "ignoring invalid packet."); + syslog( + LOG_ERR, + "ignoring invalid packet from %s.", + inet_ntoa(caddr.sin_addr) + ); continue; } nbd_nbtpd_args *args = malloc(sizeof(nbd_nbtpd_args)); if (args == NULL) { - syslog(LOG_CRIT, "unable to allocate memory"); + syslog( + LOG_CRIT, + "unable to allocate memory: %s", + strerror(errno) + ); continue; } nbd_tftp_opcode oc = ntohs(((uint16_t)buf[0] << 8) + buf[1]); - (*args).client = caddr; - (*args).err = 0; - memset((*args).path, '\0', 768); - memset((*args).mode, '\0', 32); + args->client = caddr; + args->err = 0; + memset(args->path, '\0', NBD_NBTPD_ARGS_PATH_MAX); + memset(args->mode, '\0', NBD_NBTPD_ARGS_MODE_MAX); int i; - for(i = 2; buf[i] != 0 && i < recvd_bytes && (i - 2) < 768; i++) { - (*args).path[i - 2] = buf[i]; + for(i = 2; buf[i] != 0 && i < recvd_bytes && (i - 2) < (NBD_NBTPD_ARGS_PATH_MAX - 1); i++) { + args->path[i - 2] = buf[i]; } int s = i + 1; if (s >= recvd_bytes) { oc = 0; } else { - for(i = s; buf[i] != 0 && i < recvd_bytes && (i - s) < 32; i++) { - (*args).mode[i - s] = buf[i]; + for(i = s; buf[i] != 0 && i < recvd_bytes && (i - s) < (NBD_NBTPD_ARGS_MODE_MAX - 1); i++) { + args->mode[i - s] = buf[i]; } } + //TODO: use std threads pthread_t *_thread = malloc(sizeof(pthread_t)); if (_thread == NULL) { - syslog(LOG_CRIT, "unable to allocate memory"); + syslog(LOG_CRIT, "unable to allocate memory: %s", strerror(errno)); free(args); continue; } @@ -205,21 +288,21 @@ int main(int argc, char **argv) { (*args).err = 4; func = &nbd_nbtpd_resp_error; } - if (pthread_create(_thread, 0, func, (void *)args) != 0) { + //TODO: use std threads + int e; + if ((e = pthread_create(_thread, 0, func, (void *)args)) != 0) { syslog( LOG_CRIT, - "unable to handle request: cannot create thread!" + "unable to spawn thread: %s", + strerror(e) ); + free(_thread); free(args); } } - - // free our persistent buffer +nbd_nbtpd_cleanup_main: free(buf); - // close socket close(s); - // wait for threads to exit pthread_exit(NULL); - // exit program - return 0; + return retme; } diff --git a/src/netascii.c b/src/netascii.c index fb4d7b8..d061e57 100644 --- a/src/netascii.c +++ b/src/netascii.c @@ -1,4 +1,5 @@ #include <stdint.h> +#include <stdlib.h> #include "netascii.h" @@ -29,7 +30,7 @@ uint8_t is_netascii_str(char *str) { return 1; } -uint8_t is_netascii_buf(char *buf, uint64_t len) { +uint8_t is_netascii_buf(char *buf, size_t len) { for (uint64_t i = 0; i < len; i++) { if (!is_netascii_char(buf[i])) { return 0; |