diff options
author | Ren Kararou <[email protected]> | 2025-01-09 00:09:48 -0600 |
---|---|---|
committer | Ren Kararou <[email protected]> | 2025-01-09 00:09:48 -0600 |
commit | b1b3e7eca4c8153d7c6a2422923ad4ec2b78a223 (patch) | |
tree | 799e29017bbc3a9b61c50db1754426d44a2fa8b7 | |
parent | 660da53647cdcdc5ce07054f74e5c420a15702fe (diff) | |
download | nbtpd-b1b3e7eca4c8153d7c6a2422923ad4ec2b78a223.tar.gz nbtpd-b1b3e7eca4c8153d7c6a2422923ad4ec2b78a223.tar.bz2 nbtpd-b1b3e7eca4c8153d7c6a2422923ad4ec2b78a223.zip |
work on WRQ path
-rw-r--r-- | inc/packet.h | 5 | ||||
-rw-r--r-- | src/handlers.c | 267 | ||||
-rw-r--r-- | src/packet.c | 62 |
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)) { |