about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorRen Kararou <[email protected]>2025-01-09 00:09:48 -0600
committerRen Kararou <[email protected]>2025-01-09 00:09:48 -0600
commitb1b3e7eca4c8153d7c6a2422923ad4ec2b78a223 (patch)
tree799e29017bbc3a9b61c50db1754426d44a2fa8b7
parent660da53647cdcdc5ce07054f74e5c420a15702fe (diff)
downloadnbtpd-b1b3e7eca4c8153d7c6a2422923ad4ec2b78a223.tar.gz
nbtpd-b1b3e7eca4c8153d7c6a2422923ad4ec2b78a223.tar.bz2
nbtpd-b1b3e7eca4c8153d7c6a2422923ad4ec2b78a223.zip
work on WRQ path
-rw-r--r--inc/packet.h5
-rw-r--r--src/handlers.c267
-rw-r--r--src/packet.c62
3 files changed, 298 insertions, 36 deletions
diff --git a/inc/packet.h b/inc/packet.h
index a404d49..86b43a3 100644
--- a/inc/packet.h
+++ b/inc/packet.h
@@ -50,6 +50,11 @@ typedef struct {
 char *nbd_tftp_error_to_message(nbd_tftp_ecode error);
 char *nbd_tftp_ser_data(nbd_tftp_packet_data d);
 char *nbd_tftp_ser_data_from_parts(uint16_t blocknum, char *data, size_t datalen);
+nbd_tftp_packet_data nbd_tftp_de_data(char *data, size_t len);
+char *nbd_tftp_ser_error(nbd_tftp_packet_error e);
+char *nbd_tftp_ser_error_from_code(nbd_tftp_ecode error);
+char *nbd_tftp_ser_ack(nbd_tftp_packet_ack ack);
+char *nbd_tftp_ser_ack_from_block_num(uint16_t block_num);
 nbd_tftp_packet_ack nbd_tftp_de_ack(char *buf, ssize_t buflen);
 
 #endif
diff --git a/src/handlers.c b/src/handlers.c
index 394ccb0..5bfad60 100644
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -20,7 +20,7 @@
 #include "packet.h"
 #include "netascii.h"
 
-nbd_opmode get_mode(char *mode) {
+inline nbd_opmode get_mode(char *mode) {
 	nbd_opmode opmode;
 	if (strncmp(mode, (char *)&"netascii", NBD_NBTPD_ARGS_MODE_MAX) == 0) {
 		opmode = NETASCII;
@@ -34,20 +34,45 @@ nbd_opmode get_mode(char *mode) {
 	return opmode;
 }
 
-void *read_req_resp(void *args) {
-	char *fname = NULL, *wd = NULL, *buf = NULL, *rxb = NULL;
-	FILE *fp = NULL;
-	nbd_nbtpd_args *argptr = (nbd_nbtpd_args *)args;
-	syslog(LOG_DEBUG, "%s:%d is requesting file %s", inet_ntoa(argptr->client.sin_addr), ntohs(argptr->client.sin_port), argptr->path);
+int makesock(nbd_nbtpd_args *argptr) {
+	int s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s <= 0) {
+		syslog(LOG_ERR, "unable to define socket: %s", strerror(errno));
+		return -1;
+	}
+	struct timeval timeout = { 30, 0 };
+	if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
+		syslog(LOG_ERR, "unable to set socket timeout: %s", strerror(errno));
+		return -1;
+	}
+	if (connect(s, (struct sockaddr *)&(argptr->client), sizeof(struct sockaddr)) < 0) {
+		syslog(
+			LOG_ERR,
+			"unable to connect to client %s: %s",
+			inet_ntoa(argptr->client.sin_addr),
+			strerror(errno)
+		);
+		return -1;
+	}
+	return s;
+}
+
+char *checkpath(nbd_nbtpd_args *argptr, uint8_t read) {
+	char *fname = NULL, *wd = NULL;
 	if (!is_netascii_str((char *)&argptr->path)) {
 		argptr->err = 1;
-		goto pre_socket;
+		goto cleanup;
+	}
+	if (read) {
+		fname = realpath((char *)&argptr->path, NULL);
+	} else {
+		//TODO: figure out how to canonicalize non-existent path
+		fname = realpath((char *)&argptr->path, NULL);
 	}
-	fname = realpath((char *)&argptr->path, NULL);
 	if (fname == NULL) {
 		syslog(LOG_ERR, "unable to get real path: %s", strerror(errno));
 		argptr->err = 1;
-		goto pre_socket;
+		goto cleanup;
 	}
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * POSIX does not define what happens when getcwd() is given NULL.     *
@@ -60,16 +85,16 @@ void *read_req_resp(void *args) {
 	if (wd == NULL) {
 		syslog(LOG_ERR, "unable to allocate PATH_MAX memory: %s", strerror(errno));
 		argptr->err = 0;
-		goto pre_socket;
+		goto cleanup;
 	}
 	if (getcwd(wd, MAXPATHLEN) == NULL) {
 		argptr->err = 0;
-		goto pre_socket;
+		goto cleanup;
 	}
 #else
 	if ((wd = getcwd(NULL, PATH_MAX)) == NULL) {
 		argptr->err = 0;
-		goto pre_socket;
+		goto cleanup;
 	}
 #endif
 	syslog(LOG_DEBUG, "cwd: %s :: realpath: %s", wd, fname);
@@ -82,6 +107,34 @@ void *read_req_resp(void *args) {
 			fname
 		);
 		argptr->err = 2;
+		goto cleanup;
+	}
+cleanup:
+	free(wd);
+	return fname;
+}
+
+inline ssize_t senderror(int s, nbd_nbtpd_args *argptr) {
+	size_t buflen = 4 + strlen(nbd_tftp_error_to_message((nbd_tftp_ecode)argptr->err));
+	char *buf = nbd_tftp_ser_error_from_code((nbd_tftp_ecode)argptr->err);
+	ssize_t sb = send(s, buf, buflen, 0);
+	free(buf);
+	return sb;
+}
+
+void *read_req_resp(void *args) {
+	char *fname = NULL, *buf = NULL, *rxb = NULL;
+	FILE *fp = NULL;
+	nbd_nbtpd_args *argptr = (nbd_nbtpd_args *)args;
+	syslog(
+		LOG_DEBUG,
+		"%s:%d is requesting file %s",
+		inet_ntoa(argptr->client.sin_addr),
+		ntohs(argptr->client.sin_port),
+		argptr->path
+	);
+	fname = checkpath(argptr, 1);
+	if (fname == NULL) {
 		goto pre_socket;
 	}
 	if (!is_netascii_str((char *)&(argptr->mode))) {
@@ -112,7 +165,7 @@ void *read_req_resp(void *args) {
 	}
 	buf = malloc(512);
 	rxb = malloc(256);
-	if (buf == NULL) {
+	if ((buf == NULL) || (rxb == NULL)) {
 		syslog(
 			LOG_CRIT,
 			"failed to allocate memory: %s",
@@ -121,25 +174,10 @@ void *read_req_resp(void *args) {
 		argptr->err = 0;
 		goto pre_socket;
 	}
-	int s = socket(AF_INET, SOCK_DGRAM, 0);
+	int s = makesock(argptr);
 	if (s <= 0) {
-		syslog(LOG_ERR, "unable to define socket: %s", strerror(errno));
 		goto cleanup;
 	}
-	struct timeval timeout = { 30, 0 };
-	if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
-		syslog(LOG_ERR, "unable to set socket timeout: %s", strerror(errno));
-		goto socket_clean;
-	}
-	if (connect(s, (struct sockaddr *)&(argptr->client), sizeof(struct sockaddr)) < 0) {
-		syslog(
-			LOG_ERR,
-			"unable to connect to client %s: %s",
-			inet_ntoa(argptr->client.sin_addr),
-			strerror(errno)
-		);
-		goto socket_clean;
-	}
 	uint8_t rxon = 0, lon = 1;
 	uint16_t bnum = 0;
 	while (lon) {
@@ -148,7 +186,9 @@ void *read_req_resp(void *args) {
 		uint8_t vcount = 0;
 		size_t num_read = fread(buf, 1, 512, fp);
 		if (((num_read < 512) && (!feof(fp))) || (vcount > 5)) {
-			//TODO: error response function!
+			argptr->err = 0;
+			senderror(s, argptr);
+			goto cleanup;
 		}
 		char *packet = nbd_tftp_ser_data_from_parts(++bnum, buf, num_read);
 		while (!verif) {
@@ -201,7 +241,9 @@ void *read_req_resp(void *args) {
 					}
 				}
 				if (++rxcount > 30) {
-					//TODO: error response function!
+					argptr->err = 0;
+					senderror(s, argptr);
+					goto cleanup;
 				}
 			}
 		}
@@ -210,14 +252,12 @@ void *read_req_resp(void *args) {
 			lon = 0;
 		}
 	}
-socket_clean:
 	close(s);
 cleanup:
 	fclose(fp);
 	free(buf);
 	free(rxb);
 	free(fname);
-	free(wd);
 	free(args);
 	return (void *)NULL;
 pre_socket:
@@ -227,18 +267,173 @@ pre_socket:
 	free(buf);
 	free(rxb);
 	free(fname);
-	free(wd);
 	return nbd_nbtpd_resp_error(args);
 }
 
 void *write_req_resp(void *args) {
+	char *fname = NULL, *rxb = NULL;
+	FILE *fp = NULL;
+	nbd_nbtpd_args *argptr = (nbd_nbtpd_args *)args;
+	syslog(
+		LOG_DEBUG,
+		"%s:%d is trying to write file %s",
+		inet_ntoa(argptr->client.sin_addr),
+		ntohs(argptr->client.sin_port),
+		argptr->path
+	);
+	fname = checkpath(argptr, 0);
+	if (fname == NULL) {
+		goto pre_socket;
+	}
+	if (!is_netascii_str((char *)&(argptr->mode))) {
+		syslog(
+			LOG_ERR,
+			"%s:%d mode is invalid %s",
+			inet_ntoa(argptr->client.sin_addr),
+			ntohs(argptr->client.sin_port),
+			argptr->mode
+		);
+		argptr->err = 4;
+		goto pre_socket;
+	}
+	nbd_opmode opmode = get_mode((char *)&(argptr->mode));
+	if ((opmode != NETASCII) && (opmode != OCTET)) {
+		argptr->err = 4;
+		goto pre_socket;
+	}
+	fp = fopen(fname, "w");
+	if (fp == NULL) {
+		syslog(
+			LOG_ERR,
+			"failed to open file for writing: %s",
+			strerror(errno)
+		);
+		argptr->err = 2;
+		goto pre_socket;
+	}
+	rxb = malloc(768); // 768 is 1.5x expected block size
+	if (rxb == NULL) {
+		syslog(
+			LOG_CRIT,
+			"failed to allocate memory: %s",
+			strerror(errno)
+		);
+		argptr->err = 0;
+		goto pre_socket;
+	}
+	int s = makesock(argptr);
+	if (s <= 0) {
+		goto cleanup;
+	}
+	uint8_t rxon = 0, lon = 1;
+	uint16_t bnum = 0;
+	ssize_t rcv = 516;
+	while (lon) {
+		uint8_t verif = 0;
+		uint8_t vcount = 0;
+		char *packet = nbd_tftp_ser_ack_from_block_num(bnum);
+		while (!verif) {
+			syslog(LOG_DEBUG, "sending ack number %d to %s:%d",
+				bnum,
+				inet_ntoa(argptr->client.sin_addr),
+				htons(argptr->client.sin_port)
+			);
+			if (send(s, packet, 4, 0) < 0) {
+				syslog(
+					LOG_ERR,
+					"unable to send ack to %s:%d: %s",
+					inet_ntoa(argptr->client.sin_addr),
+					htons(argptr->client.sin_port),
+					strerror(errno)
+				);
+				continue;
+			}
+			if ((rcv < 516) || (vcount > 5)) {
+				lon = 0;
+				break;
+			}
+			rxon = 1;
+			uint8_t rxcount = 0;
+			while (rxon) {
+				syslog(LOG_DEBUG, "waiting for data from %s:%d",
+					inet_ntoa(argptr->client.sin_addr),
+					htons(argptr->client.sin_port)
+				);
+				memset(rxb, '\0', 768);
+				rcv = recv(s, rxb, 768, 0);
+				syslog(LOG_DEBUG, "recv'd %ld bytes from %s:%d",
+					rcv,
+					inet_ntoa(argptr->client.sin_addr),
+					htons(argptr->client.sin_port)
+				);
+				if (rcv >= 4) {
+					nbd_tftp_packet_data data = nbd_tftp_de_data(rxb, rcv);
+					syslog(
+						LOG_DEBUG,
+						"%s:%d sent block number: %d (expect %d)",
+						inet_ntoa(argptr->client.sin_addr),
+						htons(argptr->client.sin_port),
+						data.block_num,
+						bnum + 1
+					);
+					if (data.block_num == (bnum + 1)) {
+						rxon = 0;
+						verif = 1;
+						if (data.datalen > 0) {
+							if (fwrite(data.data, data.datalen, 1, fp) < 1) {
+								argptr->err = 0;
+								senderror(s, argptr);
+								goto clean_socket;
+							}
+						}
+						break;
+					}
+					if (data.block_num == bnum) {
+						rxon = 0;
+						vcount++;
+						break;
+					}
+				}
+				if (++rxcount > 30) {
+					argptr->err = 0;
+					senderror(s, argptr);
+					goto clean_socket;
+				}
+			}
+		}
+		free(packet);
+	}
+clean_socket:
+	close(s);
+cleanup:
+	fclose(fp);
+	free(rxb);
+	free(fname);
 	free(args);
 	return (void *)NULL;
+pre_socket:
+	if (fp != NULL) {
+		fclose(fp);
+	}
+	free(rxb);
+	free(fname);
+	return nbd_nbtpd_resp_error(args);
 }
 
 void *nbd_nbtpd_resp_error(void *args) {
-	//TODO: open socket and scream error, then cleanup.
-	//TODO: error response function!
+	nbd_nbtpd_args *argptr = (nbd_nbtpd_args *)args;
+	int s = makesock(argptr);
+	if (s <= 0) {
+		syslog(
+			LOG_ERR,
+			"cannot send error to %s:%d",
+			inet_ntoa(argptr->client.sin_addr),
+			ntohs(argptr->client.sin_port)
+		);
+		goto cleanup;
+	}
+	senderror(s, argptr);
+cleanup:
 	free(args);
 	return (void *)NULL;
 }
diff --git a/src/packet.c b/src/packet.c
index 0c9d592..0663b64 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -50,6 +50,68 @@ char *nbd_tftp_ser_data_from_parts(uint16_t blocknum, char *data, size_t datalen
 	return buf;
 }
 
+nbd_tftp_packet_data nbd_tftp_de_data(char *data, size_t len) {
+	nbd_tftp_packet_data ret = { 0, 0, (char *)NULL, (size_t)0 };
+	if ((len >= 4) && (data != NULL)) {
+		ret.opcode = ((uint16_t)data[0] << 8) + data[1];
+		ret.block_num = ((uint16_t)data[2] << 8) + data[3];
+		ret.datalen = len - 4;
+		if (ret.datalen <= 0) {
+			if ((ret.data = malloc(ret.datalen)) != NULL) {
+				memcpy(ret.data, (data + 4), ret.datalen);
+			}
+		}
+	}
+	return ret;
+}
+
+char *nbd_tftp_ser_error(nbd_tftp_packet_error e) {
+	char *buf = malloc(sizeof(e.opcode) + sizeof((uint16_t)e.err) + strlen(e.emsg));
+	if (buf != NULL) {
+		uint16_t netopcode = htons(e.opcode);
+		uint16_t netecode = htons((uint16_t)e.err);
+		memcpy(buf, &netopcode, sizeof(netopcode));
+		memcpy((buf + sizeof(netopcode)), &netecode, sizeof(netecode));
+		memcpy((buf + sizeof(netopcode) + sizeof(netecode)), e.emsg, strlen(e.emsg));
+	}
+	return buf;
+}
+
+char *nbd_tftp_ser_error_from_code(nbd_tftp_ecode error) {
+	char *emsg = nbd_tftp_error_to_message(error);
+	char *buf = malloc(sizeof(uint16_t) + sizeof(uint16_t) + strlen(emsg));
+	if (buf != NULL) {
+		uint16_t netopcode = htons(5);
+		uint16_t netecode = htons((uint16_t)error);
+		memcpy(buf, &netopcode, sizeof(netopcode));
+		memcpy((buf + sizeof(netopcode)), &netecode, sizeof(netecode));
+		memcpy((buf + sizeof(netopcode) + sizeof(netecode)), emsg, strlen(emsg));
+	}
+	return buf;
+}
+
+char *nbd_tftp_ser_ack(nbd_tftp_packet_ack ack) {
+	char *buf = malloc(sizeof(ack.opcode) + sizeof(ack.block_num));
+	if (buf != NULL) {
+		uint16_t netopcode = htons(ack.opcode);
+		uint16_t netbnum = htons(ack.block_num);
+		memcpy(buf, &netopcode, sizeof(netopcode));
+		memcpy((buf + sizeof(netopcode)), &netbnum, sizeof(netbnum));
+	}
+	return buf;
+}
+
+char *nbd_tftp_ser_ack_from_block_num(uint16_t block_num) {
+	char *buf = malloc(sizeof(uint16_t) + sizeof(block_num));
+	if (buf != NULL) {
+		uint16_t netopcode = htons(5);
+		uint16_t netbnum = htons(block_num);
+		memcpy(buf, &netopcode, sizeof(netopcode));
+		memcpy((buf + sizeof(netopcode)), &netbnum, sizeof(netbnum));
+	}
+	return buf;
+}
+
 nbd_tftp_packet_ack nbd_tftp_de_ack(char *buf, ssize_t buflen) {
 	nbd_tftp_packet_ack ack = { 0, 0 };
 	if ((buf != NULL) && (buflen == 4)) {