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