Segmentation fault on Socket programming - c

I have a homework problem. Socket-Programming with Unix domain
1. Client send file name to server
2. Server checks the File exists, opens, and sends file descriptor to client
3. Client opens a file descriptor and outputs to screen.
platform = ubuntu 12.04.
I have problem with the client. Error Segmentation fault (core dumped) on line fd = *p(i have marked on client code)
this is my code
Server
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdbool.h>
#include <signal.h>
#include <sys/wait.h>
#define SOCKNAME "sockunix"
bool ende = false;
void sigfkt(int signr){
printf("SIGINT empfangen ...\n");
ende = true;
}
int main(){
int fd, sockd, sockd2, rc, *p, sl;
FILE *fp;
struct sockaddr_un uxadr;
struct msghdr mh;
struct cmsghdr *cmp;
char ctrl[sizeof(struct cmsghdr)+10];
struct iovec iov[1];
char eab[100];
//bat dau khai bao
mh.msg_name = NULL;
mh.msg_namelen = 0;
mh.msg_iov = iov;
mh.msg_iovlen = 1;
mh.msg_control = ctrl;
mh.msg_controllen = sizeof(ctrl);
mh.msg_flags = 0;
cmp = CMSG_FIRSTHDR(&mh);
cmp->cmsg_len = CMSG_LEN(fd);
cmp->cmsg_level = SOL_SOCKET;
cmp->cmsg_type = SCM_RIGHTS;
p = (int *)CMSG_DATA(cmp);
// khoi tao socket
uxadr.sun_family = AF_UNIX;
strcpy(uxadr.sun_path, SOCKNAME);
sockd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sockd<0){
perror("socket");
exit(1);
}
unlink(uxadr.sun_path);
sl = sizeof(uxadr);
rc = bind(sockd, (struct sockaddr*)&uxadr, sl);
if(rc<0){
perror("bind");
exit(2);
}
rc = listen(sockd, 10);
if(rc<0){
perror("listen");
exit(3);
}
sigset(SIGINT, sigfkt);
printf("Warte auf Client-Anforderungen ....\n"); //cho client ket noi toi
do{
sockd2 = accept(sockd, 0, 0);
if(ende)
break;
if(sockd2<0){
perror("accept");
exit(4);
}
iov[0].iov_base = eab;
iov[0].iov_len = sizeof(eab);
rc = recvmsg(sockd2,&mh,0); //nhan File name tu client
if(rc<0){
perror("recvmsg");
exit(5);
}
printf("%s\n",eab);
if(rc>0){
fp = fopen(eab,"r");// kiem tra xem file ton tai ko?
if(fp==NULL){
printf("file not existiert\n");
eab[0]=1; // thong bao cho client file ko ton tai
}
else{
fd = fileno(fp); // lay File descriptor
eab[0]=2; // thong bao cho client file ton tai
*p = fd; //gan File descriptor vao cau truc dieu khien
}
}
printf("fd=%d\n",*p);
rc = sendmsg(sockd2,&mh,0); // guoi thong bao va File descriptor den client
if(rc<0){
perror("sendmsg");
}
close(sockd2);
}while(!ende);
close(sockd);
unlink(uxadr.sun_path);
}
Client
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdbool.h>
#include <signal.h>
#include <sys/wait.h>
#define SOCKNAME "sockunix"
int main(int argc, char *argv[]){
int fd = 0, sockd, rc, *p, sl;
FILE *fp;
struct sockaddr_un uxadr;
struct msghdr mh;
struct cmsghdr *cmp;
char ctrl[sizeof(struct cmsghdr)+10];
struct iovec iov[1];
char eab[100],fname[100];
//bat dau khai bao
mh.msg_name = NULL;
mh.msg_namelen = 0;
mh.msg_iov = iov;
mh.msg_iovlen = 1;
mh.msg_control = ctrl;
mh.msg_controllen = sizeof(ctrl);
mh.msg_flags = 0;
cmp = CMSG_FIRSTHDR(&mh);
cmp->cmsg_len = CMSG_LEN(fd);
cmp->cmsg_level = SOL_SOCKET;
cmp->cmsg_type = SCM_RIGHTS;
p = (int *)CMSG_DATA(cmp);
//khoi tao socket
uxadr.sun_family = AF_UNIX;
strcpy(uxadr.sun_path, SOCKNAME);
sockd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sockd<0){
perror("socket");
exit(1);
}
sl = sizeof(uxadr);
rc = connect(sockd, (struct sockaddr *)&uxadr, sl);
if(rc<0){
perror("connect");
exit(2);
}
if(argc>=2){ // dua File name vao tu dong lenh
sprintf(fname,"%s",argv[1]);
}
else{// neu chua co File name thi bat dau nhap file name vao
printf("Bitte Filename eingeben\n");
fflush(stdin);
gets(fname);
}
iov[0].iov_base = fname;
iov[0].iov_len = strlen(fname)+1;
rc = sendmsg(sockd, &mh, 0); // guoi filename sang server
if(rc<0){
perror("sendmsg");
exit(3);
}
iov[0].iov_base = eab;
iov[0].iov_len = sizeof(eab);
rc = recvmsg(sockd, &mh, 0); // nhan thong bao va File descriptor tu server
cmp = CMSG_FIRSTHDR(&mh);
p = (int *)CMSG_DATA(cmp);
if(rc<0){
perror("recvmsg");
exit(4);
}
switch(eab[0]){// kiem tra thong bao
case 1:// file khong ton tai
printf("File ist nicht existiert!\n");
break;
case 2:// file ton tai
printf("File ist existiert. Filedeskriptor ist bereits zu verwandel!\n");
FAULT ================> fd = *p; //fault here with GDB debug
printf("fd=%d\n",fd);
fp = fdopen(fd,"r");
if(fp=NULL)
printf("fehler fd\n");
printf("Fileinhalt ausgeben\n");
printf("=============================================\n");
while(fread(eab,100,1,fp)>0)
printf("%s", eab);
printf("=============================================\n");
fclose(fp);
break;
}
close(sockd);
}

