From b1b3e7eca4c8153d7c6a2422923ad4ec2b78a223 Mon Sep 17 00:00:00 2001 From: Ren Kararou Date: Thu, 9 Jan 2025 00:09:48 -0600 Subject: work on WRQ path --- src/handlers.c | 267 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 231 insertions(+), 36 deletions(-) (limited to 'src/handlers.c') 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; } -- cgit 1.4.1-2-gfad0