1 #if defined(__MINGW32__) && (!defined(WINVER) || WINVER < 0x501)
2 /* Assume the target is newer than Windows XP */
3 # undef WINVER
4 # undef _WIN32_WINDOWS
5 # undef _WIN32_WINNT
6 # define WINVER _WIN32_WINNT_VISTA
7 # define _WIN32_WINDOWS _WIN32_WINNT_VISTA
8 # define _WIN32_WINNT _WIN32_WINNT_VISTA
9 #endif
10 
11 #ifdef _MSC_VER
12 #define _CRT_SECURE_NO_WARNINGS
13 #endif
14 
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 
18 #ifdef _WIN32
19 
20 # define _WINSOCK_DEPRECATED_NO_WARNINGS /* inet_ntoa is deprecated on MSVC but used for compatibility */
21 # include <winsock2.h>
22 # include <ws2tcpip.h>
23 
my_inet_pton(int af,const char * src,void * dst)24 static int my_inet_pton(int af, const char *src, void *dst)
25 {
26   struct sockaddr_storage ss;
27   int size = sizeof(ss);
28   char src_copy[INET6_ADDRSTRLEN+1];
29 
30   ZeroMemory(&ss, sizeof(ss));
31   /* stupid non-const API */
32   strncpy (src_copy, src, INET6_ADDRSTRLEN+1);
33   src_copy[INET6_ADDRSTRLEN] = 0;
34 
35   if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) {
36     switch(af) {
37       case AF_INET:
38     *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
39     return 1;
40       case AF_INET6:
41     *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
42     return 1;
43     }
44   }
45   return 0;
46 }
47 
48 #define INET_PTON my_inet_pton
49 #else
50 # include <arpa/inet.h>
51 # include <netinet/in.h>
52 # include <sys/select.h>
53 # include <sys/ioctl.h>
54 #define INET_PTON inet_pton
55 #endif
56 
57 #include <fcntl.h>
58 #include <errno.h>
59 #include <signal.h>
60 #include <stdio.h>
61 #include <string.h>
62 #include <stdlib.h>
63 #ifdef _WIN32
64 # define CLOSESOCKET(S) closesocket((SOCKET)S)
65 # define ssize_t int
66 #else
67 # include <unistd.h> // read, write, close
68 # define CLOSESOCKET(S) close(S)
69 #endif
70 
71 #include <libmdnsd/mdnsd.h>
72 #include <libmdnsd/sdtxt.h>
73 
74 int _shutdown = 0;
75 mdns_daemon_t *_d;
76 int daemon_socket = 0;
77 
conflict(char * name,int type,void * arg)78 void conflict(char *name, int type, void *arg)
79 {
80 	printf("conflicting name detected %s for type %d\n", name, type);
81 	exit(1);
82 }
83 
record_received(const struct resource * r,void * data)84 void record_received(const struct resource* r, void* data) {
85 	#ifndef _WIN32
86 	char ipinput[INET_ADDRSTRLEN];
87 	#endif
88 	switch(r->type) {
89 		case QTYPE_A:
90 			#ifndef _WIN32
91 			inet_ntop(AF_INET, &(r->known.a.ip), ipinput, INET_ADDRSTRLEN);
92 			printf("Got %s: A %s->%s\n", r->name,r->known.a.name, ipinput);
93 			#else
94 			printf("Got %s: A %s\n", r->name,r->known.a.name);
95 			#endif
96 			break;
97 		case QTYPE_NS:
98 			printf("Got %s: NS %s\n", r->name,r->known.ns.name);
99 			break;
100 		case QTYPE_CNAME:
101 			printf("Got %s: CNAME %s\n", r->name,r->known.cname.name);
102 			break;
103 		case QTYPE_PTR:
104 			printf("Got %s: PTR %s\n", r->name,r->known.ptr.name);
105 			break;
106 		case QTYPE_TXT:
107 			printf("Got %s: TXT %s\n", r->name,r->rdata);
108 			break;
109 		case QTYPE_SRV:
110 			printf("Got %s: SRV %d %d %d %s\n", r->name,r->known.srv.priority,r->known.srv.weight,r->known.srv.port,r->known.srv.name);
111 			break;
112 		default:
113 			printf("Got %s: unknown\n", r->name);
114 
115 	}
116 
117 }
118 
done(int sig)119 void done(int sig)
120 {
121 	_shutdown = 1;
122 	mdnsd_shutdown(_d);
123 	// wake up select
124 	write(daemon_socket, "\0", 1);
125 }
126 
socket_set_nonblocking(int sockfd)127 static void socket_set_nonblocking(int sockfd) {
128 #ifdef _WIN32
129 	u_long iMode = 1;
130     ioctlsocket(sockfd, FIONBIO, &iMode);
131 #else
132 	int opts = fcntl(sockfd, F_GETFL);
133 	fcntl(sockfd, F_SETFL, opts|O_NONBLOCK);
134 #endif
135 }
136 
137 /* Create multicast 224.0.0.251:5353 socket */
msock(void)138 int msock(void)
139 {
140 	int s, flag = 1, ittl = 255;
141 	struct sockaddr_in in;
142 	struct ip_mreq mc;
143 	u_char ttl = 255; // send to any reachable net, not only the subnet
144 
145 	memset(&in, 0, sizeof(in));
146 	in.sin_family = AF_INET;
147 	in.sin_port = htons(5353);
148 	in.sin_addr.s_addr = 0;
149 
150 	if ((s = (int)socket(AF_INET, SOCK_DGRAM, 0)) < 0)
151 		return 0;
152 
153 #ifdef SO_REUSEPORT
154 	setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (char *)&flag, sizeof(flag));
155 #endif
156 	setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
157 	if (bind(s, (struct sockaddr *)&in, sizeof(in))) {
158 		CLOSESOCKET(s);
159 		return 0;
160 	}
161 
162 	mc.imr_multiaddr.s_addr = inet_addr("224.0.0.251");
163 	mc.imr_interface.s_addr = htonl(INADDR_ANY);
164 	setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&mc, sizeof(mc));
165 	setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&ttl, sizeof(ttl));
166 	setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&ittl, sizeof(ittl));
167 
168 	socket_set_nonblocking(s);
169 
170 	return s;
171 }
172 
main(int argc,char * argv[])173 int main(int argc, char *argv[])
174 {
175 	mdns_daemon_t *d;
176 	mdns_record_t *r;
177 	struct in_addr ip;
178 	unsigned short int port;
179 	fd_set fds;
180 	int s;
181 	char hlocal[256], nlocal[256];
182 	unsigned char *packet;
183 	int len = 0;
184 	xht_t *h;
185 	char *path = NULL;
186 
187 	if (argc < 4) {
188 		printf("usage: mhttp 'unique name' 12.34.56.78 80 '/optionalpath'\n");
189 		return 1;
190 	}
191 
192 	INET_PTON(AF_INET,argv[2], &ip);
193 	port = atoi(argv[3]);
194 	if (argc == 5)
195 		path = argv[4];
196 	printf("Announcing .local site named '%s' to %s:%d and extra path '%s'\n", argv[1], inet_ntoa(ip), port, argv[4]);
197 
198 	signal(SIGINT, done);
199 	#ifdef SIGHUP
200 	signal(SIGHUP, done);
201 	#endif
202 	#ifdef SIGQUIT
203 	signal(SIGQUIT, done);
204 	#endif
205 	signal(SIGTERM, done);
206 	_d = d = mdnsd_new(QCLASS_IN, 1000);
207 	if ((s = msock()) == 0) {
208 		printf("can't create socket: %s\n", strerror(errno));
209 		return 1;
210 	}
211 
212 
213 	mdnsd_register_receive_callback(d, record_received, NULL);
214 
215 
216 	sprintf(hlocal, "%s._http._tcp.local.", argv[1]);
217 	sprintf(nlocal, "http-%s.local.", argv[1]);
218 
219 	// Announce that we have a _http._tcp service
220 	r = mdnsd_shared(d, "_services._dns-sd._udp.local.", QTYPE_PTR, 120);
221 	mdnsd_set_host(d, r, "_http._tcp.local.");
222 
223 	r = mdnsd_shared(d, "_http._tcp.local.", QTYPE_PTR, 120);
224 	mdnsd_set_host(d, r, hlocal);
225 	r = mdnsd_unique(d, hlocal, QTYPE_SRV, 600, conflict, 0);
226 	mdnsd_set_srv(d, r, 0, 0, port, nlocal);
227 	r = mdnsd_unique(d, nlocal, QTYPE_A, 600, conflict, 0);
228 	mdnsd_set_raw(d, r, (char *)&ip.s_addr, 4);
229 	r = mdnsd_unique(d, hlocal, QTYPE_TXT, 600, conflict, 0);
230 	h = xht_new(11);
231 	if (path && strlen(path))
232 		xht_set(h, "path", path);
233 	packet = sd2txt(h, &len);
234 	xht_free(h);
235 	mdnsd_set_raw(d, r, (char *)packet, len);
236 	MDNSD_free(packet);
237 
238 	// example for getting a previously published record:
239 	{
240 		mdns_record_t *get_r = mdnsd_get_published(d, "_http._tcp.local.");
241 		while(get_r) {
242 			const mdns_answer_t *data = mdnsd_record_data(get_r);
243 			printf("Found record of type %d\n", data->type);
244 			get_r = mdnsd_record_next(get_r);
245 		}
246 	}
247 
248 	{
249 		struct timeval next_sleep;
250 		next_sleep.tv_sec = 0;
251 		next_sleep.tv_usec = 0;
252 		while (1) {
253 
254 			FD_ZERO(&fds);
255 			FD_SET(s, &fds);
256 			select(s + 1, &fds, 0, 0, &next_sleep);
257 
258 			if (_shutdown)
259 				break;
260 
261 			{
262 				unsigned short retVal = mdnsd_step(d, s, FD_ISSET(s, &fds), true, &next_sleep);
263 				if (retVal == 1) {
264 					printf("can't read from socket %d: %s\n", errno, strerror(errno));
265 					break;
266 				} else if (retVal == 2) {
267 					printf("can't write to socket: %s\n", strerror(errno));
268 					break;
269 				}
270 			}
271 
272 			if (_shutdown)
273 				break;
274 		}
275 	}
276 
277 	mdnsd_shutdown(d);
278 	mdnsd_free(d);
279 
280 	return 0;
281 }
282