In theory, you can't share file descriptors between processes over TCP sockets with processes with different address spaces. In order to do that, you could use UNIX sockets.
If you want to share info between processes (not file descriptors), you can use other techniques, like mmap, pipes, message passing, and so on.

You need to check the error code returned by recvmesg before you do anything with the data it may or may not have returned.
There is no guarantee that when recvmesg returns an error that mh will be in a useable state.

A segmentation fault on *p means that p is either zero or pointing to memory that is not allocated.
It looks like p is supposed to be pointing inside mh somewhere. First check that the value of p just before the crash is between &mh and (char*)&mh + sizeof(mh).

Related

Pass argc variable to thread

I'm creating a TCP socket-based server-client application for a roulette game. Before compiling the server, the admin needs to input two arguments for how the server should work: one for the port, and the other for the roulette's required time to spin. However, when I try to compile the code it gives me a segmentation fault error. The code works fine if all references to roulette_time are taken out, though.
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <semaphore.h>
#define BACKLOG 15
long bank = 0;
typedef struct pthread_arg_t{
int new_socket_fd;
struct sockaddr_in client_address;
int port;
int *roulette_time;
/*Inserire le variabili passate dal client qui*/
int picked_num;
long stake;
int won;
}pthread_arg_t;
void *pthread_routine(void *arg);
void signal_handler(int signal_number);
int main(int argc, char *argv[]){
int port, socket_fd, new_socket_fd, chosen_number, won, roulette_time;
long stake;
struct sockaddr_in address;
pthread_attr_t pthread_attr;
pthread_arg_t *pthread_arg;
pthread_t pthread;
socklen_t client_address_len;
port = atoi(argv[1]);
roulette_time = atoi(argv[2]);
if(port == NULL){
printf("Inserisci il port qui\n");
scanf("%d",&port);
}
if(roulette_time == NULL){
printf("Inserisci il tempo minimo da far trascorrere prima dell'azionamento della roulette\n");
scanf("%d",&roulette_time);
}
pthread_arg->roulette_time = &roulette_time;
memset(&address,0,sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = INADDR_ANY;
if((socket_fd = socket(AF_INET,SOCK_STREAM,0)) == -1){
perror("creazione socket");
exit(1);
}
if(bind(socket_fd,(struct sockaddr*)&address, sizeof(address)) == -1){
perror("binding");
exit(1);
}
if(listen(socket_fd, BACKLOG) == -1){
perror("listen");
exit(1);
}
if((signal(SIGPIPE, SIG_IGN) == SIG_ERR) || (signal(SIGTERM, signal_handler) == SIG_ERR) || (signal(SIGINT, signal_handler) == SIG_ERR)){
perror("signal");
exit(1);
}
if(pthread_attr_init(&pthread_attr) != 0){
perror("pthread_attr_init");
exit(1);
}
if(pthread_attr_setdetachstate(&pthread_attr, PTHREAD_CREATE_DETACHED) != 0){
perror("pthread_attr_setdetachstate");
exit(1);
}
while(1){
pthread_arg = (pthread_arg_t *)malloc(sizeof *pthread_arg);
if(!pthread_arg){
perror("malloc");
continue;
}
client_address_len = sizeof(pthread_arg->client_address);
new_socket_fd = accept(socket_fd, (struct sockaddr *)&pthread_arg->client_address,&client_address_len);
if(new_socket_fd == -1){
perror("accept");
free(pthread_arg);
continue;
}
pthread_arg->new_socket_fd = new_socket_fd;
/*Inizializza tutte le variabili qui*/
chosen_number = pthread_arg->picked_num;
stake = pthread_arg->stake;
won = pthread_arg->won;
if(pthread_create(&pthread, &pthread_attr, pthread_routine, (void*)pthread_arg) != 0){
perror("pthread_create");
free(pthread_arg);
continue;
}
}
return 0;
}
void *pthread_routine(void *arg){
pthread_arg_t *pthread_arg = (pthread_arg_t *)arg;
int new_socket_fd = pthread_arg->new_socket_fd;
struct sockaddr_in client_address = pthread_arg->client_address;
int roulette_num, picked_num, won, accept_time = pthread_arg->roulette_time;
long stake;
time_t start, end;
sem_t mutex;
free(arg);
/*Inserisci codice roulette qui*/
while(1){
if(start == NULL){
start = time(NULL);
end = start + accept_time;
}
read(new_socket_fd, &picked_num, sizeof(picked_num));
read(new_socket_fd, &stake, sizeof(stake));
printf("Puntata ricevuta: numero %d, %d crediti\n",ntohl(picked_num), ntohl(stake));
bank = bank + stake;
printf("Soldi del banco: %d crediti\n",ntohl(bank));
start = time(NULL);
if(start >= end){
roulette_num = (rand() % (36 - 0 + 1)) + 0;
printf("Numero estrtatto dalla roulette: %d\n",roulette_num);
int converted_roulette_num = htonl(roulette_num);
write(new_socket_fd,&converted_roulette_num,sizeof(int));
if(read(new_socket_fd,&won,sizeof(int)) == 1){
int converted_bank = htonl(bank);
write(new_socket_fd,&converted_bank,sizeof(converted_bank));
bank = 0;
}
start = NULL;
}
}
close(new_socket_fd);
return NULL;
}
void signal_handler(int signal_number){
}
Edit: The input that this application accepts is ./server [socket port number] [seconds to activate roulette], for example: ./server 24106 30 (in this case 24106 is the socket port number, and we're asking the roulette to spin after 30 seconds have passed).
The output should appear in the following way: after the server receives an input from a connected client, the server prints the received number and the stake. After the amount of time passed as argument passed, the roulette spins and the server prints out the extracted number. However, as mentioned earlier, this only happens if I remove every instance of roulette_time from the code - otherwise I get Segmentation fault (core dumped) every time I try to run it.

Sending file descriptor by Linux socket

I am trying to send some file descriptor by linux socket, but it does not work. What am I doing wrong? How is one supposed to debug something like this? I tried putting perror() everywhere it's possible, but they claimed that everything is ok. Here is what I've written:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
void wyslij(int socket, int fd) // send fd by socket
{
struct msghdr msg = {0};
char buf[CMSG_SPACE(sizeof fd)];
msg.msg_control = buf;
msg.msg_controllen = sizeof buf;
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof fd);
*((int *) CMSG_DATA(cmsg)) = fd;
msg.msg_controllen = cmsg->cmsg_len; // why does example from man need it? isn't it redundant?
sendmsg(socket, &msg, 0);
}
int odbierz(int socket) // receive fd from socket
{
struct msghdr msg = {0};
recvmsg(socket, &msg, 0);
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
unsigned char * data = CMSG_DATA(cmsg);
int fd = *((int*) data); // here program stops, probably with segfault
return fd;
}
int main()
{
int sv[2];
socketpair(AF_UNIX, SOCK_DGRAM, 0, sv);
int pid = fork();
if (pid > 0) // in parent
{
close(sv[1]);
int sock = sv[0];
int fd = open("./z7.c", O_RDONLY);
wyslij(sock, fd);
close(fd);
}
else // in child
{
close(sv[0]);
int sock = sv[1];
sleep(0.5);
int fd = odbierz(sock);
}
}
Stevens (et al) UNIX® Network Programming, Vol 1: The Sockets Networking API describes the process of transferring file descriptors between processes in Chapter 15 Unix Domain Protocols and specifically §15.7 Passing Descriptors. It's fiddly to describe in full, but it must be done on a Unix domain socket (AF_UNIX or AF_LOCAL), and the sender process uses sendmsg() while the receiver uses recvmsg().
I got this mildly modified (and instrumented) version of the code from the question to work for me on Mac OS X 10.10.1 Yosemite with GCC 4.9.1:
#include "stderr.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
static
void wyslij(int socket, int fd) // send fd by socket
{
struct msghdr msg = { 0 };
char buf[CMSG_SPACE(sizeof(fd))];
memset(buf, '\0', sizeof(buf));
struct iovec io = { .iov_base = "ABC", .iov_len = 3 };
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
*((int *) CMSG_DATA(cmsg)) = fd;
msg.msg_controllen = CMSG_SPACE(sizeof(fd));
if (sendmsg(socket, &msg, 0) < 0)
err_syserr("Failed to send message\n");
}
static
int odbierz(int socket) // receive fd from socket
{
struct msghdr msg = {0};
char m_buffer[256];
struct iovec io = { .iov_base = m_buffer, .iov_len = sizeof(m_buffer) };
msg.msg_iov = &io;
msg.msg_iovlen = 1;
char c_buffer[256];
msg.msg_control = c_buffer;
msg.msg_controllen = sizeof(c_buffer);
if (recvmsg(socket, &msg, 0) < 0)
err_syserr("Failed to receive message\n");
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
unsigned char * data = CMSG_DATA(cmsg);
err_remark("About to extract fd\n");
int fd = *((int*) data);
err_remark("Extracted fd %d\n", fd);
return fd;
}
int main(int argc, char **argv)
{
const char *filename = "./z7.c";
err_setarg0(argv[0]);
err_setlogopts(ERR_PID);
if (argc > 1)
filename = argv[1];
int sv[2];
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) != 0)
err_syserr("Failed to create Unix-domain socket pair\n");
int pid = fork();
if (pid > 0) // in parent
{
err_remark("Parent at work\n");
close(sv[1]);
int sock = sv[0];
int fd = open(filename, O_RDONLY);
if (fd < 0)
err_syserr("Failed to open file %s for reading\n", filename);
wyslij(sock, fd);
close(fd);
nanosleep(&(struct timespec){ .tv_sec = 1, .tv_nsec = 500000000}, 0);
err_remark("Parent exits\n");
}
else // in child
{
err_remark("Child at play\n");
close(sv[0]);
int sock = sv[1];
nanosleep(&(struct timespec){ .tv_sec = 0, .tv_nsec = 500000000}, 0);
int fd = odbierz(sock);
printf("Read %d!\n", fd);
char buffer[256];
ssize_t nbytes;
while ((nbytes = read(fd, buffer, sizeof(buffer))) > 0)
write(1, buffer, nbytes);
printf("Done!\n");
close(fd);
}
return 0;
}
The output from the instrumented but unfixed version of the original code was:
$ ./fd-passing
fd-passing: pid=1391: Parent at work
fd-passing: pid=1391: Failed to send message
error (40) Message too long
fd-passing: pid=1392: Child at play
$ fd-passing: pid=1392: Failed to receive message
error (40) Message too long
Note that the parent finished before the child, so the prompt appeared in the middle of the output.
The output from the 'fixed' code was:
$ ./fd-passing
fd-passing: pid=1046: Parent at work
fd-passing: pid=1048: Child at play
fd-passing: pid=1048: About to extract fd
fd-passing: pid=1048: Extracted fd 3
Read 3!
This is the file z7.c.
It isn't very interesting.
It isn't even C code.
But it is used by the fd-passing program to demonstrate that file
descriptors can indeed be passed between sockets on occasion.
Done!
fd-passing: pid=1046: Parent exits
$
The primary significant changes were adding the struct iovec to the data in the struct msghdr in both functions, and providing space in the receive function (odbierz()) for the control message. I reported an intermediate step in debugging where I provided the struct iovec to the parent and the parent's "message too long" error was removed. To prove it was working (a file descriptor was passed), I added code to read and print the file from the passed file descriptor. The original code had sleep(0.5) but since sleep() takes an unsigned integer, this was equivalent to not sleeping. I used C99 compound literals to have the child sleep for 0.5 seconds. The parent sleeps for 1.5 seconds so that the output from the child is complete before the parent exits. I could use wait() or waitpid() too, but was too lazy to do so.
I have not gone back and checked that all the additions were necessary.
The "stderr.h" header declares the err_*() functions. It's code I wrote (first version before 1987) to report errors succinctly. The err_setlogopts(ERR_PID) call prefixes all messages with the PID. For timestamps too, err_setlogopts(ERR_PID|ERR_STAMP) would do the job.
Alignment issues
Nominal Animal suggests in a comment:
May I suggest you modify the code to copy the descriptor int using memcpy() instead of accessing the data directly? It is not necessarily correctly aligned — which is why the man page example also uses memcpy() — and there are many Linux architectures where unaligned int access causes problems (up to SIGBUS signal killing the process).
And not only Linux architectures: both SPARC and Power require aligned data and often run Solaris and AIX respectively. Once upon a time, DEC Alpha required that too, but they're seldom seen in the field these days.
The code in the manual page cmsg(3) related to this is:
struct msghdr msg = {0};
struct cmsghdr *cmsg;
int myfds[NUM_FD]; /* Contains the file descriptors to pass. */
char buf[CMSG_SPACE(sizeof myfds)]; /* ancillary data buffer */
int *fdptr;
msg.msg_control = buf;
msg.msg_controllen = sizeof buf;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * NUM_FD);
/* Initialize the payload: */
fdptr = (int *) CMSG_DATA(cmsg);
memcpy(fdptr, myfds, NUM_FD * sizeof(int));
/* Sum of the length of all control messages in the buffer: */
msg.msg_controllen = CMSG_SPACE(sizeof(int) * NUM_FD);
The assignment to fdptr appears to assume that CMSG_DATA(cmsg) is sufficiently well aligned to be converted to an int * and the memcpy() is used on the assumption that NUM_FD is not just 1. With that said, it is supposed to be pointing at the array buf, and that might not be sufficiently well aligned as Nominal Animal suggests, so it seems to me that the fdptr is just an interloper and it would be better if the example used:
memcpy(CMSG_DATA(cmsg), myfds, NUM_FD * sizeof(int));
And the reverse process on the receiving end would then be appropriate. This program only passes a single file descriptor, so the code is modifiable to:
memmove(CMSG_DATA(cmsg), &fd, sizeof(fd)); // Send
memmove(&fd, CMSG_DATA(cmsg), sizeof(fd)); // Receive
I also seem to recall historical issues on various OSes w.r.t. ancillary data with no normal payload data, avoided by sending at least one dummy byte too, but I cannot find any references to verify, so I might remember wrong.
Given that Mac OS X (which has a Darwin/BSD basis) requires at least one struct iovec, even if that describes a zero-length message, I'm willing to believe that the code shown above, which includes a 3-byte message, is a good step in the right general direction. The message should perhaps be a single null byte instead of 3 letters.
I've revised the code to read as shown below. It uses memmove() to copy the file descriptor to and from the cmsg buffer. It transfers a single message byte, which is a null byte.
It also has the parent process read (up to) 32 bytes of the file before passing the file descriptor to the child. The child continues reading where the parent left off. This demonstrates that the file descriptor transferred includes the file offset.
The receiver should do more validation on the cmsg before treating it as a file descriptor passing message.
#include "stderr.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
static
void wyslij(int socket, int fd) // send fd by socket
{
struct msghdr msg = { 0 };
char buf[CMSG_SPACE(sizeof(fd))];
memset(buf, '\0', sizeof(buf));
/* On Mac OS X, the struct iovec is needed, even if it points to minimal data */
struct iovec io = { .iov_base = "", .iov_len = 1 };
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
memmove(CMSG_DATA(cmsg), &fd, sizeof(fd));
msg.msg_controllen = CMSG_SPACE(sizeof(fd));
if (sendmsg(socket, &msg, 0) < 0)
err_syserr("Failed to send message\n");
}
static
int odbierz(int socket) // receive fd from socket
{
struct msghdr msg = {0};
/* On Mac OS X, the struct iovec is needed, even if it points to minimal data */
char m_buffer[1];
struct iovec io = { .iov_base = m_buffer, .iov_len = sizeof(m_buffer) };
msg.msg_iov = &io;
msg.msg_iovlen = 1;
char c_buffer[256];
msg.msg_control = c_buffer;
msg.msg_controllen = sizeof(c_buffer);
if (recvmsg(socket, &msg, 0) < 0)
err_syserr("Failed to receive message\n");
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
err_remark("About to extract fd\n");
int fd;
memmove(&fd, CMSG_DATA(cmsg), sizeof(fd));
err_remark("Extracted fd %d\n", fd);
return fd;
}
int main(int argc, char **argv)
{
const char *filename = "./z7.c";
err_setarg0(argv[0]);
err_setlogopts(ERR_PID);
if (argc > 1)
filename = argv[1];
int sv[2];
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) != 0)
err_syserr("Failed to create Unix-domain socket pair\n");
int pid = fork();
if (pid > 0) // in parent
{
err_remark("Parent at work\n");
close(sv[1]);
int sock = sv[0];
int fd = open(filename, O_RDONLY);
if (fd < 0)
err_syserr("Failed to open file %s for reading\n", filename);
/* Read some data to demonstrate that file offset is passed */
char buffer[32];
int nbytes = read(fd, buffer, sizeof(buffer));
if (nbytes > 0)
err_remark("Parent read: [[%.*s]]\n", nbytes, buffer);
wyslij(sock, fd);
close(fd);
nanosleep(&(struct timespec){ .tv_sec = 1, .tv_nsec = 500000000}, 0);
err_remark("Parent exits\n");
}
else // in child
{
err_remark("Child at play\n");
close(sv[0]);
int sock = sv[1];
nanosleep(&(struct timespec){ .tv_sec = 0, .tv_nsec = 500000000}, 0);
int fd = odbierz(sock);
printf("Read %d!\n", fd);
char buffer[256];
ssize_t nbytes;
while ((nbytes = read(fd, buffer, sizeof(buffer))) > 0)
write(1, buffer, nbytes);
printf("Done!\n");
close(fd);
}
return 0;
}
And a sample run:
$ ./fd-passing
fd-passing: pid=8000: Parent at work
fd-passing: pid=8000: Parent read: [[This is the file z7.c.
It isn't ]]
fd-passing: pid=8001: Child at play
fd-passing: pid=8001: About to extract fd
fd-passing: pid=8001: Extracted fd 3
Read 3!
very interesting.
It isn't even C code.
But it is used by the fd-passing program to demonstrate that file
descriptors can indeed be passed between sockets on occasion.
And, with the fully working code, it does indeed seem to work.
Extended testing would have the parent code read part of the file, and
then demonstrate that the child codecontinues where the parent left off.
That has not been coded, though.
Done!
fd-passing: pid=8000: Parent exits
$

File sent over TCP is created with type: application/octet-stream

I'm trying to transfer a file from a server to a client using TCP protocol.
I manage to send the whole syze of the file, but when the client creates the file, it cant be open. In this case, im sending an jpg file.
heres the code for server.c:
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <arpa/inet.h>
#define PORT 59000
int main(int argc,char *argv[]) {
int port, fd, newfd, n, nw, addrlen;
int port_was_given = 0;
char buffer[128], *ptr, *topic, *data;
size_t result;
struct hostent *h;
struct sockaddr_in addr;
FILE *send;
if((fd=socket(AF_INET,SOCK_STREAM,0))==-1)exit(1); //error
memset((void*)&addr,(int)'\0',sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
if (argc == 3) {
port = atoi(argv[2]);
port_was_given = 1;
}
if(port_was_given == 1)
addr.sin_port=htons((u_short)port);
else
addr.sin_port=htons((u_short)PORT);
if(bind(fd,(struct sockaddr*)&addr,sizeof(addr))==-1)exit(1); //error
if(listen(fd,5)==-1)exit(1); //error
while(1) {
addrlen=sizeof(addr);
if((newfd=accept(fd,(struct sockaddr*)&addr,&addrlen))==-1)exit(1); //erro
h=gethostbyaddr((char*)&addr.sin_addr,sizeof(struct in_addr),AF_INET);
while((n=read(newfd,buffer,128))!=0) {
if(n==-1)exit(1);
topic = strtok(buffer," ");
topic = strtok(NULL," ");
if (strcmp(topic, "Nacional\n")==0) {
send = fopen("flag","r");
fseek(send, 0L, SEEK_END); //vai ate ao fim do ficheiro
int sz = ftell(send); //size of file
fseek(send,0L,SEEK_SET);
//rewind(send);
data = (char*)malloc(sizeof(char)*sz);
result = fread(data,1,sz,send);
//fseek(send,0L,SEEK_SET);
fclose(send);
char ptr2[300] = "REP ok ";
char *ptrInt; //for s -> int
sprintf(ptrInt, "%d", sz);
strcat(ptr2, ptrInt);
strcat(ptr2, " ");
strcat(ptr2, data);
strcat(ptr2, "\n");
while(n>0) {
nw=write(newfd,ptr2,n); //write n bytes on each cycle
}
}
}
close(newfd);
}
close(fd);
exit(0);
}
Ok so the logic is: client requests a type of content, in this case the content is "Nacional", so the server has to send the "flag.jpg" to the client.
The answer of the server has the following type:
REP status size data
In which status can be "ok" or "nok". If "nok" then the file is not sent.
size is the size of the data.
data is data of the file itself.
Now the client.c:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <arpa/inet.h>
#define PORT 58000
#define NG 10
int main (int argc,char *argv[])
{
/** ... variables declarations and other stuff ... */
fdtcp=socket(AF_INET,SOCK_STREAM,0);
if (fdtcp==-1) exit(1); // Erro
inet_aton(ip, &address);
if (strcmp(lsname, "localhost")==0)
newHost = gethostbyname("localhost");
else
newHost = gethostbyaddr((const void *)&address,sizeof ip,AF_INET);
newPort = atoi(newport);
memset((void*)&addrtcp,(int)'\0',sizeof(addrtcp));
addrtcp.sin_family=AF_INET;
addrtcp.sin_addr.s_addr=((struct in_addr *)(newHost->h_addr_list[0]))->s_addr;
addrtcp.sin_port=htons((u_short)newPort);
k = connect(fdtcp,(struct sockaddr*)&addrtcp,sizeof(addrtcp));
if (k==-1) exit(1); // Erro
// REQ Tn (Conteudo Solicitado)
ptr = strcat(reqdata, tn);
ptr = strcat(reqdata, "\n");
// Envia-se o Comando REQ
nreqleft = 25;
while(nreqleft>0) {
kwrite=write(fdtcp,ptr,nreqleft);
if (kwrite<=0) exit(1); // Erro
nreqleft -= kwrite;
ptr += kwrite;
}
// Recebe-se o Comando REP
nreqleft = 128;
ptr = &buffertcp[0];
kread=read(fdtcp,ptr,nreqleft);
if (kread==-1) exit(1); // Erro
cmd = strtok(buffertcp, " "); // REP
cmd = strtok(NULL, " "); // Status
if(strcmp(cmd,"ok")) {
printf("ERR\n");
exit(1); // Erro
}
cmd = strtok(NULL, " "); // Size
size = atoi(cmd);
// Recebem-se os Dados do Conteúdo Desejado
nreqleft = size;
char data[size];
ptr = &data[0];
while(nreqleft>0) {
kread=read(fdtcp,ptr,nreqleft);
if (kread==-1) exit(1); // Erro
nreqleft -= kread;
ptr += kread;
}
file = fopen("file","w");
fwrite(data, 1, size, file);
fclose(file);
close(fdtcp);
// --------------------------------------------------- //
exit(0);
}
The "other stuff" part is just variables declarations and a UDP connection with another server which has nothing to do with this part, so I'm 100% sure it won't affect this part. In fact, on client.c, if I place an printf of the message received from the server, it will show "REP ok 31800 ?????" which ??? I assume would be the data of the file.
The problem is that the "file" created can't be open. Help?
One problem is that 31800 is much larger than 300, and so when you append the data to your ptr2 array in the server, you have buffer overrun. You can correct that by not sending the data with a separate write() call after sending your "header" in ptr2. Your write() loop looks like it will loop forever, but I am guessing you are not showing all of your code.
In the receiver, I don't see any attempt to parse the header to separate the header from the data. Since you read in up to 128 bytes, that read may have received both the header and some data of the file, and you make no attempt to detect and save that part of the file.
When debugging file transfer applications, I would start with textual files so that you can visually see the resulting file, and run a simple diff on the file you saved with the actual file to see if there are differences.

Polling interface names via SIOCGIFCONF in Linux

I'm attempting to poll networking device names. I've pieced this together from various snippets,
http://unixhelp.ed.ac.uk/CGI/man-cgi?netdevice+7
http://lists.apple.com/archives/Unix-porting/2002/Apr/msg00134.html
http://ubuntuforums.org/showthread.php?t=1421487
But my output is just gibberish.
#include <stdio.h>
#include <stdlib.h>
#include <net/route.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#define BUFLEN 1024
#define SEQ 9999
int main (int argc, const char* argv[])
{
// File descriptor for socket
int socketfd;
struct ifconf conf;
struct ifreq req[10];
struct ifreq *ifr;
printf("Opening socket...");
socketfd = socket(AF_ROUTE, SOCK_RAW, 0);
if (socketfd >= 0) {
printf(" OK\n");
conf.ifc_len = sizeof(req);
conf.ifc_buf = (__caddr_t) req;
ioctl(socketfd,SIOCGIFCONF,&conf);
printf("Discovering interfaces...\n");
int i;
for (i=0; i<conf.ifc_len/sizeof(req[0]); i++) {
ifr = &conf.ifc_req[i];
printf("%d. %s\n", i+1, req[i].ifr_name);
}
}
else {
printf("Failed!\n");
}
return 0;
}
Output:
Opening socket... OK
Discovering interfaces...
?u???}??Gh???
2. p?9}?
3.
4. v?=?n??u?`?y??]g?<?~?v??
5.
6.
7.
8. ?v?T?
9. ?|?mw??j??v??h??|??v?T00~??v?$?|??|?#
10. T00~??v?$?|??|?#
I tried outputting each char of the ifr_name array one-by-one to see if they were null terminated but that didn't change much. Each iteration of my program outputs something different so this leads me to think I'm referencing something wrong. Can someone provide me some insight as to what I may be doing wrong?
Here's some code I put together for Mac OS X:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
/* This is defined on Mac OS X */
#ifndef _SIZEOF_ADDR_IFREQ
#define _SIZEOF_ADDR_IFREQ sizeof
#endif
int main (int argc, const char* argv[])
{
// File descriptor for socket
int socketfd;
struct ifconf conf;
char data[4096];
struct ifreq *ifr;
char addrbuf[1024];
int i;
printf("Opening socket...");
socketfd = socket(AF_INET, SOCK_DGRAM, 0);
if (socketfd >= 0) {
printf(" OK\n");
conf.ifc_len = sizeof(data);
conf.ifc_buf = (caddr_t) data;
if (ioctl(socketfd,SIOCGIFCONF,&conf) < 0) {
perror("ioctl");
}
printf("Discovering interfaces...\n");
i = 0;
ifr = (struct ifreq*)data;
while ((char*)ifr < data+conf.ifc_len) {
switch (ifr->ifr_addr.sa_family) {
case AF_INET:
++i;
printf("%d. %s : %s\n", i, ifr->ifr_name, inet_ntop(ifr->ifr_addr.sa_family, &((struct sockaddr_in*)&ifr->ifr_addr)->sin_addr, addrbuf, sizeof(addrbuf)));
break;
#if 0
case AF_INET6:
++i;
printf("%d. %s : %s\n", i, ifr->ifr_name, inet_ntop(ifr->ifr_addr.sa_family, &((struct sockaddr_in6*)&ifr->ifr_addr)->sin6_addr, addrbuf, sizeof(addrbuf)));
break;
#endif
}
ifr = (struct ifreq*)((char*)ifr +_SIZEOF_ADDR_IFREQ(*ifr));
}
close(socketfd);
}
else {
printf(" Failed!\n");
}
return 0;
}
Poll as in you want to be notified if an interface is added or removed? Or polled as in you just want to find out the interface names once from the system? If the latter, take a look at getifaddrs().
Please see http://git.netfilter.org/cgi-bin/gitweb.cgi?p=libmnl.git;a=blob;f=examples/rtnl/rtnl-link-dump.c;hb=HEAD on how to get the list of interfaces on Linux. AF_ROUTE is some BSD thing and the use of ioctl is discouraged on Linux for its apparent limitations (such as to convey multiple addresses on a single interface).

How to get MAC address of your machine using a C program?

I am working on Ubuntu. How can I get MAC address of my machine or an interface say eth0 using C program.
Much nicer than all this socket or shell madness is simply using sysfs for this:
the file /sys/class/net/eth0/address carries your mac adress as simple string you can read with fopen()/fscanf()/fclose(). Nothing easier than that.
And if you want to support other network interfaces than eth0 (and you probably want), then simply use opendir()/readdir()/closedir() on /sys/class/net/.
You need to iterate over all the available interfaces on your machine, and use ioctl with SIOCGIFHWADDR flag to get the mac address. The mac address will be obtained as a 6-octet binary array. You also want to skip the loopback interface.
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
int main()
{
struct ifreq ifr;
struct ifconf ifc;
char buf[1024];
int success = 0;
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock == -1) { /* handle error*/ };
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { /* handle error */ }
struct ifreq* it = ifc.ifc_req;
const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));
for (; it != end; ++it) {
strcpy(ifr.ifr_name, it->ifr_name);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
if (! (ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
success = 1;
break;
}
}
}
else { /* handle error */ }
}
unsigned char mac_address[6];
if (success) memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
}
You want to take a look at the getifaddrs(3) manual page. There is an example in C in the manpage itself that you can use. You want to get the address with the type AF_LINK.
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
int main()
{
struct ifreq s;
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
strcpy(s.ifr_name, "eth0");
if (0 == ioctl(fd, SIOCGIFHWADDR, &s)) {
int i;
for (i = 0; i < 6; ++i)
printf(" %02x", (unsigned char) s.ifr_addr.sa_data[i]);
puts("\n");
return 0;
}
return 1;
}
Using getifaddrs you can get MAC address from the family AF_PACKET.
In order to display the MAC address to each interface, you can proceed like this:
#include <stdio.h>
#include <ifaddrs.h>
#include <netpacket/packet.h>
int main (int argc, const char * argv[])
{
struct ifaddrs *ifaddr=NULL;
struct ifaddrs *ifa = NULL;
int i = 0;
if (getifaddrs(&ifaddr) == -1)
{
perror("getifaddrs");
}
else
{
for ( ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if ( (ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_PACKET) )
{
struct sockaddr_ll *s = (struct sockaddr_ll*)ifa->ifa_addr;
printf("%-8s ", ifa->ifa_name);
for (i=0; i <s->sll_halen; i++)
{
printf("%02x%c", (s->sll_addr[i]), (i+1!=s->sll_halen)?':':'\n');
}
}
}
freeifaddrs(ifaddr);
}
return 0;
}
Ideone
I have just write one and test it on gentoo in virtualbox.
// get_mac.c
#include <stdio.h> //printf
#include <string.h> //strncpy
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h> //ifreq
#include <unistd.h> //close
int main()
{
int fd;
struct ifreq ifr;
char *iface = "enp0s3";
unsigned char *mac = NULL;
memset(&ifr, 0, sizeof(ifr));
fd = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name , iface , IFNAMSIZ-1);
if (0 == ioctl(fd, SIOCGIFHWADDR, &ifr)) {
mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;
//display mac address
printf("Mac : %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n" , mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
close(fd);
return 0;
}
Assuming that c++ code (c++11) is okay as well and the interface is known.
#include <cstdint>
#include <fstream>
#include <streambuf>
#include <regex>
using namespace std;
uint64_t getIFMAC(const string &ifname) {
ifstream iface("/sys/class/net/" + ifname + "/address");
string str((istreambuf_iterator<char>(iface)), istreambuf_iterator<char>());
if (str.length() > 0) {
string hex = regex_replace(str, std::regex(":"), "");
return stoull(hex, 0, 16);
} else {
return 0;
}
}
int main()
{
string iface = "eth0";
printf("%s: mac=%016llX\n", iface.c_str(), getIFMAC(iface));
}
On Linux, use the service of "Network Manager" over the DBus.
There is also good'ol shell program which can be invoke and the result grabbed (use an exec function under C):
$ /sbin/ifconfig | grep HWaddr
A very portable way is to parse the output of this command.
ifconfig | awk '$0 ~ /HWaddr/ { print $5 }'
Provided ifconfig can be run as the current user (usually can) and awk is installed (it often is). This will give you the mac address of the machine.
Expanding on the answer given by #user175104 ...
std::vector<std::string> GetAllFiles(const std::string& folder, bool recursive = false)
{
// uses opendir, readdir, and struct dirent.
// left as an exercise to the reader, as it isn't the point of this OP and answer.
}
bool ReadFileContents(const std::string& folder, const std::string& fname, std::string& contents)
{
// uses ifstream to read entire contents
// left as an exercise to the reader, as it isn't the point of this OP and answer.
}
std::vector<std::string> GetAllMacAddresses()
{
std::vector<std::string> macs;
std::string address;
// from: https://stackoverflow.com/questions/9034575/c-c-linux-mac-address-of-all-interfaces
// ... just read /sys/class/net/eth0/address
// NOTE: there may be more than one: /sys/class/net/*/address
// (1) so walk /sys/class/net/* to find the names to read the address of.
std::vector<std::string> nets = GetAllFiles("/sys/class/net/", false);
for (auto it = nets.begin(); it != nets.end(); ++it)
{
// we don't care about the local loopback interface
if (0 == strcmp((*it).substr(-3).c_str(), "/lo"))
continue;
address.clear();
if (ReadFileContents(*it, "address", address))
{
if (!address.empty())
{
macs.push_back(address);
}
}
}
return macs;
}
netlink socket is possible
man netlink(7) netlink(3) rtnetlink(7) rtnetlink(3)
#include <assert.h>
#include <stdio.h>
#include <linux/if.h>
#include <linux/rtnetlink.h>
#include <unistd.h>
#define SZ 8192
int main(){
// Send
typedef struct {
struct nlmsghdr nh;
struct ifinfomsg ifi;
} Req_getlink;
assert(NLMSG_LENGTH(sizeof(struct ifinfomsg))==sizeof(Req_getlink));
int fd=-1;
fd=socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE);
assert(0==bind(fd,(struct sockaddr*)(&(struct sockaddr_nl){
.nl_family=AF_NETLINK,
.nl_pad=0,
.nl_pid=getpid(),
.nl_groups=0
}),sizeof(struct sockaddr_nl)));
assert(sizeof(Req_getlink)==send(fd,&(Req_getlink){
.nh={
.nlmsg_len=NLMSG_LENGTH(sizeof(struct ifinfomsg)),
.nlmsg_type=RTM_GETLINK,
.nlmsg_flags=NLM_F_REQUEST|NLM_F_ROOT,
.nlmsg_seq=0,
.nlmsg_pid=0
},
.ifi={
.ifi_family=AF_UNSPEC,
// .ifi_family=AF_INET,
.ifi_type=0,
.ifi_index=0,
.ifi_flags=0,
.ifi_change=0,
}
},sizeof(Req_getlink),0));
// Receive
char recvbuf[SZ]={};
int len=0;
for(char *p=recvbuf;;){
const int seglen=recv(fd,p,sizeof(recvbuf)-len,0);
assert(seglen>=1);
len += seglen;
if(((struct nlmsghdr*)p)->nlmsg_type==NLMSG_DONE||((struct nlmsghdr*)p)->nlmsg_type==NLMSG_ERROR)
break;
p += seglen;
}
struct nlmsghdr *nh=(struct nlmsghdr*)recvbuf;
for(;NLMSG_OK(nh,len);nh=NLMSG_NEXT(nh,len)){
if(nh->nlmsg_type==NLMSG_DONE)
break;
struct ifinfomsg *ifm=(struct ifinfomsg*)NLMSG_DATA(nh);
printf("#%d ",ifm->ifi_index);
#ifdef _NET_IF_H
#pragma GCC error "include <linux/if.h> instead of <net/if.h>"
#endif
// Part 3 rtattr
struct rtattr *rta=IFLA_RTA(ifm); // /usr/include/linux/if_link.h
int rtl=RTM_PAYLOAD(nh);
for(;RTA_OK(rta,rtl);rta=RTA_NEXT(rta,rtl))switch(rta->rta_type){
case IFLA_IFNAME:printf("%s ",(const char*)RTA_DATA(rta));break;
case IFLA_ADDRESS:
printf("hwaddr ");
for(int i=0;i<5;++i)
printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
break;
case IFLA_BROADCAST:
printf("bcast ");
for(int i=0;i<5;++i)
printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
break;
case IFLA_PERM_ADDRESS:
printf("perm ");
for(int i=0;i<5;++i)
printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
break;
}
printf("\n");
}
close(fd);
fd=-1;
return 0;
}
Example
#1 lo hwaddr 00:00:00:00:00:00 bcast 00:00:00:00:00:00
#2 eth0 hwaddr 57:da:52:45:5b:1a bcast ff:ff:ff:ff:ff:ff perm 57:da:52:45:5b:1a
#3 wlan0 hwaddr 3c:7f:46:47:58:c2 bcast ff:ff:ff:ff:ff:ff perm 3c:7f:46:47:58:c2
This is a Bash line that prints all available mac addresses, except the loopback:
for x in `ls /sys/class/net |grep -v lo`; do cat /sys/class/net/$x/address; done
Can be executed from a C program.

Resources