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 */
45 typedef struct _ipcam_service_info
48 char hostname[HOSTNAMESIZE];
53 IpcamIPInfo ipinfos[MAX_ANNOUNCE_IP];
57 /* service-discovery records */
66 static IpcamServiceInfo ipcam_info;
67 static int signal_pipe[2];
70 void request_service (IpcamServiceInfo *info, int stage);
73 increment_name (char *name)
76 char *pos, *end = NULL;
79 pos = strrchr (name, '-');
83 id = strtol (pos + 1, &end, 10);
92 asprintf (&ret, "%s-%d", name, id);
98 /* conflict handling */
100 handle_conflict (mdnsdr r, char *name, int type, void *arg)
102 IpcamServiceInfo *info = (IpcamServiceInfo *) arg;
106 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
108 if (r == info->ipinfos[i].ip_to_host)
110 /* can't do anything about a reverse lookup conflict. Just stop
112 info->ipinfos[i].ip_to_host = NULL;
113 fprintf (stderr, "zeroconf reverse lookup conflict for %s!\n", info->ipinfos[i].label);
116 if (r == info->ipinfos[i].host_to_ip)
118 info->ipinfos[i].host_to_ip = NULL;
122 if (info->servicename == NULL)
124 newname = increment_name (info->hostname);
128 newname = increment_name (info->servicename);
129 free (info->servicename);
132 info->servicename = newname;
134 if (r == info->srv_to_host)
135 info->srv_to_host = NULL;
136 if (r == info->txt_for_srv)
137 info->txt_for_srv = NULL;
139 fprintf (stderr, "conflicting name \"%s\". trying %s\n",
140 name, info->servicename);
142 info->state = MDNSD_PROBE;
143 write (signal_pipe[1], " ", 1);
147 /* quit and updates */
148 void sighandler (int sig)
152 ipcam_info.state = MDNSD_SHUTDOWN;
155 write (signal_pipe[1], " ", 1);
159 /* create multicast 224.0.0.251:5353 socket */
163 int s, flag = 1, ittl = 255;
164 struct sockaddr_in in;
168 bzero (&in, sizeof (in));
169 in.sin_family = AF_INET;
170 in.sin_port = htons (5353);
171 in.sin_addr.s_addr = 0;
173 if ((s = socket (AF_INET,SOCK_DGRAM,0)) < 0)
176 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &flag, sizeof (flag));
177 if (bind (s, (struct sockaddr*) &in, sizeof (in)))
183 mc.imr_multiaddr.s_addr = inet_addr ("224.0.0.251");
184 mc.imr_interface.s_addr = htonl (INADDR_ANY);
185 setsockopt (s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mc, sizeof (mc));
186 setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof (ttl));
187 setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL, &ittl, sizeof (ittl));
189 flag = fcntl (s, F_GETFL, 0);
191 fcntl (s, F_SETFL, flag);
197 request_ip_addresses (IpcamServiceInfo *info, char *hostlocal)
203 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
205 if (info->ipinfos[i].ip)
207 ip = inet_addr (info->ipinfos[i].ip);
208 snprintf (revlookup, 256, "%ld.%ld.%ld.%ld.in-addr.arpa.",
209 (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, (ip >> 0) & 0xff);
211 if (!info->ipinfos[i].host_to_ip)
213 info->ipinfos[i].host_to_ip = mdnsd_unique (info->dnsd, hostlocal,
214 QTYPE_A, 120, handle_conflict, info);
216 mdnsd_set_raw (info->dnsd, info->ipinfos[i].host_to_ip, (unsigned char *) &ip, 4);
218 if (!info->ipinfos[i].ip_to_host)
220 info->ipinfos[i].ip_to_host = mdnsd_unique (info->dnsd, revlookup,
221 QTYPE_PTR, 120, handle_conflict, info);
223 mdnsd_set_host (info->dnsd, info->ipinfos[i].ip_to_host, hostlocal);
229 request_service (IpcamServiceInfo *info, int stage)
231 unsigned char *packet, servlocal[256], hostlocal[256];
234 sprintf (servlocal, "%s._http._tcp.local.",
235 info->servicename ? info->servicename : info->hostname);
236 sprintf (hostlocal, "%s.local.",
237 info->servicename ? info->servicename : info->hostname);
240 * Timeouts according to
241 * http://files.multicastdns.org/draft-cheshire-dnsext-multicastdns.txt
243 * As a general rule, the recommended TTL value for Multicast DNS
244 * resource records with a host name as the resource record's name
245 * (e.g. A, AAAA, HINFO, etc.) or contained within the resource record's
246 * rdata (e.g. SRV, reverse mapping PTR record, etc.) is 120 seconds.
248 * The recommended TTL value for other Multicast DNS resource records
255 request_ip_addresses (info, hostlocal);
260 if (!info->srv_to_host)
262 info->srv_to_host = mdnsd_unique (info->dnsd, servlocal,
263 QTYPE_SRV, 120, handle_conflict, info);
265 mdnsd_set_srv (info->dnsd, info->srv_to_host, 0, 0,
266 info->port, hostlocal);
268 if (!info->txt_for_srv)
270 info->txt_for_srv = mdnsd_unique (info->dnsd, servlocal,
271 QTYPE_TXT, 4500, handle_conflict, info);
273 packet = sd2txt (info->metadata, &len);
274 mdnsd_set_raw (info->dnsd, info->txt_for_srv, packet, len);
279 if (!info->ptr_to_srv)
281 info->ptr_to_srv = mdnsd_shared (info->dnsd, "_http._tcp.local.",
284 mdnsd_set_host (info->dnsd, info->ptr_to_srv, servlocal);
286 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
288 if (info->ipinfos[i].ip)
289 fprintf (stderr, "Announcing .local site named '%s' to %s:%d (%s)\n", hostlocal,
290 info->ipinfos[i].ip, info->port, servlocal);
295 fprintf (stderr, "announce stage %d is invalid\n", stage);
301 update_port_info (IpcamServiceInfo *info, int port)
303 unsigned char hostlocal[256];
305 if (port == info->port)
310 if (!info->srv_to_host)
313 sprintf (hostlocal, "%s.local.",
314 info->servicename ? info->servicename : info->hostname);
316 fprintf (stderr, "mhttp: updating port info to port %d\n", info->port);
317 mdnsd_set_srv (info->dnsd, info->srv_to_host, 0, 0,
318 info->port, hostlocal);
322 iface_change_callback (int link_index,
328 IpcamServiceInfo *info = (IpcamServiceInfo *) user_data;
331 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
333 if (strcmp (info->ipinfos[i].label, label) != 0)
336 if (add && (!info->ipinfos[i].ip ||
337 strcmp (info->ipinfos[i].ip, ipaddr) != 0 ||
338 info->ipinfos[i].link_id != link_index))
340 if (info->ipinfos[i].ip)
341 free (info->ipinfos[i].ip);
342 info->ipinfos[i].ip = strdup (ipaddr);
343 info->ipinfos[i].link_id = link_index;
346 if (!add && info->ipinfos[i].ip)
348 free (info->ipinfos[i].ip);
349 info->ipinfos[i].ip = NULL;
350 info->ipinfos[i].link_id = -1;
354 info->state = MDNSD_PROBE;
355 write (signal_pipe[1], " ", 1);
360 iface_link_callback (int link_index,
364 IpcamServiceInfo *info = (IpcamServiceInfo *) user_data;
366 int link_changed = 0;
368 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
369 if (link_index == info->ipinfos[i].link_id)
375 info->state = running ? MDNSD_PROBE : MDNSD_STARTUP;
376 write (signal_pipe[1], " ", 1);
380 int main(int argc, char *argv[])
383 unsigned short int port;
385 int bsize, ssize = sizeof(struct sockaddr_in);
386 unsigned char buf[MAX_PACKET_LEN];
387 struct sockaddr_in from, to;
390 unsigned long remote_ip;
393 int announce_stage = 0;
394 struct pollfd fds[4];
398 fprintf (stderr, "usage: mhttp <label1> <label2> <port> <key1>=<value1> <key2>=<value2> ...\n");
399 fprintf (stderr, " <label1>, <label2> are the labels of the network interface to be watched\n");
400 fprintf (stderr, " <port> is the port number of the service to be advertized\n");
401 fprintf (stderr, " <key>=<value> are the keys that get embedded into the TXT record.\n");
402 fprintf (stderr, "\n The port later can be changed by writing \"port:8080\" to " FIFO_PATH ".\n");
406 ipcam_info.dnsd = mdnsd_new (1, 1000);
408 ipcam_info.state = MDNSD_STARTUP;
410 gethostname (ipcam_info.hostname, HOSTNAMESIZE);
411 ipcam_info.hostname[HOSTNAMESIZE-1] = '\0';
412 if (strchr (ipcam_info.hostname, '.'))
413 strchr (ipcam_info.hostname, '.')[0] = '\0';
415 ipcam_info.servicename = NULL;
417 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
419 ipcam_info.ipinfos[i].label = argv[i+1];
420 ipcam_info.ipinfos[i].ip = NULL;
421 ipcam_info.ipinfos[i].link_id = -1;
422 ipcam_info.ipinfos[i].host_to_ip = NULL;
423 ipcam_info.ipinfos[i].ip_to_host = NULL;
426 ipcam_info.port = atoi(argv[3]);
428 ipcam_info.metadata = xht_new (11);
429 for (i = 4; i < argc; i++)
431 value = index (argv[i], '=');
436 xht_set (ipcam_info.metadata, argv[i], value);
440 ipcam_info.ptr_to_srv = NULL;
441 ipcam_info.srv_to_host = NULL;
442 ipcam_info.txt_for_srv = NULL;
445 signal(SIGHUP, sighandler);
446 signal(SIGINT, sighandler);
447 signal(SIGQUIT, sighandler);
448 signal(SIGTERM, sighandler);
450 if ((s = msock()) == 0)
452 fprintf (stderr, "can't create socket: %s\n", strerror(errno));
456 if ((nlink = netwatch_open ()) < 0)
458 fprintf (stderr, "can't connect to netlink: %s\n", strerror(errno));
462 netwatch_register_callbacks (iface_change_callback,
465 netwatch_queue_inforequest (nlink);
468 if (mkfifo (FIFO_PATH, S_IRWXU) < 0)
472 fprintf (stderr, "can't create named pipe: %s\n", strerror(errno));
477 if ((fifo_fd = open (FIFO_PATH, O_RDONLY | O_NONBLOCK)) < 0)
479 fprintf (stderr, "can't open named pipe: %s\n", strerror(errno));
483 /* we need to open the fifo for writing as well (although we'll never
484 * use it for this) to avoid POLLHUP to happen when no client wants
485 * something from us. Ugh. */
487 if ((i = open (FIFO_PATH, O_WRONLY)) < 0)
489 fprintf (stderr, "can't dummy-open write end of pipe: %s\n",
496 fds[0].fd = signal_pipe[0];
497 fds[0].events = POLLIN;
500 fds[1].events = POLLIN;
503 fds[2].events = POLLIN;
506 fds[3].events = POLLIN;
509 poll (fds, 4, polltime);
511 /* only used when we wake-up from a signal */
514 char hostname[HOSTNAMESIZE];
516 read (signal_pipe[0], buf, MAX_PACKET_LEN);
518 gethostname (hostname, HOSTNAMESIZE);
519 hostname[HOSTNAMESIZE-1] = '\0';
520 if (strchr (hostname, '.'))
521 strchr (hostname, '.')[0] = '\0';
522 if (strcmp (hostname, ipcam_info.hostname))
524 /* hostname changed */
525 strcpy (ipcam_info.hostname, hostname);
526 free (ipcam_info.servicename);
527 ipcam_info.servicename = NULL;
529 ipcam_info.state = MDNSD_PROBE;
535 netwatch_dispatch (nlink);
543 ret = read (fifo_fd, message, 1023);
549 if (!strncmp ("port:", message, 5))
551 int port = atoi (message + 5);
552 if (port > 0 && port < 65536)
553 update_port_info (&ipcam_info, port);
557 fprintf (stderr, "mdnsd: got unknown fifo message: %s", message);
562 fprintf (stderr, "mdnsd: can't read from pipe: %s\n", strerror (errno));
566 switch (ipcam_info.state)
569 /* we're waiting for a netwatch based statechange */
570 /* fprintf (stderr, "in STARTUP\n"); */
575 /* fprintf (stderr, "in PROBE\n"); */
576 if (ipcam_info.ptr_to_srv)
577 mdnsd_done (ipcam_info.dnsd, ipcam_info.ptr_to_srv);
578 if (ipcam_info.srv_to_host)
579 mdnsd_done (ipcam_info.dnsd, ipcam_info.srv_to_host);
580 if (ipcam_info.txt_for_srv)
581 mdnsd_done (ipcam_info.dnsd, ipcam_info.txt_for_srv);
583 ipcam_info.ptr_to_srv = NULL;
584 ipcam_info.srv_to_host = NULL;
585 ipcam_info.txt_for_srv = NULL;
587 for (i = 0; i < MAX_ANNOUNCE_IP; i++)
589 if (ipcam_info.ipinfos[i].host_to_ip)
590 mdnsd_done (ipcam_info.dnsd, ipcam_info.ipinfos[i].host_to_ip);
591 if (ipcam_info.ipinfos[i].ip_to_host)
592 mdnsd_done (ipcam_info.dnsd, ipcam_info.ipinfos[i].ip_to_host);
593 ipcam_info.ipinfos[i].host_to_ip = NULL;
594 ipcam_info.ipinfos[i].ip_to_host = NULL;
597 ipcam_info.state = MDNSD_ANNOUNCE;
604 /* fprintf (stderr, "in ANNOUNCE\n"); */
605 if (announce_stage < 3)
607 struct timeval cur_tv;
609 gettimeofday (&cur_tv, NULL);
610 msecs = (cur_tv.tv_sec - tv.tv_sec) * 1000 + cur_tv.tv_usec / 1000 - tv.tv_usec / 1000;
612 if (tv.tv_sec == 0 || msecs > 755)
614 request_service (&ipcam_info, announce_stage);
617 cur_tv = *mdnsd_sleep (ipcam_info.dnsd);
618 polltime = cur_tv.tv_sec * 1000 + cur_tv.tv_usec / 1000;
624 cur_tv = *mdnsd_sleep (ipcam_info.dnsd);
625 polltime = cur_tv.tv_sec * 1000 + cur_tv.tv_usec / 1000;
626 if (polltime >= 756 - msecs)
627 polltime = 756 - msecs;
632 tv = *mdnsd_sleep (ipcam_info.dnsd);
633 polltime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
635 ipcam_info.state = MDNSD_RUN;
640 tv = *mdnsd_sleep (ipcam_info.dnsd);
641 polltime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
645 mdnsd_shutdown (ipcam_info.dnsd);
649 fprintf (stderr, "in default???\n");
655 while ((bsize = recvfrom (s, buf, MAX_PACKET_LEN, 0,
656 (struct sockaddr*) &from, &ssize)) > 0)
658 bzero (&msg, sizeof (struct message));
659 message_parse (&msg, buf);
660 mdnsd_in (ipcam_info.dnsd, &msg,
661 (unsigned long int) from.sin_addr.s_addr,
664 if (bsize < 0 && errno != EAGAIN)
666 fprintf (stderr, "can't read from socket: %s\n", strerror (errno));
670 while (mdnsd_out (ipcam_info.dnsd, &msg, &remote_ip, &port))
672 bzero (&to, sizeof (to));
673 to.sin_family = AF_INET;
675 to.sin_addr.s_addr = remote_ip;
676 if (sendto (s, message_packet (&msg), message_packet_len (&msg),
677 0, (struct sockaddr *) &to,
678 sizeof (struct sockaddr_in)) != message_packet_len (&msg))
680 fprintf (stderr, "can't write to socket: %s\n", strerror(errno));
684 if (ipcam_info.state == MDNSD_SHUTDOWN)
688 mdnsd_shutdown (ipcam_info.dnsd);
689 mdnsd_free (ipcam_info.dnsd);