/*** Multicast chat 020609 This program allows chatting on a LAN using multicast packets. The user can set loopback off, ttl value and their names. Multicast groups go from 224.x.x.x to 239.x.x.x If loopback is switched off the program will not see any packets from the host its on even if the packet is sent by another process, hence I have made the default to be on. The machine you run this on must have routing for multicast addresses set up either explicitly or via a "default" route entry or setsockopt() will return an error. Tested under Linux 2.2, FreeBSD 4.5 and Solaris 7. Compile: Linux & BSD: cc multichat.c Solaris : cc multichat.c -lsocket -lnsl Written by Neil Robertson, June 2002 ***/ #include #include #include #include #define BUFFSIZE 2000 /*** Start here ***/ main(int argc, char **argv) { struct sockaddr_in bind_addr,from; struct ip_mreq imreq; int i,len,sock,size,namelen; int loopback,ttl,port,on; u_long iaddr; char inbuff[BUFFSIZE],outbuff[BUFFSIZE]; char *group,*name,*ptr; fd_set mask; if (argc < 3) goto USAGE; /* Setup defaults */ group=NULL; name=NULL; port=0; ttl=3; loopback=1; /* Go through args */ for(i=1;i < argc;++i) { if (!strcmp(argv[i],"-nlb")) loopback=0; else if (!strcmp(argv[i],"-ttl")) { if (++i == argc) goto USAGE; if (!(ttl=atoi(argv[i]))) { puts("Invalid TTL value."); exit(1); } } else if (!strcmp(argv[i],"-n")) { if (++i == argc) goto USAGE; name=argv[i]; namelen=strlen(name); } else if (!group) group=argv[i]; else if (!port) { if ((port=atoi(argv[i])) < 1 || port > 65535) { puts("Invalid port number"); exit(1); } } else goto USAGE; } if (!group || !port) goto USAGE; /* Create socket */ if ((sock=socket(AF_INET,SOCK_DGRAM,0))==-1) { perror("socket"); exit(1); } /* Join the multicast group */ if ((iaddr=inet_addr(group)) == -1) goto USAGE; imreq.imr_multiaddr.s_addr=iaddr; imreq.imr_interface.s_addr=INADDR_ANY; if (setsockopt( sock, IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&imreq,sizeof(imreq)) == -1) { perror("setsockopt 1"); exit(1); } /* Use this to disable loopback to the local host. Default is on. */ if (!loopback && setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP,(char *)&loopback,sizeof(loopback)) == -1) { perror("setsockopt 2"); exit(1); } /* Use this to set TTL of multicast packets. Default is 1. if (ttl != 1 && setsockopt( sock,IPPROTO_IP,IP_MULTICAST_TTL,(char *)&ttl,sizeof(ttl)) == -1) { perror("setsockopt 3"); exit(1); } /* This is so more than one process can bind to this address */ on=1; if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on)) == -1) { perror("setsockopt 4"); exit(1); } /* Bind */ bind_addr.sin_family=AF_INET; bind_addr.sin_addr.s_addr=iaddr; bind_addr.sin_port=htons(port); if (bind(sock,(struct sockaddr *)&bind_addr,sizeof(bind_addr)) == -1) { perror("bind"); exit(1); } size=sizeof(from); printf("Listening on group %s:%d.\n",group,port); /* Loop */ while(1) { FD_ZERO(&mask); FD_SET(sock,&mask); FD_SET(0,&mask); select(FD_SETSIZE,&mask,0,0,0); if (FD_ISSET(sock,&mask)) { if ((len=recvfrom( sock, inbuff,BUFFSIZE,0,(struct sockaddr *)&from,&size))==-1) { perror("recvfrom"); continue; } printf("%s:%d (%d): ", inet_ntoa(from.sin_addr), ntohs(from.sin_port),len); for(i=0;i < len;++i) putchar(inbuff[i] < 32 && inbuff[i] != 10 && inbuff[i] != 13 ? '?' : inbuff[i]); putchar('\n'); } if (FD_ISSET(0,&mask) && (len=read(0,inbuff,BUFFSIZE))) { if (name) { sprintf(outbuff,"%s says: %s",name,inbuff); len += (namelen + 7); ptr=outbuff; } else ptr=inbuff; if (sendto( sock, ptr,len,0,(struct sockaddr *)&bind_addr,size) == -1) perror("sendto"); } } USAGE: printf("Usage: %s [-lb] [-ttl ] [-n ] \n",argv[0]); exit(1); }