/* * I've got tired from trying to get browsing in MSN (Microsoft * Neigborhood) work through a router. I even started giving up the * belief that it's actually possible... I mean to get one domain * working is no problem, but arbitrary mixture of workgroups, * domains and OSes seem to be a subject for "Mission Impossible" * manuscript writers. * * So I came up with this crazy idea to tunnel the MSN broadcast * traffic between two subnets. The idea can't be simpler. I pick up * MSN broadcast traffic off RAW_UDP socket, meaning that I get IP- * and UDP-headers along. Then I ship this whole packet to a relay * in the other subnet as payload of an UDP packet. Relay does * nothing but replaces destination address with local broadcast and * simply injects the raw packet (i.e. with the *original* * destination address and stuff) into the local wire. Works like a * charm! Well, there is one thing that actually doesn't work (at * least when the relay runs on a Solaris box). To be specific * smbclient isn't capable to resolve names with broadcast... The * catch is that bound RAW_UDP socket delivers only datagrams with * the *same* destination and source port number. Both Windows and * SAMBA nmbd do send such datagrams, but not smbclient:-( * * Note that the program was written for Solaris and I at this point * have no idea if it would work under any other OS. * * This program is naturally provided "AS IS" with no warranties of * any kind. * * Copyright (c) 2000 Andy Polyakov */ #include #include #include #include #include #include #include #include #include #include struct ip_hdr { #if defined(_LITTLE_ENDIAN) unsigned int ihl:4; unsigned int version:4; #elif defined(_BIG_ENDIAN) unsigned int version:4; unsigned int ihl:4; #else #error "undefined ENDIANness" #endif unsigned char tos; unsigned short tot_len, id, frag_off; unsigned char ttl, protocol; unsigned short check; unsigned int saddr, daddr; }; struct udp_hdr { unsigned short sport,dport,len,check; }; #define MSN_137 htons(137) #define MSN_138 htons(138) #define MSN_RELAY htons(1001) #define MSN_MTU 1500 /* as large as Ethernet MTU */ main (int argc, char **argv) { int raw_udp137,raw_udp138,raw_out,udp,one=1,nrelays,i; struct sockaddr_in _addr,*relays; struct in_addr bcast; struct hostent *hp; pid_t pid; if (argc < 3) fprintf (stderr,"Usage: %s ...\n",argv[0]), exit (1); if ((bcast.s_addr = inet_addr (argv[1])) == (in_addr_t)-1) { if (!(hp = gethostbyname (argv[1]))) fprintf (stderr,"%s: unable to gethostbyname(\"%s\"): %s\n", argv[0],argv[1],strerror(errno)), exit (errno); if (hp->h_addrtype != AF_INET) /* well, not supposed to return anything else */ fprintf (stderr,"%s: only IPv4 is supported.\n",argv[0]), exit (1); memcpy (&(bcast.s_addr),hp->h_addr_list[0],sizeof(bcast.s_addr)); } nrelays = argc-2; if ((relays = malloc (nrelays*sizeof(relays[0]))) == NULL) fprintf (stderr,"%s: unable to allocate few bytes\n",argv[0]), exit(1); for (i=0;ih_addrtype != AF_INET) /* see comment above */ fprintf (stderr,"%s: only IPv4 is supported\n",argv[0]), exit (1); memcpy (&(relays[i].sin_addr.s_addr),hp->h_addr_list[0],sizeof(relays[0].sin_addr.s_addr)); } relays[i].sin_family = AF_INET; relays[i].sin_port = MSN_RELAY; } if ((raw_out = socket (AF_INET,SOCK_RAW,IPPROTO_RAW)) == -1) fprintf (stderr,"%s: unable to create IPPROTO_RAW socket: %s\n", argv[0],strerror(errno)), exit (errno); setsockopt (raw_out,SOL_SOCKET,SO_BROADCAST,(char *)&one,sizeof(one)); if ((raw_udp137 = socket (AF_INET,SOCK_RAW,IPPROTO_UDP)) == -1) fprintf (stderr,"%s: unable to create IPPROTO_UDP socket: %s\n", argv[0],strerror(errno)), exit (errno); setsockopt (raw_udp137,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one)); _addr.sin_family = AF_INET; _addr.sin_addr.s_addr = INADDR_ANY; _addr.sin_port = MSN_137; if (bind (raw_udp137,(struct sockaddr *)&_addr,sizeof(_addr)) == -1) fprintf (stderr,"%s: unable to bind IPPROTO_UDP socket: %s\n", argv[0],strerror(errno)), exit (errno); if ((raw_udp138 = socket (AF_INET,SOCK_RAW,IPPROTO_UDP)) == -1) fprintf (stderr,"%s: unable to create IPPROTO_UDP socket: %s\n", argv[0],strerror(errno)), exit (errno); setsockopt (raw_udp138,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one)); _addr.sin_family = AF_INET; _addr.sin_addr.s_addr = INADDR_ANY; _addr.sin_port = MSN_138; if (bind (raw_udp138,(struct sockaddr *)&_addr,sizeof(_addr)) == -1) fprintf (stderr,"%s: unable to bind IPPROTO_UDP socket: %s\n", argv[0],strerror(errno)), exit (errno); if ((udp = socket (AF_INET,SOCK_DGRAM,0)) == -1) fprintf (stderr,"%s: unable to cread SOCK_DGRAM socket: %s\n", argv[0],strerror(errno)), exit (errno); setsockopt (udp,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one)); _addr.sin_family = AF_INET; _addr.sin_addr.s_addr = INADDR_ANY; _addr.sin_port = MSN_RELAY; if (bind (udp,(struct sockaddr *)&_addr,sizeof(_addr)) == -1) fprintf (stderr,"%s: unable to bind SOCK_DGRAM socket: %s\n", argv[0],strerror(errno)); /* * Daemonize... */ if ((pid = fork()) < 0) fprintf (stderr,"%s: unable to fork: %s\n",argv[0],strerror(errno)), exit (errno); else if (pid) exit (0); close (2), close (1), close (0); i = open ("/dev/null", O_RDWR); dup2 (i,1), dup2 (i,2); setsid(); while (1) { struct pollfd fds [3]; unsigned int buf[MSN_MTU/sizeof(unsigned int)]; struct ip_hdr *ip = (struct ip_hdr *)buf; int numfd,len,_alen,tail,fd; _alen=sizeof(_addr); fds[0].fd = raw_udp137; fds[1].fd = raw_udp138; fds[2].fd = udp; fds[0].events = fds[1].events = fds[2].events = POLLIN; numfd = poll (fds,sizeof(fds)/sizeof(fds[0]),-1); for (i=0;numfd>0 && i<(sizeof(fds)/sizeof(fds[0]));i++) { if (fds[i].revents & POLLIN) { numfd--; fd = fds[i].fd; /* broadcasts on UDP port 137, relay 'em */ if ((fd == raw_udp137 || fd == raw_udp138) && (len = recv (fd,(void *)buf,sizeof(buf),0)) > 0 ) { if ((tail = ntohs(ip->tot_len) + ip->ihl*4 - len) > 0) { /* * I'll discard packets larger than MSN_MTU till I figure a * better way... I fetch it without polling for more data * because the IP layer *has* collected all the fragments. */ do len = recv (fd,(void *)buf,sizeof(buf),0); while (len>0 && (tail-=len)>0); } else if (ip->ttl > 1 && ip->daddr == bcast.s_addr) { /* * Despite what manual says RAW_IP replaces tot_len with * the size of (reassembled) payload and we have to fix * it up to make it look real. */ ip->tot_len = htons(len); for (i=0;i 0 ) { struct udp_hdr *udp = (struct udp_hdr *)(buf+ip->ihl); for (i=0;iversion == 4 && ip->tot_len == htons(len) && ip->protocol == IPPROTO_UDP && (udp->dport == MSN_137 || udp->dport == MSN_138) ) { /* * drop TTL in order to prevent relaying it back as * we ourselves are going to "hear" it too... */ ip->ttl = 1; ip->daddr = bcast.s_addr; sendto (raw_out,(void *)buf,len,0, /* * Following arguments are meaningless (at least * in Solaris), but have be present. So we simply * pick _addr... */ (struct sockaddr *)&_addr,sizeof(_addr)); } } } } } }