about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorRen Kararou <[email protected]>2025-01-05 02:29:15 -0600
committerRen Kararou <[email protected]>2025-01-05 02:29:15 -0600
commit5605a5ddf3c77232b04c002a82b91e227c0f27da (patch)
tree30d5cf75759aa4bd2e8e75d5a7c852931e114066
parent70a46fab050c3fdf38b9c72453f6e678cc8341be (diff)
downloadnbtpd-5605a5ddf3c77232b04c002a82b91e227c0f27da.tar.gz
nbtpd-5605a5ddf3c77232b04c002a82b91e227c0f27da.tar.bz2
nbtpd-5605a5ddf3c77232b04c002a82b91e227c0f27da.zip
builds on illumos, freebsd, and linux
-rw-r--r--inc/handlers.h7
-rw-r--r--inc/netascii.h3
-rw-r--r--makefile8
-rw-r--r--src/handlers.c140
-rw-r--r--src/main.c211
-rw-r--r--src/netascii.c3
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;