/* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * * This software is provided by Sun ``AS IS'' and any express or implied * warranties, including, but not limited to, the implied warranties of * merchantability and fitness for a particular purpose are disclaimed. * In no event shall Sun Microsystems be liable for any direct, indirect, * incidental, special, exemplary, or consequential damages. * * This software is not a product, and is provided for evaluation purposes * only. * * This software may not be resold without the express permission of * Sun Microsystems. */ #pragma ident "@(#)sctp_server.c 1.2 04/08/24 SMI" #define _XOPEN_SOURCE 500 #define __EXTENSIONS__ /* * Simple networking server which uses SCTP 1-N, 1-1 sockets. */ #include #include #include #include #include #include #include #include #define ECHO_PORT 10117 #define CHARGEN_PORT 10118 #define DISCARD_PORT 10119 #define MAXCONN 64 #define MAXIDLETIME 30 static void print_src(int fd, sctp_assoc_t assoc_id) { struct sctp_status sstat; struct sctp_paddrinfo *spinfo; char tmpname[INET6_ADDRSTRLEN]; unsigned int port; unsigned int ulen; bzero(&sstat, sizeof (sstat)); ulen = sizeof (sstat); if (sctp_opt_info(fd, assoc_id, SCTP_STATUS, &sstat, &ulen) < 0) { perror("sctp_opt_info()"); return; } spinfo = &sstat.sstat_primary; if (spinfo->spinfo_address.ss_family == AF_INET6) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)&spinfo->spinfo_address; inet_ntop(AF_INET6, &sin6->sin6_addr, tmpname, sizeof (tmpname)); port = ntohs(sin6->sin6_port); } else if (spinfo->spinfo_address.ss_family == AF_INET) { struct sockaddr_in *s_in; s_in = (struct sockaddr_in *)&spinfo->spinfo_address; inet_ntop(AF_INET, &s_in->sin_addr, tmpname, sizeof (tmpname)); port = ntohs(s_in->sin_port); } else { sprintf(tmpname, ""); } printf("Msg from %d: %s/%d\n", assoc_id, tmpname, port); } int main(int argc, char **argv) { struct sigaction act; int echo_s, discard_s, chargen_s; struct pollfd pfds[MAXCONN + 3]; struct sockaddr_in6 laddr; struct sctp_event_subscribe ses; int ret, nofds, i, j = 0; int k; struct msghdr mhdr; struct iovec iov; char data[8192], chargen_data[128], *ch; char cdata[sizeof (struct sctp_sndrcvinfo) + sizeof (struct cmsghdr)]; struct cmsghdr *cmsg; struct sctp_sndrcvinfo *sinfo; struct sctp_assoc_change *sac; struct sockaddr_in6 from; socklen_t fromlen = sizeof (struct sockaddr_in6); for (i = 0; i < sizeof (chargen_data); i++) { for (;;) { if (isprint(j)) { chargen_data[i] = j++; break; } j++; if (j >= 128) { j = 0; } } } bzero(&act, sizeof (act)); act.sa_sigaction = SIG_IGN; sigfillset(&act.sa_mask); if (sigaction(SIGPIPE, &act, NULL) < 0) { perror("sigaction()"); exit(1); } /* * Create sockets */ echo_s = socket(AF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP); discard_s = socket(AF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP); chargen_s = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP); if ((echo_s < 0) || (discard_s < 0) || (chargen_s < 0)) { perror("socket()"); exit(1); } i = 1; if (setsockopt(echo_s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof (i)) < 0 || setsockopt(discard_s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof (i)) < 0 || setsockopt(chargen_s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof (i)) < 0) { perror("setsockopt(SO_REUSEADDR)"); exit(1); } /* if (ioctl(chargen_s, FIONBIO, &i) < 0) { perror("ioctl(FIONBIO)"); exit(1); } */ i = MAXIDLETIME; if (setsockopt(echo_s, IPPROTO_SCTP, SCTP_AUTOCLOSE, &i, sizeof (i)) < 0 || setsockopt(discard_s, IPPROTO_SCTP, SCTP_AUTOCLOSE, &i, sizeof (i)) < 0) { perror("setsockopt(SCTP_AUTOCLOSE)"); exit(1); } /* * Be prepared for incoming v4/v6 traffic */ bzero(&laddr, sizeof (laddr)); laddr.sin6_family = AF_INET6; laddr.sin6_port = htons(ECHO_PORT); if (bind(echo_s, (struct sockaddr *)&laddr, sizeof (laddr)) < 0) { perror("bind(ECHO_PORT)"); exit(1); } laddr.sin6_port = htons(DISCARD_PORT); if (bind(discard_s, (struct sockaddr *)&laddr, sizeof (laddr)) < 0) { perror("bind(DISCARD_PORT)"); exit(1); } laddr.sin6_port = htons(CHARGEN_PORT); if (bind(chargen_s, (struct sockaddr *)&laddr, sizeof (laddr)) < 0) { perror("bind(CHARGEN_PORT)"); exit(1); } /* * Turn on association events for one of the sockets */ bzero(&ses, sizeof (ses)); ses.sctp_data_io_event = 1; if (setsockopt(echo_s, IPPROTO_SCTP, SCTP_EVENTS, &ses, sizeof (ses))) { perror("setsockopt(SCTP_EVENTS)"); exit(1); } ses.sctp_association_event = 1; if (setsockopt(discard_s, IPPROTO_SCTP, SCTP_EVENTS, &ses, sizeof (ses))) { perror("setsockopt(SCTP_EVENTS)"); exit(1); } if (listen(echo_s, 5) < 0 || listen(discard_s, 5) < 0 || listen(chargen_s, 5) < 0) { perror("listen()"); exit(1); } /* * Start polling for incoming connections, messages */ bzero(pfds, sizeof (pfds)); pfds[0].fd = echo_s; pfds[0].events = POLLIN|POLLRDNORM; pfds[1].fd = discard_s; pfds[1].events = POLLIN|POLLRDNORM; pfds[2].fd = chargen_s; pfds[2].events = POLLIN|POLLRDNORM; nofds = 3; while (1) { printf("poll\n"); ret = poll(pfds, nofds,-1 ); if (ret < 0) { perror("poll()"); exit(1); } if (ret == 0) { printf("Timeout?!?\n"); continue; } for (i = 0; i < nofds; i++) { if (!pfds[i].revents) { continue; } if (i < 2) { /* 1-N sockets */ bzero(cdata, sizeof (cdata)); bzero(&mhdr, sizeof (mhdr)); mhdr.msg_name = &from; mhdr.msg_namelen = fromlen; mhdr.msg_iov = &iov; mhdr.msg_iovlen = 1; mhdr.msg_control = cdata; mhdr.msg_controllen = sizeof (cdata); iov.iov_base = data; iov.iov_len = sizeof (data); cmsg = (struct cmsghdr *)cdata; sinfo = (struct sctp_sndrcvinfo *)(cmsg + 1); if ((ret = recvmsg(pfds[i].fd, &mhdr, MSG_DONTWAIT)) < 0) { perror("recvmsg()"); } if (mhdr.msg_controllen) { print_src(pfds[i].fd, sinfo->sinfo_assoc_id); } if (i == 0) { /* echo */ iov.iov_len = ret; /* should read until MSG_EOR */ if (sendmsg(pfds[i].fd, &mhdr, MSG_DONTWAIT) < 0) { perror("sendmsg()"); } continue; } /* discard */ if (mhdr.msg_flags & MSG_NOTIFICATION) { sac = (struct sctp_assoc_change *)data; if (sac->sac_type != SCTP_ASSOC_CHANGE) { printf("Shouldn't happen!\n"); continue; } if (sac->sac_state == SCTP_COMM_UP) { printf("New assoc, id: %d\n", sac->sac_assoc_id); } else if (sac->sac_state == SCTP_SHUTDOWN_COMP) { printf("Assoc gone, id: %d\n", sac->sac_assoc_id); } else { printf("Another event (%d) " "id:%d\n", sac->sac_state, sac->sac_assoc_id); } continue; } continue; } if (i == 2) { /* chargen, incoming conn */ pfds[nofds].fd = accept(chargen_s, NULL, NULL); if (pfds[nofds].fd < 0) { perror("accept()"); continue; } k = 1; if (ioctl(pfds[nofds].fd , FIONBIO, &k) < 0) { perror("ioctl(FIONBIO)"); exit(1); } pfds[nofds].events = POLLOUT; nofds++; continue; } while ((ret = write(pfds[i].fd, chargen_data, sizeof (chargen_data))) == sizeof (chargen_data)){ /* printf("ret %d\n", ret); sleep(1); */ } ; printf("ret %d\n", ret); if (ret < 0) { if (errno == EAGAIN) { continue; } if (errno != EPIPE) { perror("write()"); } close(pfds[i].fd); pfds[i] = pfds[nofds - 1]; nofds--; i--; } } } return (0); }