Related
So I'm writing an Schannel server to the IOS Gmail client app - IMAP server.
I'm forcing TLS1.0 (tried with TLS1.0to1.3 - 1.3 seems to bail with algo_mismatch but the other ones do the same, I've also tried with SSL3.0 but read that is not supported for gmail.
Currently I can't get over the handshake process which seems to hang at recv. I don't know what I'm doing wrong - I've also installed the certificate on my (client device - IOS).
I need to clarify - it goes into the loop the 1st time which successfully ends (AcceptSecurityContext with SEC_I_CONTINUE_NEEDED) but after the send (I've also checked its result which matches the cbBuffer size so it must be ok) next iteration it hangs on recv.
Here is the handshake part:
static struct performhandshake {
CtxtHandle ctx; CredHandle hCred; SOCKET sock; SecPkgContext_StreamSizes ctxSizes; BOOL bLowMemoryIndicator;
} performhandshake(ctx, hCred, sock) CtxtHandle ctx; CredHandle hCred; SOCKET sock; {
SecPkgContext_StreamSizes ctxSizes;
struct diagose_internal diagose_internal_res; BOOL bLowMemoryIndicator = FALSE; SECURITY_STATUS acceptctxsecstat;
CtxtHandle* pctx = SecIsValidHandle(&ctx) ? &ctx : 0;
SecBufferDesc buff = { .ulVersion = SECBUFFER_VERSION,1,(SecBuffer[]) { [0] = {.BufferType = SECBUFFER_TOKEN} } }, * LastRecieved,
outbuff = { .ulVersion = SECBUFFER_VERSION,1,(SecBuffer[]) { [0] = {.BufferType = SECBUFFER_TOKEN} } },
* InBuff = &outbuff,
* OutBuff = &buff; DWORD attrs; TimeStamp nocare;
char(*d)[USHRT_MAX] = malloc(sizeof * d);
//OutBuff->pBuffers[0].cbBuffer = check_last_error_int(recv, sock, OutBuff->pBuffers[0].pvBuffer = *d, sizeof * d, 0);
do {
InBuff->pBuffers[0].cbBuffer = check_last_error_int(recv, sock, InBuff->pBuffers[0].pvBuffer = *d, sizeof * d, 0);
switch (acceptctxsecstat = diagnose(AcceptSecurityContext, &hCred, pctx, InBuff, ctxflags, 0, &ctx, OutBuff, &attrs, &nocare))
{
case SEC_I_CONTINUE_NEEDED:
case_continue_needed:
//, server, ctxflags, 0, SECURITY_NATIVE_DREP,
//diagnose(CompleteAuthToken, &ctx, OutBuff),
//OutBuff->pBuffers[0].cbBuffer = check_last_error_int(recv, sock, OutBuff->pBuffers[0].pvBuffer = *d, sizeof * d, 0),
check_last_error_int(send, sock, OutBuff->pBuffers[0].pvBuffer, OutBuff->pBuffers[0].cbBuffer, 0);
FreeContextBuffer(OutBuff->pBuffers[0].pvBuffer);
LastRecieved = OutBuff; swap_ptr64(InBuff, OutBuff);
pctx = &ctx;
break;
case SEC_I_COMPLETE_NEEDED:
case SEC_I_COMPLETE_AND_CONTINUE:
diagnose(CompleteAuthToken, pctx, OutBuff);
switch (acceptctxsecstat) {
case SEC_I_COMPLETE_NEEDED:
goto end;
case SEC_I_COMPLETE_AND_CONTINUE:
goto case_continue_needed;
}
default: goto end;
}
} while (/*OutBuff->pBuffers[0].cbBuffer != SOCKET_ERROR && OutBuff->pBuffers[0].cbBuffer*/true);
end:
diagnose(QueryContextAttributes, &ctx, SECPKG_ATTR_STREAM_SIZES, &ctxSizes);
free(d);
return (struct performhandshake) { ctx, hCred, sock, ctxSizes, bLowMemoryIndicator };
}
Here is my main function:
main() {
WSADATA wsadat; WSAStartup(MAKEWORD(2, 2), &wsadat), getaddrinfo("<local-ip>", "993", (struct addrinfo[])
{ {.ai_family = AF_INET, .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_TCP, .ai_flags = AI_PASSIVE}
}, & addrinfo);
InitializeCriticalSection(&crit);
HCERTSTORE hMyCertStore = NULL;
PCCERT_CONTEXT aCertContext = NULL;
//-------------------------------------------------------
// Open the My store, also called the personal store.
// This call to CertOpenStore opens the Local_Machine My
// store as opposed to the Current_User's My store.
hMyCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
X509_ASN_ENCODING,
0,
CERT_SYSTEM_STORE_CURRENT_USER,
L"MY");
if (hMyCertStore == NULL)
printf("Error opening MY store for server.\n");
//-------------------------------------------------------
// Search for a certificate with some specified
// string in it. This example attempts to find
// a certificate with the string "example server" in
// its subject string. Substitute an appropriate string
// to find a certificate for a specific user.
aCertContext = CertFindCertificateInStore(hMyCertStore,
X509_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR_A,
server, // use appropriate subject name
NULL
);
if (aCertContext == NULL)
printf("Error retrieving server certificate.");
CertCloseStore(hMyCertStore, 0);
char buff[USHRT_MAX];
SOCKET conn = beginconn(&buff);
struct performhandshake reshandshake;
CredHandle hCred; TimeStamp nocare; struct diagose_internal diagose_internal_res; BOOL bLowMemoryIndicator = FALSE;
diagnose(AcquireCredentialsHandle, 0, UNISP_NAME, SECPKG_CRED_BOTH, 0, &(SCHANNEL_CRED){.dwVersion = SCHANNEL_CRED_VERSION, .hRootStore = hMyCertStore,
.cCreds = 1, .paCred = (PCCERT_CONTEXT[]){ aCertContext }, .grbitEnabledProtocols = SP_PROT_TLS1_0_SERVER }, 0, 0, & hCred, & nocare);
SecInvalidateHandle(&reshandshake.ctx) reshandshake = performhandshake(reshandshake.ctx, hCred, conn);
return;
}
And here is the whole snippet for reference:
#undef UNICODE
#include <winsock2.h>
#include <stdio.h>
#include <stdbool.h>
#define SECURITY_WIN32
#include <Security.h>
#include <Schnlsp.h>
const char server[] = "<cert-name>";
#define errprintf(...) (printf(__VA_ARGS__))
#define ctxflags (ASC_REQ_ALLOCATE_MEMORY|ASC_REQ_CONFIDENTIALITY)
#define swap(x,y)(x ^= y,y ^= x,x ^= y)
#define swap_ptr64(x,y)swap(*(ULONG64 *)&x, *(ULONG64 *)&y)
struct addrinfo* addrinfo;
static CRITICAL_SECTION crit;
static struct diagose_internal {
SECURITY_STATUS secstat;
BOOL bLowMemoryIndicator;
} diagose_internal(in, desc, line) char desc[]; SECURITY_STATUS in; {BOOL bLowMemoryIndicator = FALSE; in & 0x80000000 ? EnterCriticalSection(&crit),
errprintf("%d - %s - %lx\n", line, desc, in), bLowMemoryIndicator = SEC_E_INSUFFICIENT_MEMORY == in, LeaveCriticalSection(&crit) : 0; return (struct diagose_internal) { in, bLowMemoryIndicator }; }
#define diagnose(x, ...) (diagose_internal_res=diagose_internal(x(__VA_ARGS__),#x,__LINE__), bLowMemoryIndicator=bLowMemoryIndicator||diagose_internal_res.bLowMemoryIndicator, diagose_internal_res.secstat)
static BOOL check_last_error_internal_int(in, desc, line) char desc[]; {BOOL bLowMemoryIndicator = FALSE; int error = WSAGetLastError(); error ? EnterCriticalSection(&crit),
errprintf("%d - %s - %lx\n", line, desc, error), bLowMemoryIndicator = error == WSA_NOT_ENOUGH_MEMORY, //|| error == WSA_QOS_TRAFFIC_CTRL_ERROR,
LeaveCriticalSection(&crit) : 0; return in; }
#define check_last_error_int(x, ...) check_last_error_internal_int(x(__VA_ARGS__),#x,__LINE__)
static struct check_last_error_internal_handle {
SOCKET socket;
BOOL bLowMemoryIndicator;
} check_last_error_internal_handle(in, desc, line) char desc[]; SOCKET in; {BOOL bLowMemoryIndicator = FALSE; int error = WSAGetLastError(); error ? EnterCriticalSection(&crit),
errprintf("%d - %s - %lx\n", line, desc, error), bLowMemoryIndicator = error == WSA_NOT_ENOUGH_MEMORY, //|| error == WSA_QOS_TRAFFIC_CTRL_ERROR,
LeaveCriticalSection(&crit) : 0; return (struct check_last_error_internal_handle) { in, bLowMemoryIndicator }; }
#define check_last_error_handle(x, ...) check_last_error_internal_int(x(__VA_ARGS__),#x,__LINE__)
static SOCKET beginconn() {
SOCKET sock = check_last_error_handle(socket, addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
//check_last_error_int(setsockopt, sock, SOL_SOCKET, SO_RCVTIMEO, (DWORD[]) { 100 }, sizeof(DWORD)),
//check_last_error_int(setsockopt, sock, SOL_SOCKET, SO_SNDTIMEO, (DWORD[]) { 100 }, sizeof(DWORD)),
check_last_error_int(bind, sock, addrinfo->ai_addr, addrinfo->ai_addrlen),
check_last_error_int(listen, sock, SOMAXCONN);
SOCKET finalsock = accept(sock, NULL, NULL);
closesocket(sock);
return finalsock;
}
static struct performhandshake {
CtxtHandle ctx; CredHandle hCred; SOCKET sock; SecPkgContext_StreamSizes ctxSizes; BOOL bLowMemoryIndicator;
} performhandshake(ctx, hCred, sock) CtxtHandle ctx; CredHandle hCred; SOCKET sock; {
SecPkgContext_StreamSizes ctxSizes;
struct diagose_internal diagose_internal_res; BOOL bLowMemoryIndicator = FALSE; SECURITY_STATUS acceptctxsecstat;
CtxtHandle* pctx = SecIsValidHandle(&ctx) ? &ctx : 0;
SecBufferDesc buff = { .ulVersion = SECBUFFER_VERSION,1,(SecBuffer[]) { [0] = {.BufferType = SECBUFFER_TOKEN} } }, * LastRecieved,
outbuff = { .ulVersion = SECBUFFER_VERSION,1,(SecBuffer[]) { [0] = {.BufferType = SECBUFFER_TOKEN} } },
* InBuff = &outbuff,
* OutBuff = &buff; DWORD attrs; TimeStamp nocare;
char(*d)[USHRT_MAX] = malloc(sizeof * d);
//OutBuff->pBuffers[0].cbBuffer = check_last_error_int(recv, sock, OutBuff->pBuffers[0].pvBuffer = *d, sizeof * d, 0);
do {
InBuff->pBuffers[0].cbBuffer = check_last_error_int(recv, sock, InBuff->pBuffers[0].pvBuffer = *d, sizeof * d, 0);
switch (acceptctxsecstat = diagnose(AcceptSecurityContext, &hCred, pctx, InBuff, ctxflags, 0, &ctx, OutBuff, &attrs, &nocare))
{
case SEC_I_CONTINUE_NEEDED:
case_continue_needed:
//, server, ctxflags, 0, SECURITY_NATIVE_DREP,
//diagnose(CompleteAuthToken, &ctx, OutBuff),
//OutBuff->pBuffers[0].cbBuffer = check_last_error_int(recv, sock, OutBuff->pBuffers[0].pvBuffer = *d, sizeof * d, 0),
check_last_error_int(send, sock, OutBuff->pBuffers[0].pvBuffer, OutBuff->pBuffers[0].cbBuffer, 0);
FreeContextBuffer(OutBuff->pBuffers[0].pvBuffer);
LastRecieved = OutBuff; swap_ptr64(InBuff, OutBuff);
pctx = &ctx;
break;
case SEC_I_COMPLETE_NEEDED:
case SEC_I_COMPLETE_AND_CONTINUE:
diagnose(CompleteAuthToken, pctx, OutBuff);
switch (acceptctxsecstat) {
case SEC_I_COMPLETE_NEEDED:
goto end;
case SEC_I_COMPLETE_AND_CONTINUE:
goto case_continue_needed;
}
default: goto end;
}
} while (/*OutBuff->pBuffers[0].cbBuffer != SOCKET_ERROR && OutBuff->pBuffers[0].cbBuffer*/true);
end:
diagnose(QueryContextAttributes, &ctx, SECPKG_ATTR_STREAM_SIZES, &ctxSizes);
free(d);
return (struct performhandshake) { ctx, hCred, sock, ctxSizes, bLowMemoryIndicator };
}
main() {
WSADATA wsadat; WSAStartup(MAKEWORD(2, 2), &wsadat), getaddrinfo("<local-ip>", "993", (struct addrinfo[])
{ {.ai_family = AF_INET, .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_TCP, .ai_flags = AI_PASSIVE}
}, & addrinfo);
InitializeCriticalSection(&crit);
HCERTSTORE hMyCertStore = NULL;
PCCERT_CONTEXT aCertContext = NULL;
//-------------------------------------------------------
// Open the My store, also called the personal store.
// This call to CertOpenStore opens the Local_Machine My
// store as opposed to the Current_User's My store.
hMyCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
X509_ASN_ENCODING,
0,
CERT_SYSTEM_STORE_CURRENT_USER,
L"MY");
if (hMyCertStore == NULL)
printf("Error opening MY store for server.\n");
//-------------------------------------------------------
// Search for a certificate with some specified
// string in it. This example attempts to find
// a certificate with the string "example server" in
// its subject string. Substitute an appropriate string
// to find a certificate for a specific user.
aCertContext = CertFindCertificateInStore(hMyCertStore,
X509_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR_A,
server, // use appropriate subject name
NULL
);
if (aCertContext == NULL)
printf("Error retrieving server certificate.");
CertCloseStore(hMyCertStore, 0);
char buff[USHRT_MAX];
SOCKET conn = beginconn(&buff);
struct performhandshake reshandshake;
CredHandle hCred; TimeStamp nocare; struct diagose_internal diagose_internal_res; BOOL bLowMemoryIndicator = FALSE;
diagnose(AcquireCredentialsHandle, 0, UNISP_NAME, SECPKG_CRED_BOTH, 0, &(SCHANNEL_CRED){.dwVersion = SCHANNEL_CRED_VERSION, .hRootStore = hMyCertStore,
.cCreds = 1, .paCred = (PCCERT_CONTEXT[]){ aCertContext }, .grbitEnabledProtocols = SP_PROT_TLS1_0_SERVER }, 0, 0, & hCred, & nocare);
SecInvalidateHandle(&reshandshake.ctx) reshandshake = performhandshake(reshandshake.ctx, hCred, conn);
return;
}
<local-ip> and <cert-name> hide my real local ip and server/cert name.
I am trying to implement multiple clients and multiple servers using Sun-RPC. However, I am unable to run multiple servers at the same time to begin with.
Here's the IDL
const MAXLEN = 1024;
typedef string filename<MAXLEN>;
typedef int vtimestamp[5];
typedef int sender_id;
typedef int recv_id;
struct request {
filename name;
int start;
vtimestamp ts;
sender_id sid;
recv_id rid;
};
typedef struct request request;
typedef opaque filepart[MAXLEN];
struct partreceive {
filepart data;
int bytes;
vtimestamp ts;
sender_id sid;
recv_id rid;
};
typedef struct partreceive partreceive;
struct partsend {
filename name;
filepart data;
int bytes;
vtimestamp ts;
sender_id sid;
recv_id rid;
};
typedef struct partsend partsend;
union readfile_res switch (int errno) {
case 0:
partreceive part;
default:
void;
};
program FTPROG {
version FTVER {
readfile_res retrieve_file(request *) = 1;
int send_file(partsend *) = 2;
} = 1;
} = 0x31240000;
Here's the server 1's code:
#include <rpc/rpc.h>
#include <stdio.h>
#include "maekawa.h"
extern __thread int errno;
int Id = 1;
char fileLog[50] = "server_1_file_log.txt";
int cur_ts[5] = {0};
readfile_res* retrieve_file_1_svc(request *req, struct svc_req *rqstp)
{
printf("%d",req->rid);
if(req->rid == Id)
{
FILE *file;
char data[1024];
int bytes,i;
static readfile_res res;
//update the timestamp
for(i=0;i<5;i++)
cur_ts[i] = res.readfile_res_u.part.ts[i];
char fn[3*MAXLEN] = "storage/";
strcat(fn,req->name);
file = fopen(fn, "rb");
if (file == NULL)
{
res.errno = errno;
return (&res);
}
fseek (file, req->start, SEEK_SET);
bytes = fread(res.readfile_res_u.part.data, 1, 1024, file);
res.readfile_res_u.part.bytes = bytes;
res.readfile_res_u.part.sid = Id;
res.readfile_res_u.part.rid = req->sid;
res.errno = 0;
fclose(file);
//log the transaction
FILE *log = NULL;
log = fopen(fileLog, "a");
fprintf(log,"R,");
for(i=0;i<5;i++)
fprintf(log,"%d,",cur_ts[i]);
fprintf(log,"%d,",req->sid);
fprintf(log,"%s,",req->name);
fprintf(log,"%d\n",req->start);
fclose(log);
return (&res);
}
}
int* send_file_1_svc(partsend *rec, struct svc_req *rqstp)
{
int i;
printf("%d",rec->rid);
if(rec->rid == Id)
{
FILE *file;
int write_bytes;
static int result;
cur_ts[Id]++;
//update the timestamp
for(i=0;i<5;i++)
cur_ts[i] = rec->ts[i];
char fn[3*MAXLEN] = "storage/";
strcat(fn,rec->name);
file = fopen(fn, "a");
if (file == NULL) {
result = errno;
return &result;
}
write_bytes = fwrite(rec->data, 1, rec->bytes, file);
fclose(file);
result = 0;
//log the transaction
FILE *log = NULL;
log = fopen(fileLog, "a");
fprintf(log,"S,");
for(i=0;i<5;i++)
fprintf(log,"%d,",cur_ts[i]);
fprintf(log,"%d,",rec->sid);
fprintf(log,"%s\n",rec->name);
fclose(log);
return &result;
}
}
Here's Server 2 :
#include <rpc/rpc.h>
#include <stdio.h>
#include "maekawa.h"
extern __thread int errno;
int Id = 2;
char fileLog[50] = "server_2_file_log.txt";
int cur_ts[5] = {0};
readfile_res* retrieve_file_1_svc(request *req, struct svc_req *rqstp)
{
printf("%d",req->rid);
if(req->rid == Id)
{
FILE *file;
char data[1024];
int bytes,i;
static readfile_res res;
//update the timestamp
for(i=0;i<5;i++)
cur_ts[i] = res.readfile_res_u.part.ts[i];
char fn[3*MAXLEN] = "storage/";
strcat(fn,req->name);
file = fopen(fn, "rb");
if (file == NULL)
{
res.errno = errno;
return (&res);
}
fseek (file, req->start, SEEK_SET);
bytes = fread(res.readfile_res_u.part.data, 1, 1024, file);
res.readfile_res_u.part.bytes = bytes;
res.readfile_res_u.part.sid = Id;
res.readfile_res_u.part.rid = req->sid;
res.errno = 0;
fclose(file);
//log the transaction
FILE *log = NULL;
log = fopen(fileLog, "a");
fprintf(log,"R,");
for(i=0;i<5;i++)
fprintf(log,"%d,",cur_ts[i]);
fprintf(log,"%d,",req->sid);
fprintf(log,"%s,",req->name);
fprintf(log,"%d\n",req->start);
fclose(log);
return (&res);
}
}
int* send_file_1_svc(partsend *rec, struct svc_req *rqstp)
{
int i;
printf("%d",rec->rid);
if(rec->rid == Id)
{
FILE *file;
int write_bytes;
static int result;
cur_ts[Id]++;
//update the timestamp
for(i=0;i<5;i++)
cur_ts[i] = rec->ts[i];
char fn[3*MAXLEN] = "storage/";
strcat(fn,rec->name);
file = fopen(fn, "a");
if (file == NULL) {
result = errno;
return &result;
}
write_bytes = fwrite(rec->data, 1, rec->bytes, file);
fclose(file);
result = 0;
//log the transaction
FILE *log = NULL;
log = fopen(fileLog, "a");
fprintf(log,"S,");
for(i=0;i<5;i++)
fprintf(log,"%d,",cur_ts[i]);
fprintf(log,"%d,",rec->sid);
fprintf(log,"%s\n",rec->name);
fclose(log);
return &result;
}
}
Here's my client 1 code.
#include <rpc/rpc.h>
#include <stdio.h>
#include <string.h>
#include "maekawa.h"
extern __thread int errno;
int Id = 4;
char fileLog[50] = "client_1_file_log.txt";
int cur_ts[5] = {0};
//Explicit Replication due to lack of Active Directory Service in NFS.
int idMap[4] = {0,2,3,1};
char hostMap[4][MAXLEN] = {"","localhost","localhost","localhost"};
int get_file(char *host, char *name, int serverno) //just to ensure all servers are not operating on same domain, we use serverno.
{
CLIENT *clnt;
int total_bytes = 0, write_bytes;
readfile_res *result;
request req;
FILE *file;
req.name = name;
req.start = 0;
int i;
req.sid = Id;
req.rid = serverno;
clnt = clnt_create(host, FTPROG, FTVER, "tcp");
if (clnt == NULL)
{
clnt_pcreateerror(host);
exit(1);
}
file = fopen(name, "wb");
FILE *log = NULL;
log = fopen(fileLog, "a");
while (1)
{
cur_ts[Id]++;
for(i=0;i<5;i++)
req.ts[i] = cur_ts[i];
req.start = total_bytes;
result = retrieve_file_1(&req, clnt);
if (result == NULL)
{
clnt_perror(clnt, host);
exit(1);
}
if (result->errno != 0)
{
errno = result->errno;
perror(name);
exit(1);
}
write_bytes = fwrite(result->readfile_res_u.part.data, 1, result->readfile_res_u.part.bytes, file);
total_bytes += result->readfile_res_u.part.bytes;
//log the transaction
fprintf(log,"G,");
for(i=0;i<5;i++)
fprintf(log,"%d,",cur_ts[i]);
fprintf(log,"%d,",req.sid);
fprintf(log,"%s,",req.name);
fprintf(log,"%d\n",req.start);
if (result->readfile_res_u.part.bytes < MAXLEN)
break;
}
fclose(file);
fclose(log);
return 0;
}
int put_file(char *host, char *name, int serverno)//just to ensure all servers are not operating on same domain, we use serverno.
{
CLIENT *clnt;
char data[1024];
int total_bytes = 0, read_bytes;
int *result;
partsend part;
FILE *file;
int i;
part.sid = Id;
part.rid = serverno;
clnt = clnt_create(host, FTPROG, FTVER, "tcp");
if (clnt == NULL)
{
clnt_pcreateerror(host);
exit(1);
}
file = fopen(name, "r");
part.name = name;
FILE *log = NULL;
log = fopen(fileLog, "a");
for(i=0;i<5;i++)
part.ts[i] = cur_ts[i];
while (1)
{
part.bytes = total_bytes;
read_bytes = fread(part.data, 1, MAXLEN, file);
total_bytes += read_bytes;
part.bytes = read_bytes;
result = send_file_1(&part, clnt);
if (result == NULL)
{
clnt_perror(clnt, host);
exit(1);
}
if (*result != 0)
{
errno = *result;
perror(name);
exit(1);
}
for(i=0;i<5;i++)
cur_ts[i] = part.ts[i];
//log the transaction
fprintf(log,"P,");
for(i=0;i<5;i++)
fprintf(log,"%d,",cur_ts[i]);
fprintf(log,"%d,",part.sid);
fprintf(log,"%s,",part.name);
fprintf(log,"%d\n",part.bytes);
if (read_bytes < MAXLEN)
break;
}
fclose(file);
fclose(log);
return 0;
}
int read_command(char *host)
{
char command[MAXLEN], filepath[MAXLEN];
int serverno;
printf("> ");
fflush(stdin);
scanf("%s",command);
if(strcmp(command, "exit") == 0)
{
exit(0);
}
scanf(" %s %d",filepath,&serverno);
if(serverno<=0 || serverno>=4)
{
printf("Choose a server number in [1,2,3].");
}
else
{
if (strcmp(command, "get") == 0)
{
return get_file(host,filepath,serverno);
}
else if(strcmp(command, "put") == 0)
{
int updateResult,replicationResult=-1;
updateResult = put_file(host,filepath,serverno);
/*if(updateResult == 0)
{
//explicit Replication
replicationResult = put_file(hostMap[serverno],filepath,idMap[serverno]);
}
else if(replicationResult != 0)
{
printf("Replication failed.");
}*/
return 0;
}
else
{
return -1;
}
}
}
int main(int argc, char *argv[])
{
int result;
if (argc != 2)
{
fprintf(stderr, "usage: %s host\n", argv[0]);
exit(1);
}
while(TRUE)
{
result = read_command(argv[1]);
}
return 0;
}
I am trying to use Maekawa algorithm to implement file replication explicitly through my client. However, it's being ridiculously impossible to run multiple servers on RPC on same localhost. Is it even possible? If I run the code on different computers, would that work?
Currently I am receiving segmentation fault from the server that is run after the first server. The client just blurts out Connection reset by peer. Is there anyway I can make this work?
using libstrophe, can I reconnect automatically when I loose connection.
I used the following code on the client side:
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
const int error, xmpp_stream_error_t * const stream_error,
void * const userdata)
{
if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n");
}
else {
fprintf(stderr, "DEBUG: disconnected\n");
}
}
void main()
{
xmpp_log_t *log;
char *jid;
jid = strdup("test#domain.com")
xmpp_initialize();
log = xmpp_get_default_logger(XMPP_LEVEL_ERROR);
cwmp->xmpp_ctx = xmpp_ctx_new(NULL, log);
cwmp->xmpp_conn = xmpp_conn_new(cwmp->xmpp_ctx);
xmpp_conn_set_jid(cwmp->xmpp_conn, jid);
xmpp_conn_set_pass(cwmp->xmpp_conn, cwmp->xmpp_param.password);
xmpp_connect_client(cwmp->xmpp_conn, NULL, 0, conn_handler, cwmp->xmpp_ctx);
xmpp_run(cwmp->xmpp_ctx);
}
when the client is connected for the first time, i get the message "DEBUG: connected"
When the server goes done, i get the message "DEBUG: disconnected". but when the server is up for the second time, the client is not reconnected automatically.
Libstrophe doesn't reconnect automatically. Starting from libstrophe-0.9.0 xmpp_conn_t object can be reconnected without loosing login information and user handlers:
#include <stdio.h>
#include <strophe.h>
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
const int error, xmpp_stream_error_t * const stream_error,
void * const userdata)
{
if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n");
} else {
fprintf(stderr, "DEBUG: disconnected, reconnecting...\n");
xmpp_connect_client(conn, NULL, 0, conn_handler, userdata);
}
}
int main()
{
xmpp_log_t *log;
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
const char *jid = "test#domain.com";
const char *pass = "password";
xmpp_initialize();
log = xmpp_get_default_logger(XMPP_LEVEL_ERROR);
ctx = xmpp_ctx_new(NULL, log);
conn = xmpp_conn_new(ctx);
xmpp_conn_set_jid(conn, jid);
xmpp_conn_set_pass(conn, pass);
xmpp_connect_client(conn, NULL, 0, conn_handler, NULL);
xmpp_run(ctx);
xmpp_conn_release(conn);
xmpp_ctx_free(ctx);
xmpp_shutdown();
return 0;
}
In versions before 0.9.0 user can't reuse xmpp_conn_t object after disconnection and needs to create new one. Example for libstrophe-0.8.8 and older:
#include <stdio.h>
#include <strophe.h>
#define TIMEOUT 1000
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
const int error, xmpp_stream_error_t * const stream_error,
void * const userdata)
{
int *reconnect = userdata;
if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n");
} else {
fprintf(stderr, "DEBUG: disconnected, reconnecting...\n");
*reconnect = 1;
}
}
int main()
{
xmpp_log_t *log;
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
const char *jid = "test#domain.com";
const char *pass = "password";
int reconnect;
xmpp_initialize();
log = xmpp_get_default_logger(XMPP_LEVEL_ERROR);
ctx = xmpp_ctx_new(NULL, log);
while (1) {
conn = xmpp_conn_new(ctx);
xmpp_conn_set_jid(conn, jid);
xmpp_conn_set_pass(conn, pass);
xmpp_connect_client(conn, NULL, 0, conn_handler, &reconnect);
reconnect = 0;
while (!reconnect)
xmpp_run_once(ctx, TIMEOUT);
xmpp_conn_release(conn);
}
xmpp_ctx_free(ctx);
xmpp_shutdown();
return 0;
}
The goal of this server is to transfer small files, in a relatively simple manner. I have finished the code and it all compiles without error, and when I try to run it, the server side has no problems, but the client side gives an error in the socket binding and a segmentation fault. I was wondering what in the code was causing these problems.
Server:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <string.h>
#include <unistd.h>
#include <crypt.h>
#include <time.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <netinet/ip.h>
void timestamp_ss()
{
time_t current_time;
char* log_time;
log_time = ctime(¤t_time);
log_file_ss(log_time);
}
send_data(int sockfd, char info_to_send)
{
char eof_buffer[4] = "\EOF";
int sent_data, data_to_send;
data_to_send = strlen(&info_to_send);
while(data_to_send > 0)
{
sent_data = send(sockfd, &info_to_send, data_to_send, 0);
if(sent_data == -1)
perror("There was a problem in the sending of data!");
data_to_send -= sent_data;
info_to_send += sent_data;
}
send(sockfd, eof_buffer, 4, 0);
}
int recv_data(int sockfd, char *dest_buffer)
{
#define EoF "\EOF"
unsigned char *buffer;
int eof_match = 0, eof_size = 2;
buffer = dest_buffer;
while(recv(sockfd, buffer, 1, 0) == 1)
{
if(*buffer == EoF[eof_match])
{
eof_match++;
if(eof_match = eof_size)
{
*(buffer+1-eof_size) = '\0';
return strlen(dest_buffer);
}
else
{
eof_match = 0;
}
}
buffer++;
}
return 0;
}
int password_ss(char *password_attempt, char *password_actual)
{
char key[] = { "ZjQXStSi" };
char ivec[] = {"7eNP3U1b" };
char des_dec[] = { "DES_DECRYPT" };
char des_hw[] = { "DES_HW" };
int l, i;
l = strlen(password_attempt);
i = cbc_crypt(key, password_attempt, l, *des_dec | *des_hw, ivec);
if(i < 0)
error_escape("In decryption");
if(password_attempt == password_actual)
return 1;
else
return 0;
}
int log_file_ss(char *log_message)
{
char logfile[]= "/Server/log/C-File-Transfer-Server-Log";
int log_fd, len;
log_fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT);
len = strlen(log_message);
write(log_fd, log_message, len);
}
void file_to_client_ss(int sockfd, struct sockaddr_in *client_addr_ptr)
{
char file_req_c[128];
char buffer[10000];
char files[256];
char pass_attempt[128];
char *error_403[20] = { "Error 403: Forbidden" };
char *error_404[25] = { "Error 404: File Not Found" };
char *pass_path[20] = { "/server/log/PASSWORD" };
char *pass_req[50] = { "This File Requires A Password, Please Enter It Now" };
char *no_pass[37] = { "This File Does Not Require A Password" };
char *username;
char *file_s;
char *string;
char *file1_path[26] = { "/server/received_files/r_w" };
char *file2_path[24] = { "/server/received_files/r" };
char *file3_path[24] = { "/server/received_files/n" };
char file_data;
FILE *cs, *ps;
int file1, file2, file3, file_test, pass;
cs = fopen(file_req_c, "r");
recv_data(sockfd, username);
chdir("/server/log/PASSWORD");
ps = fopen(username, "r");
fread(files, 1, file_size(ps), ps);
chdir("/server");
recv_data(sockfd, file_req_c);
file_test = file_exist(file1_path, file_req_c);
if(file_test = -1)
{
file_test = file_exist(file2_path, file_req_c);
if(file_test = -1)
{
file_test = file_exist(file3_path, file_req_c);
if(file_test = -1)
{
error_escape("Opening file request from client");
}
else
{
send_data(sockfd, **pass_req);
recv_data(sockfd, pass_attempt);
pass = password_ss(pass_attempt, files);
if(pass == 0)
{
send_data(sockfd, **error_403);
error_escape("Wrong Password");
}
else
{
if(file_exist(pass_path, username) == 0)
send_data(sockfd, *file_req_c);
else
errror_escape("Error in sending file");
}
}
}
else
{
chdir(*file2_path);
file_data = fread(buffer, 1, file_size(cs), cs);
send_data(sockfd, **no_pass);
send_data(sockfd, file_data);
}
}
else
{
chdir(*file1_path);
file_data = fread(buffer, 1, file_size(cs), cs);
send_data(sockfd, **no_pass);
send_data(sockfd, file_data);
}
}
int file_size(FILE *stream)
{
off_t file_len;
fseek(stream, 0, SEEK_END);
file_len = ftell(stream);
fclose(stream);
return file_len;
}
int file_exist(char *file_path, char *file_name)
{
DIR *dp;
FILE *fc;
struct dirent *ep;
dp = opendir(file_path);
if(dp == NULL)
perror("Opening path");
else
chdir(file_path);
closedir(dp);
fc = fopen(file_name, "r");
if(fc == NULL)
{
perror("Opening file");
return(-1);
}
else {
return(0);
}
}
error_escape(char *problem)
{
char error_message[256];
strcpy(error_message, "! There Has Been An Error !");
strncat(error_message, problem, 173);
perror("Error: ");
log_file_ss(error_message);
timestamp_ss();
exit(-1);
}
void file_accept_ss(int sockfd, struct sockaddr_in *client_addr_ptr)
{
char client_request[512], username[256], file_content[8192], buf[8192];
char *client_r_w[26] = { "/server/received_files/r_w" };
char *client_r[24] = { "/server/received_files/r" };
char *client_n[24] = { "/server/received_files/n" };
char *search_string_read[6] = { "O_READ" };
char *search_string_w[14] = { "O_READANDWRITE" };
char *password_path[20] = { "/server/log/PASSWORD" };
char *mkdir[37] = { "/server/log/PASSWORD" };
char *ret;
char file_data, recv_i;
char password[256];
int change_dir_test, recv_check;
FILE *fn, *Ps;
recv_data(sockfd, username);
recv_data(sockfd, password);
strcat(*mkdir, username);
strcat(*mkdir, password);
fn = fopen(*mkdir, "a");
chdir(*password_path);
Ps = fopen(*mkdir, "a");
chdir("/server");
recv_i = fread(buf, 1, file_size(fn), fn);
recv_check = recv_data(sockfd, &recv_i);
if(recv_check = -1)
error_escape("! There Was An Error In The Receiving Of The File From The Client !");
fread(file_content, 8, file_size(fn), fn);
ret = strstr(file_content, *search_string_read);
if(ret = NULL)
{
change_dir_test = chdir(*client_n);
if(change_dir_test = -1)
error_escape("! There Was An Error In The Changing Of Directories !");
else
{
file_data = fread(buf, 1, file_size(Ps), Ps);
recv_data(sockfd, &file_data);
chdir(*client_n);
strcat(*client_n, username);
rename(username, *client_n);
}
}
if(ret = *search_string_read)
{
change_dir_test = chdir(*client_r);
if(change_dir_test = -1)
error_escape("! There Was An Error In The Changing Of Directories !");
else
{
chdir(*client_r);
rename(username, *client_r);
}
}
if(ret = *search_string_w)
{
change_dir_test = chdir(*client_r_w);
if(change_dir_test = -1)
error_escape("! There Was An Error In The Changing Of Directories !");
else
{
chdir(*client_r_w);
rename(username, *client_r_w);
}
}
log_file_ss("Client:");
log_file_ss(username);
timestamp_ss();
}
int main(void)
{
struct sockaddr_in server, client;
int sockfd, bind_test, listen_test, client_sockfd, sin_size;
sockfd = socket(PF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
error_escape("Making Socket");
server.sin_family = AF_INET;
server.sin_port = htons(80);
server.sin_addr.s_addr = INADDR_ANY;
bind_test = bind(sockfd, (struct sockaddr *)&server, sizeof(server));
if(bind_test < 0)
error_escape("Binding Socket");
listen_test = listen(sockfd, 20);
if(listen_test < 0)
error_escape("Listening");
while(1)
{
sin_size = sizeof(struct sockaddr_in);
client_sockfd = accept(sockfd, (struct sockaddr *)&client, &sin_size);
file_accept_ss(client_sockfd, &client);
file_to_client_ss(client_sockfd, &client);
}
shutdown(client_sockfd, SHUT_RDWR);
return 0;
}
Client:
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
#include <crypt.h>
#include <sys/socket.h>
#include <netinet/ip.h>
send_data(int sockfd, char info_to_send)
{
char eof_buffer[4] = "\EOF";
int sent_data, data_to_send;
data_to_send = strlen(&info_to_send);
while(data_to_send > 0)
{
sent_data = send(sockfd, &info_to_send, data_to_send, 0);
if(sent_data == -1)
perror("There was a problem in the sending of data!");
data_to_send -= sent_data;
info_to_send += sent_data;
}
send(sockfd, eof_buffer, 4, 0);
}
send_file_cs(int sockfd)
{
char file_buffer[4096], file_name[256];
char name, username, password;
char *search_st, file_data, *file_location;
int perm_choice, password_max = 20, ch;
off_t size_of_file;
FILE *fp;
printf("%s\n", "Please enter the path to file you would like to move to server:");
scanf("%s", &file_location);
ch = chdir(file_location);
if(ch == -1)
perror("! There Has Been An Error In The Directory Path !");
else
chdir(file_location);
printf("%s\n", "Now Enter The Name Of The File You Would Like To Transfer:");
printf("%s\n", "! Warning, The File May Not Exceed 4 kilobytes !");
scanf("%s", &name);
printf("%s", "What would you like the username for this file to be?");
name = *file_name;
size_of_file = file_size(fp);
if(size_of_file > 4096)
printf("! The File Is Greater Than 4 Kilobytes !");
fp = fopen(file_name, "r+");
printf("%s\n", "What Permissions Would You Like The File To Have?\n (1) For Other Clients To See The File\n (2) For Other CLients To See But Not Be Able To Access\n (3) Other Clients Cannot See Or Access The File");
scanf("%d", &perm_choice);
if(perm_choice > 3 || perm_choice < 1)
perror("! Incorrect Permissions !");
if(perm_choice = 1)
{
search_st = "O_READ";
fopen(file_name, "a");
fwrite(search_st, 1, strlen(search_st), fp);
}
if(perm_choice = 2)
{
search_st = "O_READANDWRITE";
fopen(file_name, "a");
fwrite(search_st, 1, strlen(search_st), fp);
}
if(perm_choice = 3)
{
search_st = "O_NOACCESS";
fopen(file_name, "a");
fwrite(search_st, 1, strlen(search_st), fp);
printf("%s", "Please enter a password");
scanf("%s", &password);
send_data(sockfd, password);
}
file_data = fread(file_buffer, 1, 4096, fp);
send_data(sockfd, file_data);
}
int recv_data(int sockfd, char *dest_buffer)
{
#define EoF "\EOF"
unsigned char *buffer;
int eof_match = 0, eof_size = 2;
buffer = dest_buffer;
while(recv(sockfd, buffer, 1, 0) == 1)
{
if(*buffer == EoF[eof_match])
{
eof_match++;
if(eof_match = eof_size)
{
*(buffer+1-eof_size) = '\0';
return strlen(dest_buffer);
}
else
{
eof_match = 0;
}
}
buffer++;
}
return 0;
}
int password_cs(int max_length, int sockfd)
{
char salt[] = { "ZjQXStSi" };
char ivec[] = { "7eNP3U1b" };
char des_enc[] = { "DES_ENCRYPT" };
char des_hw[] = { "DES_HW" };
char password;
char *ret, *ret2;
int l, i;
printf("%s", "Please set your password:");
scanf("%s", &password);
l = strlen(&password);
if(l > max_length)
printf("%s : %d", "Password must be less than", max_length);
i = cbc_crypt(salt, password, l, *des_enc | *des_hw, ivec);
if(i < 0)
perror("In erncryption");
send_data(sockfd, password);
return 0;
}
int file_size(FILE *stream)
{
off_t file_len;
fseek(stream, 0, SEEK_END);
file_len = ftell(stream);
fclose(stream);
return file_len;
}
int file_exist(char *file_path, char *file_name)
{
DIR *dp;
FILE *fc;
struct dirent *ep;
dp = opendir(file_path);
if(dp == NULL)
perror("Opening path");
else
chdir(file_path);
closedir(dp);
fc = fopen(file_name, "r");
if(fc == NULL)
{
perror("Opening file");
return(-1);
}
else {
return(0);
}
}
void client_request_file_cs(int sockfd)
{
char password_buf[128], file[4096], recv_file[256];
char *requires[16] = { "Requires" };
char *str, *restr, *file_contents, *name2, *path, *rebuf;
char file_req, password, name, username;
int test;
FILE *re;
printf("%s\n", "What file would you like from the server?");
scanf("%s", &file_req);
printf("%s", "What is the user name associated with the file?");
scanf("%s", &username);
send_data(sockfd, username);
printf("%s\n", "Where Would You Like The File To Be Put, Please Enter The Path:");
scanf("%s", &path);
test = chdir(path);
if(test == -1)
printf("%s\n", "Invalid Path");
printf("%s\n", "What Would You Like To Call The File?");
scanf("%s", &name);
name2 = &name;
re = fopen(name2, "w");
fread(file_contents, 1, file_size(re), re);
send_data(sockfd, file_req);
recv_data(sockfd, password_buf);
printf("%s\n", password_buf);
str = strstr(password_buf, *requires);
if(str == NULL)
recv_data(sockfd, file);
else
{
scanf("%s", &password);
send_data(sockfd, password);
}
recv_data(sockfd, rebuf);
fwrite(rebuf, 1, sizeof(rebuf), re);
fclose(re);
restr = strstr(file_contents, "error_");
if(restr != NULL)
printf("%s\n", re);
}
int main(void)
{
struct sockaddr_in client, server_addr;
int sockfd, connected;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(80);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
printf("%s", "Error opening socket");
connected = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
if(connected == -1)
printf("%s", "Error binding socket");
send_file_cs(sockfd);
client_request_file_cs(sockfd);
shutdown(sockfd, SHUT_RDWR);
return 0;
}
Thank you in advance for any help.
Among other valid questions/points in the comments, memory allocation in your code may be contributing to your segmentation fault...
char *file_location (and others) are being used as if memory has been allocated.
Your use of the variable in scanf("%s", &file_location); before allocating memory invokes undefined behavior, and most likely will be (at least partially) responsible for your run-time errors.
add memory before the scanf statement. Here are two examples of how to do that:
1) Create heap memory (using [m][c]alloc):
char *file_location;
file_location = malloc(MAX_FILENAME_LEN);//or your systems value for max directory length
if(file_location)
{
scanf("%s", &file_location);
...
2) Use stack memory: (point your pointer to a place with memory)
char file_name[256];
char *file_location = file_name;
file_location = file_name;
scanf("%s", &file_location);
But the simplest way (if there is nothing constraining you to use a pointer) would be to just create the variable on the stack and use it in scanf(...):
char file_location[256];
scanf("%s", &file_location);
There are other variables (besides file_location) in your example code that need memory before use. Remember to free any variable with memory created on the heap when finished using.
bind error
There is no bind error here. The client doesn't do a bind at all. It gets an error connecting the socket, and then misleadingly prints 'bind error'.
The connect error is because you aren't initializing the address field of the target you're trying to connect to.
NB you don't need \EOF. recv() will return zero when the peer disconnects, which you aren't checking for. You aren't checking it for errors either. This is very strange code. – EJP 19 mins ago
What would be the best way to do an scp or sftp copy in a Unix environment using C?
I'm interested in knowing the best library to use and an example if at all possible. I'm working on a Solaris server with the Sun tools installed.
Try Libcurl
libcurl is a free and easy-to-use client-side URL transfer library, supporting DICT, FILE, > FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet and TFTP. libcurl supports SSL certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, cookies, user+password authentication (Basic, Digest, NTLM, Negotiate, Kerberos), file transfer resume, http proxy tunneling and more!
libcurl is highly portable, it builds and works identically on numerous platforms, including Solaris, NetBSD, FreeBSD, OpenBSD, Darwin, HPUX, IRIX, AIX, Tru64, Linux, UnixWare, HURD, Windows, Amiga, OS/2, BeOs, Mac OS X, Ultrix, QNX, OpenVMS, RISC OS, Novell NetWare, DOS and more...
I'm not really a C expert, but I think you can use system() to run OS commands. This would assume that you don't actually want to re-implement scp, just use it.
I've always just used the system() command. Of course doing this requires that you have SSH keys properly installed between the client and target machine so that it doesn't prompt for the password.
You can use libssh for sftp. I put some code here for you which has Pause/Resume and works on Windows. For Linux, you need to replace local file handling functions. I cannot copy the entire class because it will exceed this website limit. Change username and password and hostname to proper equivalents of your SFTP server:
int main(array<System::String ^> ^args)
{
//Console::WriteLine(L"Hello World");
pSFTPConnector sshc = new SFTPConnector(L".\\", L"127.0.0.1", 22, L"iman", L"iman"); // Change the hostname, port, username, password to your SFTP server, your credentials
//FILE *nullfile = fopen("null", "w");
//sshc->setLogFile(nullfile);
sshc->setVerbosity(SSH_LOG_RARE); // You can change the verbosity as appropriate for you
int i = sshc->InitSession();
i = sshc->ConnectSession();
i = sshc->InitSFTP();
//i = sshc->SFTPrename("renamed_myfile.txt", "myfile.txt"); // Change these file names
//i = sshc->Makedir("sftpdir");
//i = sshc->testUploadFile("myfile2.txt", "1234567890testfile");
// Change these file names to whatever appropriate
//i = sshc->SFTPget("c:\\testdir\\Got_CAR_HIRE_FINAL_test.jpg", "CAR_HIRE_FINAL_test.jpg", 64*1024);
i = sshc->SFTPget("c:\\testdir\\get_downloaded_CAR_HIRE_FINAL.jpg", "CAR_HIRE_FINAL.jpg", 64 *1024);
i = sshc->SFTPreget("c:\\testdir\\reget_downloaded_CAR_HIRE_FINAL.jpg", "CAR_HIRE_FINAL.jpg", 64 * 1024);
i = sshc->SFTPput("c:\\testdir\\CAR_HIRE_FINAL.jpg", "put_CAR_HIRE_FINAL.jpg", 64 * 1024);
i = sshc->SFTPreput("c:\\testdir\\CAR_HIRE_FINAL.jpg", "reput_CAR_HIRE_FINAL.jpg", 64 * 1024);
delete sshc;
return 0;
}
typedef enum sshconerr {
E_OK = 1, E_SESSION_ALOC = -1, E_SSH_CONNECT_ERR = -2, E_SFTP_ALLOC = -3, E_INIT_SFTP = -4, E_CREATE_DIR = -5, E_FILEOPEN_WRITE = -6, E_WRITE_ERR = -7,
E_FILE_CLOSE = -8, E_FILE_OPEN_READ = -9, E_INVALID_PARAMS = -10, E_SFTP_ERR = -11, E_SFTP_READ_ERR = -12, E_SFTP_READBYTES_ERR = -13, E_GET_FILEINF = -14,
E_LOCAL_FILE_NOTFOUND = -15, E_RENAME_ERR = -16, E_MEM_ALLOC = -17, E_LOCAL_FILE_READ = -18, E_LOCAL_FILE_RDWR = -19, E_REMOTEFILE_SEEK = -20,
E_REMOTE_FILE_OPEN = -21, E_DELETE_ERR = -22, E_RENAME_LOCAL_FILE = -23, E_LOCAL_DELETE_FILE = -24, E_FILEOPEN_RDONLY = -25, E_SFTP_READ_EOF = -26,
E_UNKNOWN = -999
} ESSHERR;
// Status of transfers;
typedef enum sftpstat{ES_DONE=0, ES_INPROGRESS, ES_FAILED, ES_STARTING, ES_PAUSED, ES_RESUMING, ES_CANCELLED, ES_NONE } ESFTPSTAT;
using namespace std;
// Statistics about the transfer;
typedef struct transferstatstruct {
string remote_file_name;
string local_file_name;
__int64 total_size;
__int64 transferred;
__int64 averagebps;
long long seconds_elapsed;
long long seconds_remained;
int percent;
ESFTPSTAT transferstate;
} TTransStat;
#define E_SESSION_NEW -1
// These libraries are required
#pragma comment(lib, "ssh.lib")
// This is the main class that does the majority of the work
typedef class CSFTPConnector {
private:
ssh_session session; // SSH session
sftp_session sftp; // SFTP session
sftp_file file; // Structure for a remote file
FILE *localfile; // Not used on Windows, but it could be local file pointer in Unix
FILE *logfile; // The file for writing logs, default is set to stderr
string filename; // File name of the transfer;
string localfilename; // File name of local file;
string tempfilename; // A temporary file name will be used during the transfer which is renamed when transfer is completed.
ESFTPSTAT transferstatus; // State of the transfer which has one of the above values (ESFTPSTAT)
time_t transferstarttime; // Time of start of the transfer
wchar_t username[SHORT_BUFF_LEN];
wchar_t password[SHORT_BUFF_LEN];
wchar_t hostname[SHORT_BUFF_LEN]; // Hostname of the SFTP server
wchar_t basedir[SHORT_BUFF_LEN]; // This base directory is the directory of the public and private key structure (NOT USED IN THIS VERSION)
int port; // Port of the server;
int verbosity; // Degree of verbosity of libssh
__int64 filesize; // Total number of bytes to be transferred;
DWORD local_file_size_hiDWORD; // Bill Gates cannot accept the file size
// without twisting the programmers, so
// he accepts them in two separate words
// like this
DWORD local_file_size_lowDWORD; // These two DWORDs when connected together comprise a 64 bit file size.
__int64 lfilesize; // Local file size
__int64 rfilesize; // Remote file size
__int64 transferred; // Number of bytes already transferred
bool pause; // Pause flag
TTransStat stats; // Statistics of the transfer
HANDLE localfilehandle; // Windows uses handles to manipulate files. this is the handle to local file.
ESSHERR CSFTPConnector::rwopen_existing_SFTPfile(char *fn); // Open a file on remote (server) read/write for upload
ESSHERR CSFTPConnector::rdopen_existing_SFTPfile(char *fn); // Open a file on remote (server) read only for download
ESSHERR createSFTPfile(char *fn); // Create a file on server;
ESSHERR writeSFTPfile(char *block, size_t blocksize); // Write a block of data to the open remote file
ESSHERR readSFTPfile(char *block, size_t len, size_t *bytesread); // Read a block of data from the open remote file
ESSHERR readSFTPfile(char *block, __int64 len, DWORD *bytesread);
ESSHERR closeSFTPfile(); // Closes the remote file;
ESSHERR openSFTPfile(char *fn); // Opens the remote file
ESSHERR getSFTPfileinfo(); // Gets information about the remote file
public:
wstring errstring; // The string describing last error
ESSHERR Err; // Error code of last error
CSFTPConnector(); // Default constructor;
CSFTPConnector(wchar_t *dir, wchar_t *hn, int hostport, wchar_t *un, wchar_t *pass); // Constructor
void setVerbosity(int v);
int getVerbosity();
ESSHERR InitSession(); // Must be called before doing any transfer
ESSHERR ConnectSession(); // Connects to the SSH server
ESSHERR InitSFTP(); // Must be called before doing any transfer
ESSHERR Makedir(char *newdir);
ESSHERR testUploadFile(char *fn, char *block); // Do not use this, only for test purposes for myself
ESSHERR SFTPput(char *lfn, char *rfn, size_t blocksize); // Upload a file from start
ESSHERR SFTPreput(char *lfn, char *rfn, size_t blocksize); // Checks for previouse interrupted transfer, then
// either continues the previous transfer (if
// there was any) or starts a new one (UPLOAD)
ESSHERR SFTPrename(char *newname, char *oldname); // Renames a remote file( must be closed)
ESSHERR CSFTPConnector::SFTPdelete(char *remfile); // Deletes a remote file
TTransStat getStatus(); // Gets statistics of the transfer
ESSHERR CSFTPConnector::SFTPget(char *lfn, char *rfn, size_t blocksize); // Downloads a file from the SFTP server
ESSHERR CSFTPConnector::SFTPreget(char *lfn, char *rfn, size_t blocksize); // Checks for a previous interrupted transfer,
// then either continues the previous transfer
// (if there was any) or starts a new one (DOWNLOAD).
void CancelTransfer();
void PauseTransfer();
void setLogFile(FILE *logf); // Sets the log file. If not set, standard
// error will be used. By default.
void CloseLocalFile();
void CloseRemoteFile();
~CSFTPConnector();
} SFTPConnector, *pSFTPConnector;
void CSFTPConnector::CloseLocalFile()
{
CloseHandle(localfilehandle);
}
void CSFTPConnector::CloseRemoteFile()
{
sftp_close(file);
}
void CSFTPConnector::setLogFile(FILE *logf)
{
logfile = logf;
}
void CSFTPConnector::CancelTransfer()
{
transferstatus = ES_CANCELLED;
}
void CSFTPConnector::PauseTransfer()
{
transferstatus = ES_PAUSED;
pause = true;
}
//----------------------------------------
ESSHERR CSFTPConnector::SFTPget(char *lfn, char *rfn, size_t blocksize)
{
DWORD result;
int rc;
BOOL bresult;
DWORD bytesread;
filesize = 0;
transferred = 0;
pause = false;
transferstatus = ES_NONE;
char *block;
struct stat st;
wchar_t temp[SHORT_BUFF_LEN];
size_t tempsize;
wstring wlfn;
int loopcounter = 0;
localfilename = lfn;
filename = rfn;
tempfilename = string(lfn) + ".sftp_temp";
mbstowcs_s(&tempsize, temp, tempfilename.c_str(), SHORT_BUFF_LEN);
localfilehandle = CreateFile(temp, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (localfilehandle == INVALID_HANDLE_VALUE)
{
transferstatus = ES_FAILED;
errstring = L"Could not open local file:" + wstring(temp) + L" for read and write";
Err = E_LOCAL_FILE_RDWR;
return E_LOCAL_FILE_RDWR;
}
lfilesize = 0;
transferred = 0;
block = (char*)malloc(blocksize + 1);
if (block == NULL) {
Err = E_MEM_ALLOC;
transferstatus = ES_FAILED;
errstring = L"Could not allocate memory for file block size";
CloseLocalFile();
return E_MEM_ALLOC;
}
result = rdopen_existing_SFTPfile((char *)rfn);
if (result == E_OK) {
getSFTPfileinfo();
filesize = rfilesize;
}
else
{
Err = E_REMOTE_FILE_OPEN;
transferstatus = ES_FAILED;
errstring = L"Could not open remote file";
CloseLocalFile();
delete block;
return E_REMOTEFILE_SEEK;
}
transferstatus = ES_STARTING;
sftp_file_set_blocking(file);
transferstarttime = time(NULL);
transferstatus = ES_INPROGRESS;
while (transferstatus != ES_FAILED &&
transferstatus != ES_PAUSED &&
transferstatus != ES_CANCELLED &&
transferstatus != ES_DONE)
{
loopcounter++;
result = readSFTPfile(block, blocksize, (size_t *)&bytesread);
if (result != E_OK && result!= E_SFTP_READ_EOF)
{
errstring = L"Error reading from remote SFTP server file.";
Err = (ESSHERR)result;
transferstatus = ES_FAILED;
CloseRemoteFile();
CloseLocalFile();
delete block;
return (ESSHERR)result;
}
if (result == E_SFTP_READ_EOF)
transferstatus = ES_DONE;
fprintf(logfile, "Read %d bytes from input file. Number of packets: %d, %llu from %llu bytes\n", bytesread, loopcounter, transferred, filesize);
bresult = WriteFile(localfilehandle, (LPVOID)block, bytesread, &bytesread, NULL);
if (bytesread < blocksize)
{
if (bresult == FALSE)
{
errstring = L"Error writing to local file.";
Err = E_LOCAL_FILE_RDWR;
transferstatus = ES_FAILED;
CloseRemoteFile();
CloseLocalFile();
delete block;
return E_LOCAL_FILE_RDWR;
}
else if (bytesread == 0)
{
errstring = L"Transfer done.";
Err = E_OK;
transferstatus = ES_DONE;
continue;
}
}
Err = E_OK;
if (pause == true)
transferstatus = ES_PAUSED;
if (bresult == TRUE && bytesread == 0)
{
// At the end of the file
transferstatus = ES_DONE;
}
Sleep(BLOCKTRANSDELAY);
if (loopcounter % 331 == 0)
Sleep(77 * BLOCKTRANSDELAY);
if (loopcounter % 3331 == 0)
Sleep(777 * BLOCKTRANSDELAY);
}
// Closing files
result = closeSFTPfile();
CloseHandle(localfilehandle);
Sleep(1000);
if (transferstatus == ES_DONE)
{
wchar_t temp2[SHORT_BUFF_LEN];
mbstowcs_s(&tempsize, temp2, lfn, SHORT_BUFF_LEN);
bresult = MoveFile(temp, temp2);
if (bresult != TRUE)
{
Err = E_RENAME_LOCAL_FILE;
errstring = L"Could not rename local file: " + wstring(temp);
transferstatus = ES_FAILED;
delete block;
return E_RENAME_LOCAL_FILE;
}
}
if (transferstatus == ES_CANCELLED)
{
wchar_t temp2[SHORT_BUFF_LEN];
mbstowcs_s(&tempsize, temp2, lfn, SHORT_BUFF_LEN);
bresult = DeleteFile(temp);
if (bresult != TRUE)
{
Err = E_LOCAL_DELETE_FILE;
errstring = L"Could not rename local file: " + wstring(temp);
transferstatus = ES_FAILED;
delete block;
return E_LOCAL_DELETE_FILE;
}
}
delete block;
return (ESSHERR) result;
}
TTransStat CSFTPConnector::getStatus()
{
stats.seconds_elapsed = time(NULL) - transferstarttime;
stats.averagebps = (transferred * 8) / stats.seconds_elapsed;
if (filesize > 0) {
stats.percent = (transferred *100)/ filesize;
stats.seconds_remained = ((filesize - transferred) * 8) / stats.averagebps;
}
else
{
stats.percent = -1;
stats.seconds_remained = -1;
}
stats.total_size = filesize;
stats.transferstate = transferstatus;
stats.remote_file_name = filename;
stats.local_file_name = localfilename;
return stats;
}
ESSHERR CSFTPConnector::SFTPrename(char *newname, char *oldname)
{
int rc = sftp_rename(sftp, oldname, newname);
if (rc != SSH_OK) {
return E_RENAME_ERR;
}
return E_OK;
}
ESSHERR CSFTPConnector::SFTPdelete(char *remfile)
{
int rc = sftp_unlink(sftp, remfile);
if (rc != SSH_OK) {
return E_DELETE_ERR;
}
return E_OK;
}
ESSHERR CSFTPConnector::SFTPreput(char *lfn, char *rfn, size_t blocksize)
{
ESSHERR result;
BOOL bresult;
DWORD bytesread;
filesize = 0;
transferred = 0;
pause = false;
transferstatus = ES_NONE;
char *block;
struct stat st;
wchar_t temp[SHORT_BUFF_LEN];
size_t tempsize;
wstring wlfn;
int loopcounter = 0;
localfilename = lfn;
//wlfn = wstring(lfn);
//localfile = fopen(lfn, L"r");
filename = rfn;
mbstowcs_s(&tempsize, temp, lfn, SHORT_BUFF_LEN);
//filesize = getFileSize(localfilename);
/*if (filesize < 0) {
transferstatus = ES_FAILED;
Err = E_LOCAL_FILE_NOTFOUND;
return E_LOCAL_FILE_NOTFOUND;
}*/
localfilehandle = CreateFile(temp, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (localfilehandle == INVALID_HANDLE_VALUE)
{
transferstatus = ES_FAILED;
Err = E_LOCAL_FILE_NOTFOUND;
return E_LOCAL_FILE_NOTFOUND;
}
local_file_size_lowDWORD = GetFileSize(localfilehandle, &local_file_size_hiDWORD);
filesize = (local_file_size_hiDWORD * 0x100000000) + local_file_size_lowDWORD;
if (filesize < 0) {
transferstatus = ES_FAILED;
Err = E_LOCAL_FILE_NOTFOUND;
CloseLocalFile();
return E_LOCAL_FILE_NOTFOUND;
}
block = (char*)malloc(blocksize + 1);
if (block == NULL) {
Err = E_MEM_ALLOC;
transferstatus = ES_FAILED;
errstring = L"Could not allocate memory for file block size";
CloseLocalFile();
return E_MEM_ALLOC;
}
tempfilename = string(rfn) + ".sftp_temp";
result = rwopen_existing_SFTPfile((char *)tempfilename.c_str());
if (result == E_OK) {
getSFTPfileinfo();
sftp_seek64(file, rfilesize);
__int64 tempi64 = rfilesize & 0x00000000FFFFFFFF;
DWORD dwlow = tempi64;
tempi64 = (rfilesize & 0x7FFFFFFF00000000);
tempi64 = tempi64 >> 32;
long dwhi = tempi64;
DWORD dwResult = SetFilePointer(localfilehandle, dwlow, &dwhi, FILE_BEGIN);
if (dwResult == INVALID_SET_FILE_POINTER)
{
transferstatus = ES_FAILED; Err = result; return result;
}
transferstatus = ES_RESUMING;
transferred = rfilesize;
}
else{
result = createSFTPfile((char *)tempfilename.c_str());
transferstatus = ES_STARTING;
if (result != E_OK) {
transferstatus = ES_FAILED;
Err = result;
CloseLocalFile();
return result;
}
}
sftp_file_set_blocking(file);
transferstarttime = time(NULL);
transferstatus = ES_INPROGRESS;
while (transferstatus != ES_FAILED &&
transferstatus != ES_PAUSED &&
transferstatus != ES_DONE)
{
loopcounter++;
bresult = ReadFile(localfilehandle, (LPVOID)block, blocksize, &bytesread, NULL);
fprintf(logfile, "Read %d bytes from input file. Number of packets: %d, %llu from %llu bytes\n", bytesread, loopcounter, transferred, filesize);
if (bytesread < blocksize)
{
if (bresult == FALSE)
{
errstring = L"Error reading from local file.";
Err = E_LOCAL_FILE_READ;
transferstatus = ES_FAILED;
CloseRemoteFile();
CloseLocalFile();
return E_LOCAL_FILE_READ;
}
else if (bytesread == 0)
{
errstring = L"Transfer done.";
Err = E_OK;
transferstatus = ES_DONE;
continue;
}
}
result = writeSFTPfile(block, bytesread);
if (result != E_OK && bytesread>0)
{
errstring = L"Error transmitting to remote SFTP server file.";
Err = result;
transferstatus = ES_FAILED;
CloseRemoteFile();
CloseLocalFile();
return result;
}
Err = E_OK;
//transferred = transferred + bytesread;
if (pause == true)
transferstatus = ES_PAUSED;
if (bresult == TRUE && bytesread == 0)
{
// At the end of the file
transferstatus = ES_DONE;
}
Sleep(BLOCKTRANSDELAY);
if (loopcounter % 331 == 0)
Sleep(77 * BLOCKTRANSDELAY);
if (loopcounter % 3331 == 0)
Sleep(777 * BLOCKTRANSDELAY);
}
CloseRemoteFile();
CloseLocalFile();
Sleep(1000);
if (transferstatus == ES_CANCELLED)
{
result = SFTPdelete((char *)tempfilename.c_str());
if (bresult != E_OK)
{
Err = E_DELETE_ERR;
errstring = L"Could not delete remote file.";
transferstatus = ES_FAILED;
return E_DELETE_ERR;
}
}
if (transferstatus == ES_DONE)
result = SFTPrename(rfn, (char *)tempfilename.c_str());
delete block;
return result;
}
ESSHERR CSFTPConnector::getSFTPfileinfo()
{
sftp_attributes fileinf = sftp_fstat(file);
if (fileinf == NULL) {
return E_GET_FILEINF;
}
rfilesize = fileinf->size;
sftp_attributes_free(fileinf);
return E_OK;
}
ESSHERR CSFTPConnector::closeSFTPfile()
{
int rc = sftp_close(file);
if (rc != SSH_OK)
{
fprintf(logfile, "Can't close the written file: %s\n",
ssh_get_error(session));
return E_FILE_CLOSE;
}
return E_OK;
}
ESSHERR CSFTPConnector::writeSFTPfile(char *block, size_t blocksize)
{
size_t nwritten = sftp_write(file, block, blocksize);
if (nwritten != blocksize)
{
fprintf(logfile, "Can't write data to file: %s\n",
ssh_get_error(session));
//sftp_close(file);
transferred = transferred + nwritten;
return E_WRITE_ERR;
}
transferred = transferred + nwritten;
return E_OK;
}
ESSHERR CSFTPConnector::readSFTPfile(char *block, size_t len, size_t *bytesread)
{
DWORD readbytes;
*bytesread = 0;
if (len <= 0)
return E_INVALID_PARAMS;
if (bytesread == NULL || block == NULL)
return E_INVALID_PARAMS;
readbytes = sftp_read(file, block, len);
if (readbytes < 0)
{
fprintf(logfile, "Can't read from remote file: %s %s\n", filename.c_str(), ssh_get_error(session));
*bytesread = 0;
return E_SFTP_READ_ERR;
}
if (readbytes < len)
{
*bytesread = readbytes;
transferred = transferred + readbytes;
return E_SFTP_READ_EOF;
}
*bytesread = readbytes;
transferred = transferred + readbytes;
return E_OK;
}
ESSHERR CSFTPConnector::readSFTPfile(char *block, __int64 len, DWORD *bytesread)
{
DWORD readbytes;
*bytesread = 0;
if (len <= 0)
return E_INVALID_PARAMS;
if (bytesread == NULL || block == NULL)
return E_INVALID_PARAMS;
readbytes = sftp_read(file, block, len);
if (readbytes < 0)
{
fprintf(logfile, "Can't read from remote file: %s %s\n", filename.c_str(), ssh_get_error(session));
*bytesread = 0;
return E_SFTP_READ_ERR;
}
if (readbytes < len)
{
*bytesread = readbytes;
return E_SFTP_READ_EOF;
}
*bytesread = readbytes;
transferred = transferred + readbytes;
return E_OK;
}
ESSHERR CSFTPConnector::createSFTPfile(char *fn)
{
int access_type = O_CREAT | O_RDWR;
int rc, nwritten;
filename = string(fn);
file = sftp_open(sftp, fn,
access_type, S_IWRITE);
if (file == NULL)
{
fprintf(logfile, "Can't open file for writing: %s\n",
ssh_get_error(session));
return E_FILEOPEN_WRITE;
}
return E_OK;
}
ESSHERR CSFTPConnector::rdopen_existing_SFTPfile(char *fn)
{
int access_type = O_RDONLY;
int rc, nwritten;
filename = string(fn);
file = sftp_open(sftp, fn,
access_type, S_IREAD);
if (file == NULL)
{
fprintf(logfile, "Can't open file for writing: %s\n",
ssh_get_error(session));
return E_FILEOPEN_RDONLY;
}
return E_OK;
}
ESSHERR CSFTPConnector::openSFTPfile(char *fn)
{
int access_type = O_RDONLY;
int rc, nwritten;
filename = string(fn);
file = sftp_open(sftp, fn,
access_type, S_IWRITE);
if (file == NULL)
{
fprintf(logfile, "Can't open file for writing: %s\n",
ssh_get_error(session));
return E_FILE_OPEN_READ;
}
return E_OK;
}
ESSHERR CSFTPConnector::Makedir(char *newdir)
{
int rc;
rc = sftp_mkdir(sftp, newdir, S_IFDIR);
if (rc != SSH_OK)
{
if (sftp_get_error(sftp) != SSH_FX_FILE_ALREADY_EXISTS)
{
fprintf(logfile, "Can't create directory: %s\n",
ssh_get_error(session));
return E_CREATE_DIR;
}
}
return E_OK;
}
SFTPConnector::CSFTPConnector()
{
//libssh2_init(0);
session = ssh_new();
if (session == NULL)
{
Err = E_SESSION_ALOC;
errstring = L"Could not allocate a session.";
}
wcscpy(hostname, L"localhost");
wcscpy(username, L"User");
wcscpy(password, L"Password");
wcscpy(basedir, L".\\");
port = 22;
verbosity = SSH_LOG_RARE;
filesize = 0;
transferred = 0;
pause = false;
transferstatus = ES_NONE;
logfile = stderr;
}
CSFTPConnector::CSFTPConnector(wchar_t *dir, wchar_t *hn, int hostport, wchar_t *un, wchar_t *pass)
{
session = ssh_new();
if (session == NULL)
{
Err = E_SESSION_ALOC;
errstring = L"Could not allocate a session.";
}
wcscpy(hostname, hn);
wcscpy(username, un);
wcscpy(password, pass);
wcscpy(basedir, dir);
port = hostport;
verbosity = SSH_LOG_RARE;
filesize = 0;
transferred = 0;
pause = false;
transferstatus = ES_NONE;
logfile = stderr;
}
ESSHERR CSFTPConnector::InitSFTP()
{
int rc;
sftp = sftp_new(session);
if (session == NULL)
{
Err = E_SFTP_ALLOC;
errstring = L"Could not allocate a sftp session.";
}
rc = sftp_init(sftp);
if (rc != SSH_OK)
{
fprintf(logfile, "Error initializing SFTP session: %s.\n",
sftp_get_error(sftp));
sftp_free(sftp);
return E_INIT_SFTP;
}
return E_OK;
}
ESSHERR CSFTPConnector::ConnectSession()
{
char temp[SHORT_BUFF_LEN];
size_t n_of_chars;
wcstombs_s(&n_of_chars, temp, SHORT_BUFF_LEN, (const wchar_t *)password, SHORT_BUFF_LEN);
int ir;
ir = ssh_connect(session);
if (ir != SSH_OK) {
errstring = L"Could not connect the ssh session.";
return E_SSH_CONNECT_ERR;
}
ir = ssh_userauth_password(session, NULL, temp);
if (ir != SSH_OK) {
errstring = L"Could not connect the ssh session.";
return E_SSH_CONNECT_ERR;
}
return E_OK;
}
ESSHERR CSFTPConnector::InitSession()
{
char temp[SHORT_BUFF_LEN];
size_t n_of_chars;
wcstombs_s(&n_of_chars, temp, SHORT_BUFF_LEN, (const wchar_t *) hostname, SHORT_BUFF_LEN);
ssh_options_set(session, SSH_OPTIONS_HOST, temp);
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_options_set(session, SSH_OPTIONS_PORT, &port);
wcstombs_s(&n_of_chars, temp, SHORT_BUFF_LEN, (const wchar_t *)username, SHORT_BUFF_LEN);
ssh_options_set(session, SSH_OPTIONS_USER, temp);
wcstombs_s(&n_of_chars, temp, SHORT_BUFF_LEN, (const wchar_t *)basedir, SHORT_BUFF_LEN);
ssh_options_set(session, SSH_OPTIONS_SSH_DIR, temp);
return E_OK;
}
CSFTPConnector::~CSFTPConnector()
{
sftp_free(sftp);
ssh_disconnect(session);
ssh_free(session);
return;
}
In the past, I've simply called a shell script that contained the file transfer code.
int transferFile()
{
// Declare the transfer command
char transferCommand[50] = "/home/tyler/transferFile.shl";
// Execute the command
return system(transferCommand);
}
This will return 1 if the transfer command returns successfully.