#include #include #include #include #include #include #include #include "sysdep.h" #define DEBUG #ifdef DEBUG #include #endif extern struct in_addr host2ip(char *); int opensocks(u_int32_t addr, int port, int ttl); int closesocks(int ssock_, int rsock_); int OpenSendSock(u_int32_t addr, u_short port, int ttl); int localname(sockaddr_in* p, int ssock_); opensocks(u_int32_t addr, int port, int ttl) { int ssock_; int rsock_; ssock_ = openssock(addr, port, ttl); if (ssock_ < 0) return (-1); /* * Connecting the send socket also bound the local address. * On a multihomed host we need to bind the receive socket * to the same local address the kernel has chosen to send on. */ struct sockaddr_in local; localname(&local, ssock_); rsock_ = openrsock(addr, port, local); if (rsock_ < 0) { close(ssock_); return (-1); } return (0); } closesocks(int ssock_, int rsock_) { if (ssock_ >= 0) { close(ssock_); close(rsock_); ssock_ = rsock_ = -1; } return (0); } localname(struct sockaddr_in* p, int ssock_) { memset((char *)p, 0, sizeof(*p)); p->sin_family = AF_INET; int len = sizeof(*p); /* * returns the current name for ssock_ and put in sockaddr* p */ if (getsockname(ssock_, (struct sockaddr *)p, &len) < 0) { perror("getsockname"); p->sin_addr.s_addr = 0; p->sin_port = 0; } #ifdef DEBUG printf(" sockaddr binded from localname is %u \n", p->sin_addr.s_addr); #endif return(0); } /* * host2ip(char* ) returns strcut in_addr in network byte order * struct in_addr{ unsigned int s_addr;}; * host2ip(char*) called in advance to get input to OpenSendSocket * struct sockaddr_in * { * unsigned short int sa_family_t; * unsigned short int sin_port; * struct in_addr sin_addr;}; * OpenSendSocekt(host2ip(host).s_addr, port, ttl); * addr returned by host2ip should be in network byte order * port should also be in network order */ int OpenSendSock(u_int32_t addr, u_short port, int ttl) { int fd; struct sockaddr_in sin; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("socket"); exit(1); } nonblock(fd); memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = port; sin.sin_addr.s_addr = addr; if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("connect"); exit(1); } if (IN_CLASSD(ntohl(addr))) { #ifdef IP_ADD_MEMBERSHIP u_char t; t = (ttl > 255) ? 255 : (ttl < 0) ? 0 : ttl; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&t, sizeof(t)) < 0) { perror("IP_MULTICAST_TTL"); exit(1); } #else fprintf(stderr, "\ not compiled with support for IP multicast\n\ you must specify a unicast destination\n"); exit(1); #endif } return (fd); } void nonblock(int fd) { int flags = fcntl(fd, F_GETFL, 0); flags |= O_NONBLOCK|O_NDELAY; if (fcntl(fd, F_SETFL, flags) == -1) { perror("fcntl: F_SETFL"); exit(1); } } /* * ssock_ is the return value of OpenSendSock(addr, port, ttl) */ int OpenRcvSock(u_int32_t addr, u_short port, const struct sockaddr_in& local) { int fd; struct sockaddr_in sin; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("socket"); exit(1); } nonblock(fd); int on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { perror("SO_REUSEADDR"); } #ifdef SO_REUSEPORT on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&on, sizeof(on)) < 0) { perror("SO_REUSEPORT"); exit(1); } #endif memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = port; #ifdef IP_ADD_MEMBERSHIP if (IN_CLASSD(ntohl(addr))) { /* * Try to bind the multicast address as the socket * dest address. On many systems this won't work * so fall back to a destination of INADDR_ANY if * the first bind fails. */ sin.sin_addr.s_addr = addr; if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { sin.sin_addr.s_addr = INADDR_ANY; if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) { perror("bind"); exit(1); } } /* * XXX This is bogus multicast setup that really * shouldn't have to be done (group membership should be * implicit in the IP class D address, route should contain * ttl & no loopback flag, etc.). Steve Deering has promised * to fix this for the 4.4bsd release. We're all waiting * with bated breath. */ struct ip_mreq mr; mr.imr_multiaddr.s_addr = addr; mr.imr_interface.s_addr = INADDR_ANY; if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mr, sizeof(mr)) < 0) { perror("IP_ADD_MEMBERSHIP"); exit(1); } } else #endif { /* * bind the local host's address to this socket. If that * fails, another vic probably has the addresses bound so * just exit. */ sin.sin_addr.s_addr = local.sin_addr.s_addr; if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("bind"); exit(1); } /* * Despite several attempts on our part to get this fixed, * Microsoft Windows isn't complient with the Internet Host * Requirements standard (RFC-1122) and won't let us include * the source address in the receive socket demux state. * (The consequence of this is that all conversations have * to be assigned a unique local port so the vat 'side * conversation' (middle click on site name) function is * essentially useless under windows.) */ #ifndef WIN32 /* * (try to) connect the foreign host's address to this socket. */ sin.sin_port = 0; sin.sin_addr.s_addr = addr; connect(fd, (struct sockaddr *)&sin, sizeof(sin)); #endif } /* * XXX don't need this for the session socket. */ int bufsize = 80 * 1024; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize)) < 0) { bufsize = 32 * 1024; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize)) < 0) perror("SO_RCVBUF"); } return (fd); }