The Secure Copy Protocol (SCP) provides a method to transfer files. It supports both uploading and downloading files.
libssh makes using SCP easy. This chapter's code repository contains an example, ssh_download.c, which shows the basic method for downloading a file over SCP with libssh.
After the SSH session is started and the user is authenticated, ssh_download.c prompts the user for the remote filename using the following code:
/*ssh_download.c excerpt*/
printf("Remote file to download: ");
char filename[128];
fgets(filename, sizeof(filename), stdin);
filename[strlen(filename)-1] = 0;
A new SCP session is initialized by calling the libssh library function ssh_scp_new(), as shown in the following code:
/*ssh_download.c excerpt*/
ssh_scp scp = ssh_scp_new(ssh, SSH_SCP_READ, filename);
if (!scp) {
fprintf(stderr, "ssh_scp_new() failed.\n%s\n",
ssh_get_error(ssh));
return 1;
}
In the preceding code, SSH_SCP_READ is passed to ssh_scp_new(). This specifies that we are going to use the new SCP session for downloading files. The SSH_SCP_WRITE option would be used to upload files. The libssh library also provides the SSH_SCP_RECURSIVE option to assist with uploading or downloading entire directory trees.
After the SCP session is created successfully, ssh_scp_init() must be called to initialize the SCP channel. The following code shows this:
/*ssh_download.c excerpt*/
if (ssh_scp_init(scp) != SSH_OK) {
fprintf(stderr, "ssh_scp_init() failed.\n%s\n",
ssh_get_error(ssh));
return 1;
}
ssh_scp_pull_request() must be called to begin the file download. This function returns SSH_SCP_REQUEST_NEWFILE to indicate that the remote host is going to begin sending a new file. The following code shows this:
/*ssh_download.c excerpt*/
if (ssh_scp_pull_request(scp) != SSH_SCP_REQUEST_NEWFILE) {
fprintf(stderr, "ssh_scp_pull_request() failed.\n%s\n",
ssh_get_error(ssh));
return 1;
}
libssh provides some methods we can use to retrieve the remote filename, file size, and permissions. The following code retrieves these values and prints them to the console:
/*ssh_download.c excerpt*/
int fsize = ssh_scp_request_get_size(scp);
char *fname = strdup(ssh_scp_request_get_filename(scp));
int fpermission = ssh_scp_request_get_permissions(scp);
printf("Downloading file %s (%d bytes, permissions 0%o\n",
fname, fsize, fpermission);
free(fname);
Once the file size is known, we can allocate space to store it in memory. This is done using malloc() as shown in the following code:
/*ssh_download.c excerpt*/
char *buffer = malloc(fsize);
if (!buffer) {
fprintf(stderr, "malloc() failed.\n");
return 1;
}
Our program then accepts the new file request with ssh_scp_accept_request() and downloads the file with ssh_scp_read(). The following code shows this:
/*ssh_download.c excerpt*/
ssh_scp_accept_request(scp);
if (ssh_scp_read(scp, buffer, fsize) == SSH_ERROR) {
fprintf(stderr, "ssh_scp_read() failed.\n%s\n",
ssh_get_error(ssh));
return 1;
}
The downloaded file can be printed to the screen with a simple call to printf(). When we're finished with the file data, it's important to free the allocated space too. The following code prints out the file's contents and frees the allocated memory:
/*ssh_download.c excerpt*/
printf("Received %s:\n", filename);
printf("%.*s\n", fsize, buffer);
free(buffer);
An additional call to ssh_scp_pull_request() should return SSH_SCP_REQUEST_EOF. This indicates that we received the entire file from the remote host. The following code checks for the end-of-file request from the remote host:
/*ssh_download.c excerpt*/
if (ssh_scp_pull_request(scp) != SSH_SCP_REQUEST_EOF) {
fprintf(stderr, "ssh_scp_pull_request() unexpected.\n%s\n",
ssh_get_error(ssh));
return 1;
}
The preceding code is simplified a bit. The remote host could also return other values which aren't necessarily errors. For example, if ssh_scp_pull_request() returns SSH_SCP_REQUEST_WARNING, then the remote host has sent a warning. This warning can be read by calling ssh_scp_request_get_warning(), but, in any case, ssh_scp_pull_request() should be called again.
After the file is received, ssh_scp_close() and ssh_scp_free() should be used to free resources as shown in the following code excerpt:
/*ssh_download.c excerpt*/
ssh_scp_close(scp);
ssh_scp_free(scp);
After your program is done with the SSH session, don't forget to call ssh_disconnect() and ssh_free() as well.
The entire file-downloading example is included with this chapter's code as ssh_download.c.
You can compile ssh_download.c with the following command on Windows using MinGW:
gcc ssh_download.c -o ssh_download.exe -lssh
On Linux and macOS, the command to compile ssh_download.c is as follows:
gcc ssh_download.c -o ssh_download -lssh
The following screenshot shows ssh_download.c being successfully compiled and used on Linux to download a file:
As you can see from the preceding screenshot, downloading a file using SSH and SCP is pretty straightforward. This can be a useful way to transfer data between computers securely.