Skip to content

Commit b66d8ec

Browse files
authored
Merge pull request #3864 from karlhto/libcfnet_get
CFE-3087: New functions Get and StatGet for protocol
2 parents 2c0a0f9 + af2a0d6 commit b66d8ec

File tree

3 files changed

+174
-11
lines changed

3 files changed

+174
-11
lines changed

cf-net/cf-net.c

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -695,17 +695,9 @@ static void *CFNetGetFile(void *arg)
695695
return NULL;
696696
}
697697

698-
struct stat sb;
699-
data->ret = cf_remote_stat(conn, true, data->remote_file, &sb, "file");
700-
if (data->ret != 0)
701-
{
702-
printf("Could not stat: '%s'\n", data->remote_file);
703-
}
704-
else
705-
{
706-
bool ok = CopyRegularFileNet(data->remote_file, data->local_file, sb.st_size, true, conn);
707-
data->ret = ok ? 0 : -1;
708-
}
698+
bool ok = ProtocolStatGet(conn, data->remote_file,
699+
data->local_file, 0644);
700+
data->ret = ok ? 0 : -1;
709701
CFNetDisconnect(conn);
710702
return NULL;
711703
}

libcfnet/protocol.c

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <client_protocol.h>
2929
#include <definitions.h>
3030
#include <net.h>
31+
#include <stat_cache.h>
3132
#include <string_lib.h>
3233
#include <tls_generic.h>
3334

@@ -88,3 +89,122 @@ Seq *ProtocolOpenDir(AgentConnection *conn, const char *path)
8889

8990
return seq;
9091
}
92+
93+
bool ProtocolGet(AgentConnection *conn, const char *remote_path,
94+
const char *local_path, const uint32_t file_size, int perms)
95+
{
96+
assert(conn != NULL);
97+
assert(remote_path != NULL);
98+
assert(local_path != NULL);
99+
assert(file_size != 0);
100+
101+
perms = (perms == 0) ? CF_PERMS_DEFAULT : perms;
102+
103+
unlink(local_path);
104+
FILE *file_ptr = safe_fopen_create_perms(local_path, "wx", perms);
105+
if (file_ptr == NULL)
106+
{
107+
Log(LOG_LEVEL_WARNING, "Failed to open file %s (fopen: %s)",
108+
local_path, GetErrorStr());
109+
return false;
110+
}
111+
112+
char buf[CF_MSGSIZE] = {0};
113+
int to_send = snprintf(buf, CF_MSGSIZE, "GET %d %s",
114+
CF_MSGSIZE, remote_path);
115+
116+
117+
int ret = SendTransaction(conn->conn_info, buf, to_send, CF_DONE);
118+
if (ret == -1)
119+
{
120+
Log(LOG_LEVEL_WARNING, "Failed to send request for remote file %s:%s",
121+
conn->this_server, remote_path);
122+
unlink(local_path);
123+
fclose(file_ptr);
124+
return false;
125+
}
126+
127+
char cfchangedstr[sizeof(CF_CHANGEDSTR1 CF_CHANGEDSTR2)];
128+
snprintf(cfchangedstr, sizeof(cfchangedstr), "%s%s",
129+
CF_CHANGEDSTR1, CF_CHANGEDSTR2);
130+
131+
bool success = true;
132+
uint32_t received_bytes = 0;
133+
while (received_bytes < file_size)
134+
{
135+
int len = TLSRecv(conn->conn_info->ssl, buf, CF_MSGSIZE);
136+
if (len == -1)
137+
{
138+
Log(LOG_LEVEL_WARNING, "Failed to GET file %s:%s",
139+
conn->this_server, remote_path);
140+
success = false;
141+
break;
142+
}
143+
else if (len > CF_MSGSIZE)
144+
{
145+
Log(LOG_LEVEL_WARNING,
146+
"Incorrect length of incoming packet "
147+
"while retrieving %s:%s, %d > %d",
148+
conn->this_server, remote_path, len, CF_MSGSIZE);
149+
success = false;
150+
break;
151+
}
152+
153+
if (BadProtoReply(buf))
154+
{
155+
Log(LOG_LEVEL_ERR,
156+
"Error from server while retrieving file %s:%s: %s",
157+
conn->this_server, remote_path, buf);
158+
success = false;
159+
break;
160+
}
161+
162+
if (StringSafeEqualN(buf, cfchangedstr, sizeof(cfchangedstr) - 1))
163+
{
164+
Log(LOG_LEVEL_ERR,
165+
"Remote file %s:%s changed during file transfer",
166+
conn->this_server, remote_path);
167+
success = false;
168+
break;
169+
}
170+
171+
ret = fwrite(buf, sizeof(char), len, file_ptr);
172+
if (ret < 0)
173+
{
174+
Log(LOG_LEVEL_ERR,
175+
"Failed to write during retrieval of file %s:%s (fwrite: %s)",
176+
conn->this_server, remote_path, GetErrorStr());
177+
success = false;
178+
break;
179+
}
180+
181+
received_bytes += len;
182+
}
183+
184+
if (!success)
185+
{
186+
unlink(local_path);
187+
}
188+
189+
fclose(file_ptr);
190+
return success;
191+
}
192+
193+
bool ProtocolStatGet(AgentConnection *conn, const char *remote_path,
194+
const char *local_path, int perms)
195+
{
196+
assert(conn != NULL);
197+
assert(remote_path != NULL);
198+
199+
struct stat sb;
200+
int ret = cf_remote_stat(conn, false, remote_path, &sb, "file");
201+
if (ret == -1)
202+
{
203+
Log(LOG_LEVEL_ERR,
204+
"%s: Failed to stat remote file %s:%s",
205+
__func__, conn->this_server, remote_path);
206+
return false;
207+
}
208+
209+
return ProtocolGet(conn, remote_path, local_path, sb.st_size, perms);
210+
}

