2 #include <sys/socket.h>
3 #include <netinet/in.h>
17 #include "dns_sd_txt.h"
20 #elif defined(__GNUC__)
21 # define UNUSED(x) UNUSED_ ## x __attribute__((unused))
22 #elif defined(__LCLINT__)
23 # define UNUSED(x) /*@unused@*/ x
28 #define HOSTNAMESIZE 64
37 typedef struct _service_info
40 char hostname[HOSTNAMESIZE];
46 /* service-discovery records */
47 TMdnsdRecord *host_to_ip;
48 TMdnsdRecord *ip_to_host;
49 struct in_addr announce_ip;
53 /* service-discovery records */
54 TMdnsdRecord *srv_to_host;
55 TMdnsdRecord *txt_for_srv;
57 TMdnsdRecord *ptr_to_srvtype;
58 TMdnsdRecord *ptr_to_srv;
63 static ServiceInfo service_info;
65 void request_service (ServiceInfo *info, int stage);
66 void request_ip_addresses (ServiceInfo *info);
68 char *increment_name (char *name)
75 pos = strrchr (name, '-');
78 id = strtol (pos + 1, &end, 10);
88 asprintf (&ret, "%s-%d", name, id);
94 /* conflict handling */
95 void handle_conflict (TMdnsdRecord *record, char *name, int UNUSED(type), void *arg)
97 ServiceInfo *info = (ServiceInfo *) arg;
100 if (record == info->ip_to_host) {
101 /* can't do anything about a reverse lookup conflict. Just stop
103 info->ip_to_host = NULL;
104 fprintf (stderr, "zeroconf reverse lookup conflict for %s!\n", info->ip);
108 if (record == info->host_to_ip) {
109 info->host_to_ip = NULL;
110 info->announce_ip.s_addr = 0;
113 if (info->servicename == NULL) {
114 newname = increment_name (info->hostname);
116 newname = increment_name (info->servicename);
117 free (info->servicename);
120 info->servicename = newname;
122 if (record == info->srv_to_host) {
123 info->srv_to_host = NULL;
126 if (record == info->txt_for_srv) {
127 info->txt_for_srv = NULL;
130 fprintf (stderr, "conflicting name \"%s\". trying %s\n",
131 name, info->servicename);
133 /* The hostname was changed, so go back to probe state */
134 info->state = MDNSD_PROBE;
138 /* quit and updates */
139 void sighandler (int sig)
142 service_info.state = MDNSD_SHUTDOWN;
147 /* create multicast 224.0.0.251:5353 socket */
154 struct sockaddr_in in;
157 bzero (&in, sizeof (in));
158 in.sin_family = AF_INET;
159 in.sin_port = htons (MDNS_PORT);
160 in.sin_addr.s_addr = 0;
162 if ((sock_fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
166 setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*) &flag, sizeof (flag));
167 if (bind (sock_fd, (struct sockaddr*) &in, sizeof (in))) {
172 mc.imr_multiaddr.s_addr = inet_addr ("224.0.0.251");
173 mc.imr_interface.s_addr = htonl (INADDR_ANY);
174 setsockopt (sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mc, sizeof (mc));
175 setsockopt (sock_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof (ttl));
176 setsockopt (sock_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ittl, sizeof (ittl));
178 flag = fcntl (sock_fd, F_GETFL, 0);
180 fcntl (sock_fd, F_SETFL, flag);
185 void request_ip_addresses (ServiceInfo *info)
192 sprintf (hostlocal, "%s.local.", info->servicename ? info->servicename : info->hostname);
195 ip.s_addr = inet_addr (info->ip);
197 if (ip.s_addr != info->announce_ip.s_addr) {
198 snprintf (revlookup, 256, "%d.%d.%d.%d.in-addr.arpa.",
199 (ip.s_addr >> 24) & 0xff, (ip.s_addr >> 16) & 0xff,
200 (ip.s_addr >> 8) & 0xff, (ip.s_addr >> 0) & 0xff);
202 if (!info->host_to_ip) {
203 info->host_to_ip = MdnsdAllocUnique(info->mdnsd, hostlocal,
204 QTYPE_A, 120, handle_conflict, info);
206 MdnsdSetRaw (info->mdnsd, info->host_to_ip, (char *) &ip, 4);
208 if (!info->ip_to_host) {
209 info->ip_to_host = MdnsdAllocUnique(info->mdnsd, revlookup,
210 QTYPE_PTR, 120, handle_conflict, info);
212 MdnsdSetHost (info->mdnsd, info->ip_to_host, hostlocal);
214 info->announce_ip = ip;
219 if (info->host_to_ip) {
220 MdnsdDone (service_info.mdnsd, info->host_to_ip);
222 if (info->ip_to_host) {
223 MdnsdDone (service_info.mdnsd, info->ip_to_host);
226 info->host_to_ip = NULL;
227 info->ip_to_host = NULL;
228 info->announce_ip.s_addr = 0;
232 void request_service (ServiceInfo *info, int stage)
239 sprintf (servlocal, "%s._http._tcp.local.",
240 info->servicename ? info->servicename : info->hostname);
243 * Timeouts according to
244 * http://files.multicastdns.org/draft-cheshire-dnsext-multicastdns.txt
246 * As a general rule, the recommended TTL value for Multicast DNS
247 * resource records with a host name as the resource record's name
248 * (e.g. A, AAAA, HINFO, etc.) or contained within the resource record's
249 * rdata (e.g. SRV, reverse mapping PTR record, etc.) is 120 seconds.
251 * The recommended TTL value for other Multicast DNS resource records
258 request_ip_addresses (info);
263 sprintf (hostlocal, "%s.local.",
264 info->servicename ? info->servicename : info->hostname);
266 if (!info->srv_to_host) {
267 info->srv_to_host = MdnsdAllocUnique (info->mdnsd, servlocal,
268 QTYPE_SRV, 120, handle_conflict, info);
271 MdnsdSetSrv (info->mdnsd, info->srv_to_host, 0, 0, info->port, hostlocal);
273 if (!info->txt_for_srv) {
274 info->txt_for_srv = MdnsdAllocUnique (info->mdnsd, servlocal,
275 QTYPE_TXT, 4500, handle_conflict, info);
278 packet = DnsSd2Txt (info->metadata, &len);
279 MdnsdSetRaw (info->mdnsd, info->txt_for_srv, packet, len);
284 if (!info->ptr_to_srv) {
285 info->ptr_to_srv = MdnsdAllocShared (info->mdnsd, "_http._tcp.local.",
288 MdnsdSetHost (info->mdnsd, info->ptr_to_srv, servlocal);
290 if (!info->ptr_to_srvtype) {
291 info->ptr_to_srvtype = MdnsdAllocShared (info->mdnsd, "_services._dns-sd._udp.local.",
294 MdnsdSetHost (info->mdnsd, info->ptr_to_srvtype, "_http._tcp.local.");
297 fprintf (stderr, "Announcing \"%s.local\" to %s:%d\n",
298 info->servicename ? info->servicename : info->hostname,
299 info->ip, info->port);
304 fprintf (stderr, "announce stage %d is invalid\n", stage);
309 int main(int argc, char *argv[])
315 int ssize = sizeof(struct sockaddr_in);
316 uint8_t buf[MAX_PACKET_LEN];
317 struct sockaddr_in from;
318 struct sockaddr_in to;
321 struct in_addr remote_ip;
324 int announce_stage = 0;
325 struct pollfd fds[1];
329 fprintf (stderr, "usage: mhttp <hostname> <ip> <port> <key1>=<value1> <key2>=<value2> ...\n");
330 fprintf (stderr, " <hostname> The hostname which shall be promoted as 'hostname.local'\n");
331 fprintf (stderr, " <ip> The IP address to promote\n");
332 fprintf (stderr, " <port> is the port number of the service to be advertized\n");
333 fprintf (stderr, " <key>=<value> are the keys that get embedded into the TXT record.\n");
337 service_info.mdnsd = MdnsdNew (1, 1000);
339 //gethostname (service_info.hostname, HOSTNAMESIZE);
340 strncpy(service_info.hostname, argv[1], HOSTNAMESIZE-1);
341 service_info.hostname[HOSTNAMESIZE-1] = '\0';
343 if (strchr (service_info.hostname, '.'))
344 strchr (service_info.hostname, '.')[0] = '\0';
346 service_info.servicename = NULL;
348 service_info.ip = strdup(argv[2]);
349 service_info.announce_ip.s_addr = inet_addr(service_info.ip);
350 service_info.host_to_ip = NULL;
351 service_info.ip_to_host = NULL;
353 service_info.port = atoi(argv[3]);
355 service_info.metadata = SHashInit (11);
356 for (idx = 3; idx < argc; idx++) {
357 value = index (argv[idx], '=');
361 SHashSet (service_info.metadata, argv[idx], value);
365 service_info.ptr_to_srvtype = NULL;
366 service_info.ptr_to_srv = NULL;
367 service_info.srv_to_host = NULL;
368 service_info.txt_for_srv = NULL;
370 signal(SIGHUP, sighandler);
371 signal(SIGINT, sighandler);
372 signal(SIGQUIT, sighandler);
373 signal(SIGTERM, sighandler);
375 if ((s = msock()) == 0)
377 fprintf (stderr, "can't create socket: %s\n", strerror(errno));
381 request_ip_addresses (&service_info);
383 service_info.state = MDNSD_PROBE;
387 fds[0].events = POLLIN;
390 poll (fds, 1, polltime);
392 switch (service_info.state)
396 if (service_info.ptr_to_srvtype) {
397 MdnsdDone (service_info.mdnsd, service_info.ptr_to_srvtype);
400 if (service_info.ptr_to_srv) {
401 MdnsdDone (service_info.mdnsd, service_info.ptr_to_srv);
404 if (service_info.srv_to_host) {
405 MdnsdDone (service_info.mdnsd, service_info.srv_to_host);
408 if (service_info.txt_for_srv) {
409 MdnsdDone (service_info.mdnsd, service_info.txt_for_srv);
412 service_info.ptr_to_srvtype = NULL;
413 service_info.ptr_to_srv = NULL;
414 service_info.srv_to_host = NULL;
415 service_info.txt_for_srv = NULL;
417 if (service_info.host_to_ip) {
418 MdnsdDone (service_info.mdnsd, service_info.host_to_ip);
421 if (service_info.ip_to_host) {
422 MdnsdDone (service_info.mdnsd, service_info.ip_to_host);
425 service_info.host_to_ip = NULL;
426 service_info.ip_to_host = NULL;
427 service_info.announce_ip.s_addr = 0;
429 service_info.state = MDNSD_ANNOUNCE;
436 if (announce_stage < 3) {
437 struct timeval cur_tv;
440 gettimeofday (&cur_tv, NULL);
441 msecs = (cur_tv.tv_sec - tv.tv_sec) * 1000 + cur_tv.tv_usec / 1000 - tv.tv_usec / 1000;
443 if ((tv.tv_sec == 0) || (msecs > 755)) {
444 request_service (&service_info, announce_stage);
447 cur_tv = *MdnsdGetMaxSleepTime (service_info.mdnsd);
448 polltime = cur_tv.tv_sec * 1000 + cur_tv.tv_usec / 1000;
449 if (polltime >= 756) {
453 cur_tv = *MdnsdGetMaxSleepTime (service_info.mdnsd);
454 polltime = cur_tv.tv_sec * 1000 + cur_tv.tv_usec / 1000;
455 if (polltime >= 756 - msecs) {
456 polltime = 756 - msecs;
460 tv = *MdnsdGetMaxSleepTime (service_info.mdnsd);
461 polltime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
463 service_info.state = MDNSD_RUN;
468 tv = *MdnsdGetMaxSleepTime (service_info.mdnsd);
469 polltime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
473 MdnsdShutdown (service_info.mdnsd);
477 fprintf (stderr, "in default???\n");
481 if (fds[0].revents) {
482 while ((bsize = recvfrom (s, buf, MAX_PACKET_LEN, 0, (struct sockaddr*) &from, &ssize)) > 0)
484 bzero (&msg, sizeof (DNSMESSAGE));
485 DnsParseMsg (&msg, buf);
486 MdnsdInput(service_info.mdnsd, &msg,
491 if (bsize < 0 && errno != EAGAIN) {
492 fprintf (stderr, "can't read from socket: %s\n", strerror (errno));
496 while (MdnsdOutput (service_info.mdnsd, &msg, &remote_ip, &port)) {
497 bzero (&to, sizeof (to));
498 to.sin_family = AF_INET;
500 to.sin_addr.s_addr = remote_ip.s_addr;
502 if (sendto (s, DnsMsg2Pkt (&msg), DnsMsgLen(&msg), 0, (struct sockaddr *) &to, sizeof (struct sockaddr_in)) != DnsMsgLen(&msg)) {
503 fprintf (stderr, "can't write to socket: %s\n", strerror(errno));
507 if (service_info.state == MDNSD_SHUTDOWN) {
512 MdnsdShutdown (service_info.mdnsd);
513 MdnsdFree (service_info.mdnsd);