4 #include <sys/socket.h>
5 #include <netinet/in.h>
21 #define HOSTNAMESIZE 64
22 #define FIFO_PATH "/tmp/mdns-fifo"
24 #define MAX_ANNOUNCE_IP 2
34 typedef struct _ipcam_ip_info
40 /* service-discovery records */
46 typedef struct _ipcam_service_info
49 char hostname[HOSTNAMESIZE];
54 IpcamIPInfo ipinfos[MAX_ANNOUNCE_IP];
58 /* service-discovery records */
67 static IpcamServiceInfo ipcam_info;
68 static int signal_pipe[2];
71 void request_service (IpcamServiceInfo *info, int stage);
72 void request_ip_addresses (IpcamServiceInfo *info);
75 increment_name (char *name)
78 char *pos, *end = NULL;
81 pos = strrchr (name, '-');
85 id = strtol (pos + 1, &end, 10);
94 asprintf (&ret, "%s-%d", name, id);
100 /* conflict handling */
102 handle_conflict (mdnsdr r, char *name, int type, void *arg)
104 IpcamServiceInfo *info = (IpcamServiceInfo *) arg;
108 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
110 if (r == info->ipinfos[i].ip_to_host)
112 /* can't do anything about a reverse lookup conflict. Just stop
114 info->ipinfos[i].ip_to_host = NULL;
115 fprintf (stderr, "zeroconf reverse lookup conflict for %s!\n", info->ipinfos[i].label);
118 if (r == info->ipinfos[i].host_to_ip)
120 info->ipinfos[i].host_to_ip = NULL;
121 info->ipinfos[i].announce_ip = 0;
125 if (info->servicename == NULL)
127 newname = increment_name (info->hostname);
131 newname = increment_name (info->servicename);
132 free (info->servicename);
135 info->servicename = newname;
137 if (r == info->srv_to_host)
138 info->srv_to_host = NULL;
139 if (r == info->txt_for_srv)
140 info->txt_for_srv = NULL;
142 fprintf (stderr, "conflicting name \"%s\". trying %s\n",
143 name, info->servicename);
145 info->state = MDNSD_PROBE;
146 write (signal_pipe[1], " ", 1);
150 /* quit and updates */
151 void sighandler (int sig)
155 ipcam_info.state = MDNSD_SHUTDOWN;
158 write (signal_pipe[1], " ", 1);
162 /* create multicast 224.0.0.251:5353 socket */
166 int s, flag = 1, ittl = 255;
167 struct sockaddr_in in;
171 bzero (&in, sizeof (in));
172 in.sin_family = AF_INET;
173 in.sin_port = htons (5353);
174 in.sin_addr.s_addr = 0;
176 if ((s = socket (AF_INET,SOCK_DGRAM,0)) < 0)
179 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &flag, sizeof (flag));
180 if (bind (s, (struct sockaddr*) &in, sizeof (in)))
186 mc.imr_multiaddr.s_addr = inet_addr ("224.0.0.251");
187 mc.imr_interface.s_addr = htonl (INADDR_ANY);
188 setsockopt (s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mc, sizeof (mc));
189 setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof (ttl));
190 setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL, &ittl, sizeof (ittl));
192 flag = fcntl (s, F_GETFL, 0);
194 fcntl (s, F_SETFL, flag);
200 request_ip_addresses (IpcamServiceInfo *info)
202 char revlookup[256], hostlocal[256];
207 sprintf (hostlocal, "%s.local.",
208 info->servicename ? info->servicename : info->hostname);
210 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
212 if (info->ipinfos[i].ip)
214 ip = inet_addr (info->ipinfos[i].ip);
216 if (ip != info->ipinfos[i].announce_ip)
218 snprintf (revlookup, 256, "%ld.%ld.%ld.%ld.in-addr.arpa.",
219 (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, (ip >> 0) & 0xff);
221 if (!info->ipinfos[i].host_to_ip)
223 info->ipinfos[i].host_to_ip = mdnsd_unique (info->dnsd, hostlocal,
224 QTYPE_A, 120, handle_conflict, info);
226 mdnsd_set_raw (info->dnsd, info->ipinfos[i].host_to_ip, (unsigned char *) &ip, 4);
228 if (!info->ipinfos[i].ip_to_host)
230 info->ipinfos[i].ip_to_host = mdnsd_unique (info->dnsd, revlookup,
231 QTYPE_PTR, 120, handle_conflict, info);
233 mdnsd_set_host (info->dnsd, info->ipinfos[i].ip_to_host, hostlocal);
235 info->ipinfos[i].announce_ip = ip;
242 if (info->ipinfos[i].host_to_ip)
243 mdnsd_done (ipcam_info.dnsd, info->ipinfos[i].host_to_ip);
244 if (info->ipinfos[i].ip_to_host)
245 mdnsd_done (ipcam_info.dnsd, info->ipinfos[i].ip_to_host);
247 info->ipinfos[i].host_to_ip = NULL;
248 info->ipinfos[i].ip_to_host = NULL;
249 info->ipinfos[i].announce_ip = 0;
254 info->state = MDNSD_STARTUP;
258 request_service (IpcamServiceInfo *info, int stage)
260 unsigned char *packet, servlocal[256], hostlocal[256];
263 sprintf (servlocal, "%s._http._tcp.local.",
264 info->servicename ? info->servicename : info->hostname);
267 * Timeouts according to
268 * http://files.multicastdns.org/draft-cheshire-dnsext-multicastdns.txt
270 * As a general rule, the recommended TTL value for Multicast DNS
271 * resource records with a host name as the resource record's name
272 * (e.g. A, AAAA, HINFO, etc.) or contained within the resource record's
273 * rdata (e.g. SRV, reverse mapping PTR record, etc.) is 120 seconds.
275 * The recommended TTL value for other Multicast DNS resource records
282 request_ip_addresses (info);
287 sprintf (hostlocal, "%s.local.",
288 info->servicename ? info->servicename : info->hostname);
290 if (!info->srv_to_host)
292 info->srv_to_host = mdnsd_unique (info->dnsd, servlocal,
293 QTYPE_SRV, 120, handle_conflict, info);
295 mdnsd_set_srv (info->dnsd, info->srv_to_host, 0, 0,
296 info->port, hostlocal);
298 if (!info->txt_for_srv)
300 info->txt_for_srv = mdnsd_unique (info->dnsd, servlocal,
301 QTYPE_TXT, 4500, handle_conflict, info);
303 packet = sd2txt (info->metadata, &len);
304 mdnsd_set_raw (info->dnsd, info->txt_for_srv, packet, len);
309 if (!info->ptr_to_srv)
311 info->ptr_to_srv = mdnsd_shared (info->dnsd, "_http._tcp.local.",
314 mdnsd_set_host (info->dnsd, info->ptr_to_srv, servlocal);
316 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
318 if (info->ipinfos[i].ip)
319 fprintf (stderr, "Announcing \"%s.local\" to %s:%d\n",
320 info->servicename ? info->servicename : info->hostname,
321 info->ipinfos[i].ip, info->port);
326 fprintf (stderr, "announce stage %d is invalid\n", stage);
332 update_port_info (IpcamServiceInfo *info, int port)
334 unsigned char hostlocal[256];
336 if (port == info->port)
341 if (!info->srv_to_host)
344 sprintf (hostlocal, "%s.local.",
345 info->servicename ? info->servicename : info->hostname);
347 fprintf (stderr, "mhttp: updating port info to port %d\n", info->port);
348 mdnsd_set_srv (info->dnsd, info->srv_to_host, 0, 0,
349 info->port, hostlocal);
353 iface_change_callback (int link_index,
359 IpcamServiceInfo *info = (IpcamServiceInfo *) user_data;
362 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
364 if (strcmp (info->ipinfos[i].label, label) != 0)
367 if (add && (!info->ipinfos[i].ip ||
368 strcmp (info->ipinfos[i].ip, ipaddr) != 0 ||
369 info->ipinfos[i].link_id != link_index))
371 if (info->ipinfos[i].ip)
372 free (info->ipinfos[i].ip);
373 info->ipinfos[i].ip = strdup (ipaddr);
374 info->ipinfos[i].link_id = link_index;
375 fprintf (stderr, "new ip address on %s: %s\n", label, ipaddr);
378 if (!add && info->ipinfos[i].ip)
380 fprintf (stderr, "lost ip address on %s\n", label);
381 free (info->ipinfos[i].ip);
382 info->ipinfos[i].ip = NULL;
383 info->ipinfos[i].link_id = -1;
387 if (add && info->state == MDNSD_STARTUP)
389 info->state = MDNSD_PROBE;
393 request_ip_addresses (info);
396 write (signal_pipe[1], " ", 1);
401 iface_link_callback (int link_index,
405 IpcamServiceInfo *info = (IpcamServiceInfo *) user_data;
407 int link_changed = 0;
409 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
410 if (link_index == info->ipinfos[i].link_id)
416 info->state = running ? MDNSD_PROBE : MDNSD_STARTUP;
417 write (signal_pipe[1], " ", 1);
421 int main(int argc, char *argv[])
424 unsigned short int port;
426 int bsize, ssize = sizeof(struct sockaddr_in);
427 unsigned char buf[MAX_PACKET_LEN];
428 struct sockaddr_in from, to;
431 unsigned long remote_ip;
434 int announce_stage = 0;
435 struct pollfd fds[4];
439 fprintf (stderr, "usage: mhttp <label1> <label2> <port> <key1>=<value1> <key2>=<value2> ...\n");
440 fprintf (stderr, " <label1>, <label2> are the labels of the network interface to be watched\n");
441 fprintf (stderr, " <port> is the port number of the service to be advertized\n");
442 fprintf (stderr, " <key>=<value> are the keys that get embedded into the TXT record.\n");
443 fprintf (stderr, "\n The port later can be changed by writing \"port:8080\" to " FIFO_PATH ".\n");
447 ipcam_info.dnsd = mdnsd_new (1, 1000);
449 ipcam_info.state = MDNSD_STARTUP;
451 gethostname (ipcam_info.hostname, HOSTNAMESIZE);
452 ipcam_info.hostname[HOSTNAMESIZE-1] = '\0';
453 if (strchr (ipcam_info.hostname, '.'))
454 strchr (ipcam_info.hostname, '.')[0] = '\0';
456 ipcam_info.servicename = NULL;
458 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
460 ipcam_info.ipinfos[i].label = argv[i+1];
461 ipcam_info.ipinfos[i].ip = NULL;
462 ipcam_info.ipinfos[i].link_id = -1;
463 ipcam_info.ipinfos[i].announce_ip = 0;
464 ipcam_info.ipinfos[i].host_to_ip = NULL;
465 ipcam_info.ipinfos[i].ip_to_host = NULL;
468 ipcam_info.port = atoi(argv[3]);
470 ipcam_info.metadata = xht_new (11);
471 for (i = 4; i < argc; i++)
473 value = index (argv[i], '=');
478 xht_set (ipcam_info.metadata, argv[i], value);
482 ipcam_info.ptr_to_srv = NULL;
483 ipcam_info.srv_to_host = NULL;
484 ipcam_info.txt_for_srv = NULL;
487 signal(SIGHUP, sighandler);
488 signal(SIGINT, sighandler);
489 signal(SIGQUIT, sighandler);
490 signal(SIGTERM, sighandler);
492 if ((s = msock()) == 0)
494 fprintf (stderr, "can't create socket: %s\n", strerror(errno));
498 if ((nlink = netwatch_open ()) < 0)
500 fprintf (stderr, "can't connect to netlink: %s\n", strerror(errno));
504 netwatch_register_callbacks (iface_change_callback,
507 netwatch_queue_inforequest (nlink);
510 if (mkfifo (FIFO_PATH, S_IRWXU) < 0)
514 fprintf (stderr, "can't create named pipe: %s\n", strerror(errno));
519 if ((fifo_fd = open (FIFO_PATH, O_RDONLY | O_NONBLOCK)) < 0)
521 fprintf (stderr, "can't open named pipe: %s\n", strerror(errno));
525 /* we need to open the fifo for writing as well (although we'll never
526 * use it for this) to avoid POLLHUP to happen when no client wants
527 * something from us. Ugh. */
529 if ((i = open (FIFO_PATH, O_WRONLY)) < 0)
531 fprintf (stderr, "can't dummy-open write end of pipe: %s\n",
538 fds[0].fd = signal_pipe[0];
539 fds[0].events = POLLIN;
542 fds[1].events = POLLIN;
545 fds[2].events = POLLIN;
548 fds[3].events = POLLIN;
551 poll (fds, 4, polltime);
553 /* only used when we wake-up from a signal */
556 char hostname[HOSTNAMESIZE];
558 read (signal_pipe[0], buf, MAX_PACKET_LEN);
560 gethostname (hostname, HOSTNAMESIZE);
561 hostname[HOSTNAMESIZE-1] = '\0';
562 if (strchr (hostname, '.'))
563 strchr (hostname, '.')[0] = '\0';
564 if (strcmp (hostname, ipcam_info.hostname))
566 /* hostname changed */
567 strcpy (ipcam_info.hostname, hostname);
568 free (ipcam_info.servicename);
569 ipcam_info.servicename = NULL;
571 ipcam_info.state = MDNSD_PROBE;
577 netwatch_dispatch (nlink);
585 ret = read (fifo_fd, message, 1023);
591 if (!strncmp ("port:", message, 5))
593 int port = atoi (message + 5);
594 if (port > 0 && port < 65536)
595 update_port_info (&ipcam_info, port);
599 fprintf (stderr, "mdnsd: got unknown fifo message: %s", message);
604 fprintf (stderr, "mdnsd: can't read from pipe: %s\n", strerror (errno));
608 switch (ipcam_info.state)
611 /* we're waiting for a netwatch based statechange */
612 /* fprintf (stderr, "in STARTUP\n"); */
617 /* fprintf (stderr, "in PROBE\n"); */
618 if (ipcam_info.ptr_to_srv)
619 mdnsd_done (ipcam_info.dnsd, ipcam_info.ptr_to_srv);
620 if (ipcam_info.srv_to_host)
621 mdnsd_done (ipcam_info.dnsd, ipcam_info.srv_to_host);
622 if (ipcam_info.txt_for_srv)
623 mdnsd_done (ipcam_info.dnsd, ipcam_info.txt_for_srv);
625 ipcam_info.ptr_to_srv = NULL;
626 ipcam_info.srv_to_host = NULL;
627 ipcam_info.txt_for_srv = NULL;
629 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
631 if (ipcam_info.ipinfos[i].host_to_ip)
632 mdnsd_done (ipcam_info.dnsd, ipcam_info.ipinfos[i].host_to_ip);
633 if (ipcam_info.ipinfos[i].ip_to_host)
634 mdnsd_done (ipcam_info.dnsd, ipcam_info.ipinfos[i].ip_to_host);
635 ipcam_info.ipinfos[i].host_to_ip = NULL;
636 ipcam_info.ipinfos[i].ip_to_host = NULL;
637 ipcam_info.ipinfos[i].announce_ip = 0;
640 ipcam_info.state = MDNSD_ANNOUNCE;
647 /* fprintf (stderr, "in ANNOUNCE\n"); */
648 if (announce_stage < 3)
650 struct timeval cur_tv;
652 gettimeofday (&cur_tv, NULL);
653 msecs = (cur_tv.tv_sec - tv.tv_sec) * 1000 + cur_tv.tv_usec / 1000 - tv.tv_usec / 1000;
655 if (tv.tv_sec == 0 || msecs > 755)
657 request_service (&ipcam_info, announce_stage);
660 cur_tv = *mdnsd_sleep (ipcam_info.dnsd);
661 polltime = cur_tv.tv_sec * 1000 + cur_tv.tv_usec / 1000;
667 cur_tv = *mdnsd_sleep (ipcam_info.dnsd);
668 polltime = cur_tv.tv_sec * 1000 + cur_tv.tv_usec / 1000;
669 if (polltime >= 756 - msecs)
670 polltime = 756 - msecs;
675 tv = *mdnsd_sleep (ipcam_info.dnsd);
676 polltime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
678 ipcam_info.state = MDNSD_RUN;
683 tv = *mdnsd_sleep (ipcam_info.dnsd);
684 polltime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
688 mdnsd_shutdown (ipcam_info.dnsd);
692 fprintf (stderr, "in default???\n");
698 while ((bsize = recvfrom (s, buf, MAX_PACKET_LEN, 0,
699 (struct sockaddr*) &from, &ssize)) > 0)
701 bzero (&msg, sizeof (struct message));
702 message_parse (&msg, buf);
703 mdnsd_in (ipcam_info.dnsd, &msg,
704 (unsigned long int) from.sin_addr.s_addr,
707 if (bsize < 0 && errno != EAGAIN)
709 fprintf (stderr, "can't read from socket: %s\n", strerror (errno));
713 while (mdnsd_out (ipcam_info.dnsd, &msg, &remote_ip, &port))
715 bzero (&to, sizeof (to));
716 to.sin_family = AF_INET;
718 to.sin_addr.s_addr = remote_ip;
719 if (sendto (s, message_packet (&msg), message_packet_len (&msg),
720 0, (struct sockaddr *) &to,
721 sizeof (struct sockaddr_in)) != message_packet_len (&msg))
723 fprintf (stderr, "can't write to socket: %s\n", strerror(errno));
727 if (ipcam_info.state == MDNSD_SHUTDOWN)
731 mdnsd_shutdown (ipcam_info.dnsd);
732 mdnsd_free (ipcam_info.dnsd);