libcfnet/protocol.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,55 @@
6464
*/
6565
Seq *ProtocolOpenDir(AgentConnection *conn, const char *path);
6666

67+
/**
68+
* Receives a file from a remote host.
69+
*
70+
* The server will use "/var/cfengine" as working directory if absolute path
71+
* is not specified. The client will send a request that looks like this:
72+
* `GET <buf_size> <remote_path>`
73+
*
74+
* `buf_size` is the local buffer size: how much of the file to receive in
75+
* each transaction. It should be aligned to block size (which is usually
76+
* 4096), but currently the protocol only allows _exactly_ 4095 bytes per
77+
* transaction.
78+
*
79+
* The function shall fail if connection is not established, or if the server
80+
* gives a bad response (denoted by a message preceded by "BAD").
81+
*
82+
* @param [in] conn The connection to use
83+
* @param [in] remote_path Path on remote host
84+
* @param [in] local_path Path of received file
85+
* @param [in] file_size Size of file to get
86+
* @param [in] perms Permissions of local file
87+
* @return True if file was successfully transferred, false otherwise
88+
*
89+
* Example (for printing each directory entry):
90+
* @code
91+
* AgentConnection *conn = ServerConnection("127.0.0.1", "666", ...);
92+
* bool got_file = ProtocolGet(conn, "masterfiles/update.cf",
93+
* "update.cf", CF_MSGSIZE, 0644);
94+
* if (got_file)
95+
* {
96+
* struct stat sb;
97+
* stat("update.cf", &sb);
98+
* printf("This file is %ld big!\n", sb.st_size);
99+
* }
100+
* @endcode
101+
*
102+
* In the protocol, this will look like this on the server side:
103+
* Received: GET masterfiles/update.cf
104+
* Translated to: GET /var/cfengine/masterfiles/update.cf
105+
*/
106+
bool ProtocolGet(AgentConnection *conn, const char *remote_path,
107+
const char *local_path, const uint32_t file_size, int perms);
108+
109+
110+
/**
111+
* Receives a file from a remote host, see documentation for #ProtocolGet
112+
*
113+
* This funtion will first stat the remote path before attempting to receive
114+
* it.
115+
*/
116+
bool ProtocolStatGet(AgentConnection *conn, const char *remote_path,
117+
const char *local_path, int perms);
67118
#endif

0 commit comments

Comments
 (0)