I wrote a C server that should send to a client the file it requested through the XDR library, using the buffer paradigm. The program uses a custom struct.
enum tagtype {
GET = 0,
OK = 1,
QUIT = 2,
ERR = 3
};
struct file {
opaque contents<>;
unsigned int last_mod_time;
};
union message switch (tagtype tag) {
case GET:
string filename<256>;
case OK:
struct file fdata;
case QUIT:
void;
case ERR:
void;
};
This is the core part of the XDR server.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <rpc/xdr.h>
#include "xdr_file.h"
//main() and call to manage_clixrequest()
int manage_clixrequest(int clisocket_fd) {
FILE *req_file;
struct stat metadata;
char rcv_message[CHUNK_SIZE] = "";
char *buffer, buffer2[2048];
size_t nbytes = 0;
int sent, n;
XDR xdrs_r, xdrs_w; // Pointer to XDR streams
message msg, send_msg;
/* Create codification stream */
xdrmem_create(&xdrs_r, rcv_message, sizeof(rcv_message), XDR_DECODE);
memset(&msg, 0, sizeof(msg));
memset(&send_msg, 0, sizeof(send_msg));
msg.tag = ERR;
msg.message_u.filename = NULL;
msg.message_u.fdata.contents.contents_len = 0;
msg.message_u.fdata.contents.contents_val = NULL;
msg.message_u.fdata.last_mod_time = 0;
/* The server waits for data */
n = recv(clisocket_fd, rcv_message, sizeof(rcv_message), 0);
/* Check if the server received some data */
if (n < 0) {
printf("Errno %d in recv(): %s\n", errno, strerror(errno));
}
if(!xdr_message(&xdrs_r, &msg)){
printf("Error in xdr_message()\n");
return -1;
}
xdr_destroy(&xdrs_r);
/* For a GET message the function extracts the filename
* and send the corresponding file to the client */
if (msg.tag == GET) {
req_file = fopen(msg.message_u.filename, "rb");
/* Check if the file is valid */
if (req_file == NULL) {
if (errno == 2){
xdrmem_create(&xdrs_w, buffer, sizeof(buffer), XDR_ENCODE);
/* The requested file does not exist, so an error message is sent
* and the connection is going to be shutdown */
send_msg.tag = ERR;
if(!xdr_message(&xdrs_w, &send_msg)){
printf("Error in xdr_message()\n");
return -1;
}
sent = send(clisocket_fd, buffer, sizeof(buffer), 0);
/* Check if the server sent the data */
if (sent < 0) {
printf("Errno %d in send(): %s\n", errno, strerror(errno));
}
xdr_destroy(&xdrs_r);
xdr_destroy(&xdrs_w);
return 0;
} else {
printf("Errno %d in fopen(): %s\n", errno, strerror(errno));
return -1;
}
}
/* Check if the file metadata are correctly retrieved */
if (fstat(fileno(req_file), &metadata) < 0) {
printf("Errno %d in fstat(): %s\n", errno, strerror(errno));
return -1;
}
/* File is read and memorized in a temporary buffer */
buffer = (char *) malloc(sizeof(char)*metadata.st_size);
memset(buffer, 0, metadata.st_size);
nbytes = fread(buffer, sizeof(char), metadata.st_size, req_file);
memset(buffer2, 0, sizeof(buffer2));
/* Create codification stream */
xdrmem_create(&xdrs_w, buffer2, sizeof(buffer2), XDR_ENCODE);
/* This is the beginning of the message for the client
* when the file is going to be sent */
send_msg.tag = OK;
/* The file size and last modification time are retrieved */
send_msg.message_u.fdata.contents.contents_len= metadata.st_size;
send_msg.message_u.fdata.last_mod_time = metadata.st_mtime;
send_msg.message_u.filename = msg.message_u.filename;
send_msg.message_u.fdata.contents.contents_val = buffer;
if(!xdr_message(&xdrs_w, &send_msg)){
printf("Error in xdr_message()\n");
return -1;
}
/* The XDR struct is sent with the send() function */
sent = send(clisocket_fd, buffer2, nbytes, 0);
/* Check if the server sent the data */
if (sent < 0) {
printf("Errno %d in send(): %s\n", errno, strerror(errno));
return -1;
}
free(buffer);
xdr_destroy(&xdrs_w);
} else if (msg.tag == QUIT) {
/* The client asked to shutdown the connection.
* Unused if the client itself closes the connection */
return 0;
} else {
/* The client sent a request with a not expected tag, so an error message is sent
* and the connection is going to be shutdown*/
xdrmem_create(&xdrs_w, buffer, sizeof(buffer), XDR_ENCODE);
send_msg.tag = ERR;
if(!xdr_message(&xdrs_w, &send_msg)){
printf("Errno %d in xdr_message(): %s\n", errno, strerror(errno));
return -1;
}
sent = send(clisocket_fd, buffer, sizeof(buffer), 0);
/* Check if the server sent the data */
if (sent < 0) {
printf("Errno %d in send(): %s\n", errno, strerror(errno));
}
xdr_destroy(&xdrs_w);
return 0;
}
return 1;
}
Unfortunately the encodification of the message causes a segmentation fault.
Program received signal SIGSEGV, Segmentation fault.
xdrmem_putlong (xdrs=0x7fffffffd050, lp=0x7fffffffcf58) at xdr_mem.c:124
124 xdr_mem.c: No such file or directory.
I checked the program with GDB and I obtained this backtrace
(gdb) backtrace
#0 xdrmem_putlong (xdrs=0x7fffffffd050, lp=0x7fffffffcf58)
at xdr_mem.c:124
#1 0x00007ffff7b4d99c in __GI_xdr_enum (xdrs=<optimized out>,
ep=0x7fffffffd000) at xdr.c:500
#2 0x000055555555617e in xdr_tagtype (xdrs=0x7fffffffd050,
objp=0x7fffffffd000) at xdr_file.c:13
#3 0x0000555555556214 in xdr_message (xdrs=0x7fffffffd050,
objp=0x7fffffffd000) at xdr_file.c:35
#4 0x0000555555555847 in manage_clixrequest (clisocket_fd=4)
at server3-4.c:273
#5 0x00005555555552a6 in main (argc=3, argv=0x7fffffffdd78)
at server3-4.c:129
where manage_clixrequest(socket_fd) is the function I wrote. I don't know if I am using badly the XDR library, or if I am forgetting to include something so that the xdr_mem.c is not linked correctly. Do you have any ideas/tips?
EDIT I reported the whole server function and the xdr_file.c.
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include "xdr_file.h"
bool_t
xdr_tagtype (XDR *xdrs, tagtype *objp)
{
register int32_t *buf;
if (!xdr_enum (xdrs, (enum_t *) objp))
return FALSE;
return TRUE;
}
bool_t
xdr_file (XDR *xdrs, file *objp)
{
register int32_t *buf;
if (!xdr_bytes (xdrs, (char **)&objp->contents.contents_val, (u_int *) &objp->contents.contents_len, ~0))
return FALSE;
if (!xdr_u_int (xdrs, &objp->last_mod_time))
return FALSE;
return TRUE;
}
bool_t
xdr_message (XDR *xdrs, message *objp)
{
register int32_t *buf;
if (!xdr_tagtype (xdrs, &objp->tag))
return FALSE;
switch (objp->tag) {
case GET:
if (!xdr_string (xdrs, &objp->message_u.filename, 256))
return FALSE;
break;
case OK:
if (!xdr_file (xdrs, &objp->message_u.fdata))
return FALSE;
break;
case QUIT:
break;
case ERR:
break;
default:
return FALSE;
}
return TRUE;
}
I found out that I used the same buffer to memorize the file bytes and to create the XDR stream, that is an error. Now there is no segmentation fault and the XDR struct message
is correctly filled, but now the call to the generated function xdr_bytes()
returns false and the encodification still fails.