/* This is an UDP server which deals with the clients upon receiving a SIGIO signal. The signal is generated whenever a datagram arrives. For this kind of client handling, three steps have to be done: 1. Establish signal handler for SIGIO 2. Set the owner for receiving SIGIO using fcntl() 3. Set the O_ASYNC flag for socket using fcntl() or use ioctl() */ #include #include #include #include #include #include #include #include #include #include #define PORT 9000 #define SA struct sockaddr #ifdef __linux__ #define IP_RECVDSTADDR IP_PKTINFO struct in_pktinfo *ip_pkt; #elif __FreeBSD__ struct in_addr *ip_addr; #endif void handle_incoming_data(void); int sock; /* Global member. */ void handle_sigio(int signo) { printf("In handle_sigio() function.\n"); handle_incoming_data(); return; } void handle_sigaction(int signo, siginfo_t *details, void *addr) { printf("In handle_sigaction() function.\n"); if (details->si_code == SI_SIGIO) printf("File handle: %d; sock = %d\n", details->si_fd, sock); else printf("si_code = %d\n", details->si_code); handle_incoming_data(); return; } int main(int argc, char **argv) { int retn, temp, flags; struct sockaddr_in addr; char buf[128]; int optval, optlen; struct sigaction act; /* Create a socket. */ sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { perror("socket"); exit(-1); } printf("Created socket.\n"); /* Set the socket to return destination address as ancillary data. */ optval = 1, optlen = sizeof(optval); retn = setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &optval, optlen); if (retn < 0) { perror("IP_PKTINFO"); close(sock); exit(-1); } printf("Successfully set the option to receive destination address.\n"); /* 1. Establish signal handler. */ act.sa_sigaction = &handle_sigaction; act.sa_flags = SA_SIGINFO; printf("PID: %d\n", getpid()); sigaction(SIGIO, &act, NULL); /* 2. Set the SIGIO owner */ retn = fcntl(sock, F_SETOWN, getpid()); if (retn < 0) { perror("fcntl:F_SETOWN"); close(sock); exit(-3); } printf("Successfully set the signal handler for SIGIO signal.\n"); /* 3. Set the ASYNC flag for socket. */ flags = fcntl(sock, F_GETFL); retn = fcntl(sock, F_SETFL, flags | O_ASYNC); if (retn < 0) { perror("fcntl:F_SETFL"); close(sock); exit(-1); } printf("Set the O_ASYNC flag for the socket to facilitate SIGIO.\n"); /* Fill in the address to bind. */ memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = htons(INADDR_ANY); /* Bind the socket. */ retn = bind(sock, (SA *)&addr, sizeof(SA)); if (retn < 0) { perror("Bind"); close(sock); exit(-2); } printf("Bound the socket successfully.\n"); /* Wait for signal and its processing. */ pause(); close(sock); return(0); } /* Routine to handle incoming data. */ void handle_incoming_data(void) { struct sockaddr_in peer; int retn, len; char ip[20], mesg[128]; struct iovec iov[1]; struct msghdr msg; struct cmsghdr *cmsg; /* Obtain the client address. Data is also received here. */ len = sizeof(SA); memset(mesg, 0, sizeof(mesg)); retn = recvfrom(sock, mesg, 128, MSG_PEEK, (SA *)&peer, &len); if (retn < 0) { perror("recvfrom"); return; } printf("Received message from a client.\n"); printf(" Client-address: %s\n", inet_ntoa(peer.sin_addr)); /* Now receive the destination address. */ iov[0].iov_base = mesg; iov[0].iov_len = 1; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = calloc(1, 100); msg.msg_controllen = 100; msg.msg_iov = &iov[0]; msg.msg_iovlen = 1; /* Now issue the recvmsg() */ retn = recvmsg(sock, &msg, 0); if (retn < 0) { perror("recvmsg"); return; } if (! msg.msg_controllen) { printf(" Message: %s\n", mesg); printf("No ancillary data present. Returning.\n"); return; } /* Get the ancillary data using CMSG_* defines. The code becomes platform specific from hereon. */ cmsg = CMSG_FIRSTHDR(&msg); for(;cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if ((cmsg->cmsg_level == IPPROTO_IP) &&(cmsg->cmsg_type == IP_RECVDSTADDR)) { #ifdef __linux__ ip_pkt = (struct in_pktinfo *)CMSG_DATA(cmsg); printf(" Destination address: %s\n", inet_ntoa(ip_pkt->ipi_addr)); #elif __FreeBSD__ ip_addr = (struct in_addr *)CMSG_DATA(cmsg); printf(" Destination address: %s\n", inet_ntoa(*ip_addr)); #endif } } printf(" Message: %s\n", mesg); return; }