Item *RemoteDirList(const char *dirname, bool encrypt, AgentConnection *conn) { char sendbuffer[CF_BUFSIZE]; char recvbuffer[CF_BUFSIZE]; char in[CF_BUFSIZE]; char out[CF_BUFSIZE]; int n, cipherlen = 0, tosend; char *sp; Item *files = NULL; Item *ret = NULL; if (strlen(dirname) > CF_BUFSIZE - 20) { Log(LOG_LEVEL_ERR, "Directory name too long"); return NULL; } if (encrypt) { if (conn->session_key == NULL) { Log(LOG_LEVEL_ERR, "Cannot do encrypted copy without keys (use cf-key)"); return NULL; } snprintf(in, CF_BUFSIZE, "OPENDIR %s", dirname); cipherlen = EncryptString(conn->encryption_type, in, out, conn->session_key, strlen(in) + 1); snprintf(sendbuffer, CF_BUFSIZE - 1, "SOPENDIR %d", cipherlen); memcpy(sendbuffer + CF_PROTO_OFFSET, out, cipherlen); tosend = cipherlen + CF_PROTO_OFFSET; } else { snprintf(sendbuffer, CF_BUFSIZE, "OPENDIR %s", dirname); tosend = strlen(sendbuffer); } if (SendTransaction(conn->sd, sendbuffer, tosend, CF_DONE) == -1) { return NULL; } while (true) { if ((n = ReceiveTransaction(conn->sd, recvbuffer, NULL)) == -1) { return NULL; } if (n == 0) { break; } if (encrypt) { memcpy(in, recvbuffer, n); DecryptString(conn->encryption_type, in, recvbuffer, conn->session_key, n); } if (FailedProtoReply(recvbuffer)) { Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, dirname); return NULL; } if (BadProtoReply(recvbuffer)) { Log(LOG_LEVEL_INFO, "%s", recvbuffer + 4); return NULL; } for (sp = recvbuffer; *sp != '\0'; sp++) { Item *ip; if (strncmp(sp, CFD_TERMINATOR, strlen(CFD_TERMINATOR)) == 0) /* End transmission */ { return ret; } ip = xcalloc(1, sizeof(Item)); ip->name = (char *) AllocateDirentForFilename(sp); if (files == NULL) /* First element */ { ret = ip; files = ip; } else { files->next = ip; files = ip; } while (*sp != '\0') { sp++; } } } return ret; }
/* Returning NULL (an empty list) does not mean empty directory but ERROR, * since every directory has to contain at least . and .. */ Item *RemoteDirList(const char *dirname, bool encrypt, AgentConnection *conn) { char sendbuffer[CF_BUFSIZE]; char recvbuffer[CF_BUFSIZE]; char in[CF_BUFSIZE]; char out[CF_BUFSIZE]; int cipherlen = 0, tosend; if (strlen(dirname) > CF_BUFSIZE - 20) { Log(LOG_LEVEL_ERR, "Directory name too long"); return NULL; } /* We encrypt only for CLASSIC protocol. The TLS protocol is always over * encrypted layer, so it does not support encrypted (S*) commands. */ encrypt = encrypt && conn->conn_info->protocol == CF_PROTOCOL_CLASSIC; if (encrypt) { if (conn->session_key == NULL) { Log(LOG_LEVEL_ERR, "Cannot do encrypted copy without keys (use cf-key)"); return NULL; } snprintf(in, CF_BUFSIZE, "OPENDIR %s", dirname); cipherlen = EncryptString(conn->encryption_type, in, out, conn->session_key, strlen(in) + 1); snprintf(sendbuffer, CF_BUFSIZE - 1, "SOPENDIR %d", cipherlen); memcpy(sendbuffer + CF_PROTO_OFFSET, out, cipherlen); tosend = cipherlen + CF_PROTO_OFFSET; } else { snprintf(sendbuffer, CF_BUFSIZE, "OPENDIR %s", dirname); tosend = strlen(sendbuffer); } if (SendTransaction(conn->conn_info, sendbuffer, tosend, CF_DONE) == -1) { return NULL; } Item *start = NULL, *end = NULL; /* NULL == empty list */ while (true) { /* TODO check the CF_MORE flag, no need for CFD_TERMINATOR. */ int nbytes = ReceiveTransaction(conn->conn_info, recvbuffer, NULL); /* If recv error or socket closed before receiving CFD_TERMINATOR. */ if (nbytes == -1 || nbytes == 0) { /* TODO mark connection in the cache as closed. */ goto err; } if (recvbuffer[0] == '\0') { Log(LOG_LEVEL_ERR, "Empty%s server packet when listing directory '%s'!", (start == NULL) ? " first" : "", dirname); goto err; } if (encrypt) { memcpy(in, recvbuffer, nbytes); DecryptString(conn->encryption_type, in, recvbuffer, conn->session_key, nbytes); } if (FailedProtoReply(recvbuffer)) { Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, dirname); goto err; } if (BadProtoReply(recvbuffer)) { Log(LOG_LEVEL_INFO, "%s", recvbuffer + strlen("BAD: ")); goto err; } /* Double '\0' means end of packet. */ for (char *sp = recvbuffer; *sp != '\0'; sp += strlen(sp) + 1) { if (strcmp(sp, CFD_TERMINATOR) == 0) /* end of all packets */ { return start; } Item *ip = xcalloc(1, sizeof(Item)); ip->name = (char *) AllocateDirentForFilename(sp); if (start == NULL) /* First element */ { start = ip; end = ip; } else { end->next = ip; end = ip; } } } return start; err: /* free list */ for (Item *ip = start; ip != NULL; ip = start) { start = ip->next; free(ip->name); free(ip); } return NULL; }