]> git.karo-electronics.de Git - mdnsd.git/blob - mhttp.c
Add futher files to .gitignore
[mdnsd.git] / mhttp.c
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 #include <netinet/in.h>
4 #include <arpa/inet.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <string.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 #include <signal.h>
12 #include <poll.h>
13 #include <sys/time.h>
14 #include <sys/stat.h>
15
16 #include "mdnsd.h"
17 #include "dns_sd_txt.h"
18
19 #ifdef UNUSED
20 #elif defined(__GNUC__)
21 # define UNUSED(x) UNUSED_ ## x __attribute__((unused))
22 #elif defined(__LCLINT__)
23 # define UNUSED(x) /*@unused@*/ x
24 #else
25 # define UNUSED(x) x
26 #endif
27
28 #define HOSTNAMESIZE 64
29
30 typedef enum {
31     MDNSD_PROBE,
32     MDNSD_ANNOUNCE,
33     MDNSD_RUN,
34     MDNSD_SHUTDOWN
35 } TMdnsdState;
36
37 typedef struct _service_info
38 {
39     TMdnsd        *mdnsd;
40     char           hostname[HOSTNAMESIZE];
41     char          *servicename;
42
43     char          *ip;
44     int            port;
45
46     /* service-discovery records */
47     TMdnsdRecord  *host_to_ip;
48     TMdnsdRecord  *ip_to_host;
49     struct in_addr announce_ip;
50
51     SHASH          metadata;
52
53     /* service-discovery records */
54     TMdnsdRecord  *srv_to_host;
55     TMdnsdRecord  *txt_for_srv;
56
57     TMdnsdRecord  *ptr_to_srvtype;
58     TMdnsdRecord  *ptr_to_srv;
59
60     TMdnsdState    state;
61 } ServiceInfo;
62
63 static ServiceInfo service_info;
64
65 void     request_service      (ServiceInfo *info, int stage);
66 void     request_ip_addresses (ServiceInfo *info);
67
68 char *increment_name (char *name)
69 {
70     int   id = 1;
71     char *pos;
72     char *end = NULL;
73     char *ret = NULL;
74
75     pos = strrchr (name, '-');
76
77     if (pos) {
78         id = strtol (pos + 1, &end, 10);
79         if (*end == '\0') {
80             *pos = '\0';
81         } else {
82             id = 1;
83         }
84     }
85
86     id += 1;
87
88     asprintf (&ret, "%s-%d", name, id);
89
90     return ret;
91 }
92
93
94 /* conflict handling */
95 void handle_conflict (TMdnsdRecord *record, char *name, int UNUSED(type), void *arg)
96 {
97     ServiceInfo *info = (ServiceInfo *) arg;
98     char *newname;
99
100     if (record == info->ip_to_host) {
101         /* can't do anything about a reverse lookup conflict. Just stop
102          * announcing it. */
103         info->ip_to_host = NULL;
104         fprintf (stderr, "zeroconf reverse lookup conflict for %s!\n", info->ip);
105         return;
106     }
107
108     if (record == info->host_to_ip) {
109         info->host_to_ip = NULL;
110         info->announce_ip.s_addr = 0;
111     }
112
113     if (info->servicename == NULL) {
114         newname = increment_name (info->hostname);
115     } else {
116         newname = increment_name (info->servicename);
117         free (info->servicename);
118     }
119
120     info->servicename = newname;
121
122     if (record == info->srv_to_host) {
123         info->srv_to_host = NULL;
124     }
125
126     if (record == info->txt_for_srv) {
127         info->txt_for_srv = NULL;
128     }
129
130     fprintf (stderr, "conflicting name \"%s\". trying %s\n",
131              name, info->servicename);
132
133     /* The hostname was changed, so go back to probe state */
134     info->state = MDNSD_PROBE;
135 }
136
137
138 /* quit and updates */
139 void sighandler (int sig)
140 {
141     if (sig != SIGHUP) {
142         service_info.state = MDNSD_SHUTDOWN;
143     }
144 }
145
146
147 /* create multicast 224.0.0.251:5353 socket */
148 int msock ()
149 {
150     int    sock_fd;
151     int    flag = 1;
152     int    ittl = 255;
153     char   ttl = 255;
154     struct sockaddr_in in;
155     struct ip_mreq mc;
156
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;
161
162     if ((sock_fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
163         return 0;
164     }
165
166     setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*) &flag, sizeof (flag));
167     if (bind (sock_fd, (struct sockaddr*) &in, sizeof (in))) {
168         close(sock_fd);
169         return 0;
170     }
171
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));
177
178     flag =  fcntl (sock_fd, F_GETFL, 0);
179     flag |= O_NONBLOCK;
180     fcntl (sock_fd, F_SETFL, flag);
181
182     return sock_fd;
183 }
184
185 void request_ip_addresses (ServiceInfo *info)
186 {
187     char revlookup[256];
188     char hostlocal[256];
189     struct in_addr ip;
190     int  num_ips = 0;
191
192     sprintf (hostlocal, "%s.local.", info->servicename ? info->servicename : info->hostname);
193
194     if (info->ip) {
195         ip.s_addr = inet_addr (info->ip);
196
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);
201
202             if (!info->host_to_ip) {
203                 info->host_to_ip  = MdnsdAllocUnique(info->mdnsd, hostlocal,
204                                                              QTYPE_A, 120, handle_conflict, info);
205             }
206             MdnsdSetRaw (info->mdnsd, info->host_to_ip, (char *) &ip, 4);
207
208             if (!info->ip_to_host) {
209                 info->ip_to_host  = MdnsdAllocUnique(info->mdnsd, revlookup,
210                                                              QTYPE_PTR, 120, handle_conflict, info);
211             }
212             MdnsdSetHost (info->mdnsd, info->ip_to_host, hostlocal);
213
214             info->announce_ip = ip;
215         }
216
217         num_ips++;
218     } else {
219         if (info->host_to_ip) {
220             MdnsdDone (service_info.mdnsd, info->host_to_ip);
221         }
222         if (info->ip_to_host) {
223             MdnsdDone (service_info.mdnsd, info->ip_to_host);
224         }
225
226         info->host_to_ip = NULL;
227         info->ip_to_host = NULL;
228         info->announce_ip.s_addr = 0;
229     }
230 }
231
232 void request_service (ServiceInfo *info, int stage)
233 {
234     uint8_t *packet;
235     char     servlocal[256];
236     char     hostlocal[256];
237     int      len = 0;
238
239     sprintf (servlocal, "%s._http._tcp.local.",
240              info->servicename ? info->servicename : info->hostname);
241
242     /*
243      * Timeouts according to
244      *   http://files.multicastdns.org/draft-cheshire-dnsext-multicastdns.txt
245      *
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.
250              *
251      * The recommended TTL value for other Multicast DNS resource records
252      * is 75 minutes.
253      */
254
255     switch (stage)
256     {
257         case 0:
258             request_ip_addresses (info);
259
260             break;
261
262         case 1:
263             sprintf (hostlocal, "%s.local.",
264                      info->servicename ? info->servicename : info->hostname);
265
266             if (!info->srv_to_host) {
267                 info->srv_to_host = MdnsdAllocUnique (info->mdnsd, servlocal,
268                                                       QTYPE_SRV, 120, handle_conflict, info);
269             }
270
271             MdnsdSetSrv (info->mdnsd, info->srv_to_host, 0, 0, info->port, hostlocal);
272
273             if (!info->txt_for_srv) {
274                 info->txt_for_srv = MdnsdAllocUnique (info->mdnsd, servlocal,
275                                                       QTYPE_TXT, 4500, handle_conflict, info);
276             }
277
278             packet = DnsSd2Txt (info->metadata, &len);
279             MdnsdSetRaw (info->mdnsd, info->txt_for_srv, packet, len);
280             free(packet);
281             break;
282
283         case 2:
284             if (!info->ptr_to_srv) {
285                 info->ptr_to_srv  = MdnsdAllocShared (info->mdnsd, "_http._tcp.local.",
286                                                   QTYPE_PTR, 4500);
287             }
288             MdnsdSetHost (info->mdnsd, info->ptr_to_srv, servlocal);
289
290                         if (!info->ptr_to_srvtype) {
291                                 info->ptr_to_srvtype = MdnsdAllocShared (info->mdnsd, "_services._dns-sd._udp.local.",
292                                                                                                   QTYPE_PTR, 4500);
293                         }
294                         MdnsdSetHost (info->mdnsd, info->ptr_to_srvtype, "_http._tcp.local.");
295
296             if (info->ip) {
297                 fprintf (stderr, "Announcing \"%s.local\" to %s:%d\n",
298                          info->servicename ? info->servicename : info->hostname,
299                          info->ip, info->port);
300             }
301             break;
302
303         default:
304             fprintf (stderr, "announce stage %d is invalid\n", stage);
305             break;
306     }
307 }
308
309 int main(int argc, char *argv[])
310 {
311     DNSMESSAGE msg;
312     uint16_t   port;
313     struct timeval tv;
314     int        bsize;
315     int        ssize = sizeof(struct sockaddr_in);
316     uint8_t    buf[MAX_PACKET_LEN];
317     struct sockaddr_in from;
318     struct sockaddr_in to;
319     int        idx;
320     int        s;
321     struct in_addr remote_ip;
322     char      *value;
323     int        polltime = 0;
324     int        announce_stage = 0;
325     struct pollfd fds[1];
326
327     if(argc < 4)
328     {
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");
334         return -1;
335     }
336
337     service_info.mdnsd = MdnsdNew (1, 1000);
338
339     //gethostname (service_info.hostname, HOSTNAMESIZE);
340     strncpy(service_info.hostname, argv[1], HOSTNAMESIZE-1);
341     service_info.hostname[HOSTNAMESIZE-1] = '\0';
342
343     if (strchr (service_info.hostname, '.'))
344         strchr (service_info.hostname, '.')[0] = '\0';
345
346     service_info.servicename = NULL;
347
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;
352
353     service_info.port = atoi(argv[3]);
354
355     service_info.metadata = SHashInit (11);
356     for (idx = 3; idx < argc; idx++) {
357         value = index (argv[idx], '=');
358         if (value) {
359             value[0] = '\0';
360             value++;
361             SHashSet (service_info.metadata, argv[idx], value);
362         }
363     }
364
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;
369
370     signal(SIGHUP,  sighandler);
371     signal(SIGINT,  sighandler);
372     signal(SIGQUIT, sighandler);
373     signal(SIGTERM, sighandler);
374
375     if ((s = msock()) == 0)
376     {
377         fprintf (stderr, "can't create socket: %s\n", strerror(errno));
378         return -1;
379     }
380
381     request_ip_addresses (&service_info);
382
383     service_info.state = MDNSD_PROBE;
384
385     while(1) {
386         fds[0].fd      = s;
387         fds[0].events  = POLLIN;
388         fds[0].revents = 0;
389
390         poll (fds, 1, polltime);
391
392         switch (service_info.state)
393         {
394             case MDNSD_PROBE:
395
396                 if (service_info.ptr_to_srvtype) {
397                     MdnsdDone (service_info.mdnsd, service_info.ptr_to_srvtype);
398                 }
399
400                 if (service_info.ptr_to_srv) {
401                     MdnsdDone (service_info.mdnsd, service_info.ptr_to_srv);
402                 }
403
404                 if (service_info.srv_to_host) {
405                     MdnsdDone (service_info.mdnsd, service_info.srv_to_host);
406                 }
407
408                 if (service_info.txt_for_srv) {
409                     MdnsdDone (service_info.mdnsd, service_info.txt_for_srv);
410                 }
411
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;
416
417                 if (service_info.host_to_ip) {
418                     MdnsdDone (service_info.mdnsd, service_info.host_to_ip);
419                 }
420
421                 if (service_info.ip_to_host) {
422                     MdnsdDone (service_info.mdnsd, service_info.ip_to_host);
423                 }
424
425                 service_info.host_to_ip  = NULL;
426                 service_info.ip_to_host  = NULL;
427                 service_info.announce_ip.s_addr = 0;
428
429                 service_info.state = MDNSD_ANNOUNCE;
430                 announce_stage = 0;
431                 tv.tv_sec = 0;
432                 tv.tv_usec = 0;
433                 break;
434
435             case MDNSD_ANNOUNCE:
436                 if (announce_stage < 3) {
437                     struct timeval cur_tv;
438                     long msecs;
439
440                     gettimeofday (&cur_tv, NULL);
441                     msecs = (cur_tv.tv_sec - tv.tv_sec) * 1000 + cur_tv.tv_usec / 1000 - tv.tv_usec / 1000;
442
443                     if ((tv.tv_sec == 0) || (msecs > 755)) {
444                         request_service (&service_info, announce_stage);
445                         announce_stage ++;
446                         tv = cur_tv;
447                         cur_tv = *MdnsdGetMaxSleepTime (service_info.mdnsd);
448                         polltime = cur_tv.tv_sec * 1000 + cur_tv.tv_usec / 1000;
449                         if (polltime >= 756) {
450                             polltime = 756;
451                         }
452                     } else {
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;
457                         }
458                     }
459                 } else {
460                     tv = *MdnsdGetMaxSleepTime (service_info.mdnsd);
461                     polltime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
462
463                     service_info.state = MDNSD_RUN;
464                 }
465                 break;
466
467             case MDNSD_RUN:
468                 tv = *MdnsdGetMaxSleepTime (service_info.mdnsd);
469                 polltime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
470                 break;
471
472             case MDNSD_SHUTDOWN:
473                 MdnsdShutdown (service_info.mdnsd);
474                 break;
475
476             default:
477                 fprintf (stderr, "in default???\n");
478                 break;
479         }
480
481         if (fds[0].revents) {
482             while ((bsize = recvfrom (s, buf, MAX_PACKET_LEN, 0, (struct sockaddr*) &from, &ssize)) > 0)
483             {
484                 bzero (&msg, sizeof (DNSMESSAGE));
485                 DnsParseMsg (&msg, buf);
486                 MdnsdInput(service_info.mdnsd, &msg,
487                            from.sin_addr,
488                            from.sin_port);
489             }
490
491             if (bsize < 0 && errno != EAGAIN) {
492                 fprintf (stderr, "can't read from socket: %s\n", strerror (errno));
493             }
494         }
495
496         while (MdnsdOutput (service_info.mdnsd, &msg, &remote_ip, &port)) {
497             bzero (&to, sizeof (to));
498             to.sin_family = AF_INET;
499             to.sin_port = port;
500             to.sin_addr.s_addr = remote_ip.s_addr;
501
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));
504             }
505         }
506
507         if (service_info.state == MDNSD_SHUTDOWN) {
508             break;
509         }
510     }
511
512     MdnsdShutdown (service_info.mdnsd);
513     MdnsdFree (service_info.mdnsd);
514     return 0;
515 }
516