Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/networking/dnsd.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 532 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 17  Line 17 
17   * the first porting of oao' scdns to busybox also.   * the first porting of oao' scdns to busybox also.
18   */   */
19    
20  #include "busybox.h"  #include "libbb.h"
21    #include <syslog.h>
 static const char *fileconf = "/etc/dnsd.conf";  
 #define LOCK_FILE       "/var/run/dnsd.lock"  
   
 // Must match getopt32 call  
 #define OPT_daemon  (option_mask32 & 0x10)  
 #define OPT_verbose (option_mask32 & 0x20)  
22    
23  //#define DEBUG 1  //#define DEBUG 1
24  #define DEBUG 0  #define DEBUG 0
# Line 42  enum { Line 36  enum {
36     ttl(4B) + rlen(2B) + r (MAX_NAME_LEN =21B) +     ttl(4B) + rlen(2B) + r (MAX_NAME_LEN =21B) +
37     2*querystring (2 MAX_NAME_LEN= 42B), all together 90 Byte     2*querystring (2 MAX_NAME_LEN= 42B), all together 90 Byte
38  */  */
39   MAX_PACK_LEN = 512 + 1,   MAX_PACK_LEN = 512,
40    
41   DEFAULT_TTL = 30,       // increase this when not testing?   DEFAULT_TTL = 30,       // increase this when not testing?
42    
# Line 50  enum { Line 44  enum {
44   REQ_PTR = 12   REQ_PTR = 12
45  };  };
46    
 struct dns_repl { // resource record, add 0 or 1 to accepted dns_msg in resp  
  uint16_t rlen;  
  uint8_t *r; // resource  
  uint16_t flags;  
 };  
   
47  struct dns_head { // the message from client and first part of response mag  struct dns_head { // the message from client and first part of response mag
48   uint16_t id;   uint16_t id;
49   uint16_t flags;   uint16_t flags;
# Line 75  struct dns_entry { // element of known Line 63  struct dns_entry { // element of known
63   char name[MAX_HOST_LEN];   char name[MAX_HOST_LEN];
64  };  };
65    
66  static struct dns_entry *dnsentry = NULL;  static struct dns_entry *dnsentry;
67  static uint32_t ttl = DEFAULT_TTL;  static uint32_t ttl = DEFAULT_TTL;
68    
69    static const char *fileconf = "/etc/dnsd.conf";
70    
71    // Must match getopt32 call
72    #define OPT_daemon  (option_mask32 & 0x10)
73    #define OPT_verbose (option_mask32 & 0x20)
74    
75    
76  /*  /*
77   * Convert host name from C-string to dns length/string.   * Convert host name from C-string to dns length/string.
78   */   */
# Line 107  static void undot(uint8_t * rip) Line 102  static void undot(uint8_t * rip)
102  }  }
103    
104  /*  /*
  * Read one line of hostname/IP from file  
  * Returns 0 for each valid entry read, -1 at EOF  
  * Assumes all host names are lower case only  
  * Hostnames with more than one label is not handled correctly.  
  * Presently the dot is copied into name without  
  * converting to a length/string substring for that label.  
  */  
   
 static int getfileentry(FILE * fp, struct dns_entry *s)  
 {  
  unsigned int a,b,c,d;  
  char *r, *name;  
   
  restart:  
  r = xmalloc_fgets(fp);  
  if (!r)  
  return -1;  
  while (*r == ' ' || *r == '\t') {  
  r++;  
  if (!*r || *r == '#' || *r == '\n')  
  goto restart; /* skipping empty/blank and commented lines  */  
  }  
  name = r;  
  while (*r != ' ' && *r != '\t')  
  r++;  
  *r++ = 0;  
  if (sscanf(r, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)  
  goto restart; /* skipping wrong lines */  
   
  sprintf(s->ip, "%u.%u.%u.%u", a, b, c, d);  
  sprintf(s->rip, ".%u.%u.%u.%u", d, c, b, a);  
  undot((uint8_t*)s->rip);  
  convname(s->name,(uint8_t*)name);  
   
  if (OPT_verbose)  
  fprintf(stderr, "\tname:%s, ip:%s\n", &(s->name[1]),s->ip);  
   
  return 0;  
 }  
   
 /*  
105   * Read hostname/IP records from file   * Read hostname/IP records from file
106   */   */
107  static void dnsentryinit(void)  static void dnsentryinit(void)
108  {  {
109   FILE *fp;   char *token[2];
110     parser_t *parser;
111   struct dns_entry *m, *prev;   struct dns_entry *m, *prev;
  prev = dnsentry = NULL;  
   
  fp = xfopen(fileconf, "r");  
112    
113   while (1) {   prev = dnsentry = NULL;
114   m = xmalloc(sizeof(struct dns_entry));   parser = config_open(fileconf);
115     while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
116     unsigned a, b, c, d;
117     /*
118     * Assumes all host names are lower case only
119     * Hostnames with more than one label are not handled correctly.
120     * Presently the dot is copied into name without
121     * converting to a length/string substring for that label.
122     */
123    // if (!token[1] || sscanf(token[1], ".%u.%u.%u.%u"+1, &a, &b, &c, &d) != 4)
124     if (sscanf(token[1], ".%u.%u.%u.%u"+1, &a, &b, &c, &d) != 4)
125     continue;
126    
127     m = xzalloc(sizeof(*m));
128     /*m->next = NULL;*/
129     sprintf(m->ip, ".%u.%u.%u.%u"+1, a, b, c, d);
130     sprintf(m->rip, ".%u.%u.%u.%u", d, c, b, a);
131     undot((uint8_t*)m->rip);
132     convname(m->name, (uint8_t*)token[0]);
133    
134   m->next = NULL;   if (OPT_verbose)
135   if (getfileentry(fp, m))   fprintf(stderr, "\tname:%s, ip:%s\n", &(m->name[1]), m->ip);
  break;  
136    
137   if (prev == NULL)   if (prev == NULL)
138   dnsentry = m;   dnsentry = m;
# Line 171  static void dnsentryinit(void) Line 140  static void dnsentryinit(void)
140   prev->next = m;   prev->next = m;
141   prev = m;   prev = m;
142   }   }
143   fclose(fp);   config_close(parser);
144  }  }
145    
146  /*  /*
# Line 181  static void dnsentryinit(void) Line 150  static void dnsentryinit(void)
150  static int table_lookup(uint16_t type, uint8_t * as, uint8_t * qs)  static int table_lookup(uint16_t type, uint8_t * as, uint8_t * qs)
151  {  {
152   int i;   int i;
153   struct dns_entry *d=dnsentry;   struct dns_entry *d = dnsentry;
154    
155   do {   do {
156  #if DEBUG  #if DEBUG
# Line 196  static int table_lookup(uint16_t type, u Line 165  static int table_lookup(uint16_t type, u
165   for (i = 1; i <= (int)(d->name[0]); i++)   for (i = 1; i <= (int)(d->name[0]); i++)
166   if (tolower(qs[i]) != d->name[i])   if (tolower(qs[i]) != d->name[i])
167   break;   break;
168   if (i > (int)(d->name[0])) {   if (i > (int)(d->name[0]) ||
169  #if DEBUG      (d->name[0] == 1 && d->name[1] == '*')) {
  fprintf(stderr, " OK");  
 #endif  
170   strcpy((char *)as, d->ip);   strcpy((char *)as, d->ip);
171  #if DEBUG  #if DEBUG
172   fprintf(stderr, " as:%s\n", as);   fprintf(stderr, " OK as:%s\n", as);
173  #endif  #endif
174   return 0;   return 0;
175   }   }
176   } else   } else if (type == REQ_PTR) { /* search by IP-address */
177   if (type == REQ_PTR) { /* search by IP-address */   if ((d->name[0] != 1 || d->name[1] != '*') &&
178   if (!strncmp((char*)&d->rip[1], (char*)&qs[1], strlen(d->rip)-1)) {      !strncmp((char*)&d->rip[1], (char*)&qs[1], strlen(d->rip)-1)) {
179   strcpy((char *)as, d->name);   strcpy((char *)as, d->name);
180   return 0;   return 0;
181   }   }
# Line 218  static int table_lookup(uint16_t type, u Line 185  static int table_lookup(uint16_t type, u
185   return -1;   return -1;
186  }  }
187    
   
188  /*  /*
189   * Decode message and generate answer   * Decode message and generate answer
190   */   */
191  #define eret(s) do { fputs(s, stderr); return -1; } while (0)  static int process_packet(uint8_t *buf)
 static int process_packet(uint8_t * buf)  
192  {  {
193     uint8_t answstr[MAX_NAME_LEN + 1];
194   struct dns_head *head;   struct dns_head *head;
195   struct dns_prop *qprop;   struct dns_prop *qprop;
196   struct dns_repl outr;   uint8_t *from, *answb;
197   void *next, *from, *answb;   uint16_t outr_rlen;
198     uint16_t outr_flags;
  uint8_t answstr[MAX_NAME_LEN + 1];  
  int lookup_result, type, len, packet_len;  
199   uint16_t flags;   uint16_t flags;
200     int lookup_result, type, packet_len;
201     int querystr_len;
202    
203   answstr[0] = '\0';   answstr[0] = '\0';
204    
205   head = (struct dns_head *)buf;   head = (struct dns_head *)buf;
206   if (head->nquer == 0)   if (head->nquer == 0) {
207   eret("no queries\n");   bb_error_msg("no queries");
208     return -1;
209     }
210    
211   if (head->flags & 0x8000)   if (head->flags & 0x8000) {
212   eret("ignoring response packet\n");   bb_error_msg("ignoring response packet");
213     return -1;
214     }
215    
216   from = (void *)&head[1]; //  start of query string   from = (void *)&head[1]; //  start of query string
217   next = answb = from + strlen((char *)from) + 1 + sizeof(struct dns_prop);   // where to append answer block  //FIXME: strlen of untrusted data??!
218     querystr_len = strlen((char *)from) + 1 + sizeof(struct dns_prop);
219     answb = from + querystr_len;   // where to append answer block
220    
221   outr.rlen = 0; // may change later   outr_rlen = 0;
222   outr.r = NULL;   outr_flags = 0;
  outr.flags = 0;  
223    
224   qprop = (struct dns_prop *)(answb - 4);   qprop = (struct dns_prop *)(answb - 4);
225   type = ntohs(qprop->type);   type = ntohs(qprop->type);
# Line 259  static int process_packet(uint8_t * buf) Line 230  static int process_packet(uint8_t * buf)
230   }   }
231    
232   if (ntohs(qprop->class) != 1 /* class INET */ ) {   if (ntohs(qprop->class) != 1 /* class INET */ ) {
233   outr.flags = 4; /* not supported */   outr_flags = 4; /* not supported */
234   goto empty_packet;   goto empty_packet;
235   }   }
236   /* we only support standard queries */   /* we only support standard queries */
# Line 269  static int process_packet(uint8_t * buf) Line 240  static int process_packet(uint8_t * buf)
240    
241   // We have a standard query   // We have a standard query
242   bb_info_msg("%s", (char *)from);   bb_info_msg("%s", (char *)from);
243   lookup_result = table_lookup(type, answstr, (uint8_t*)from);   lookup_result = table_lookup(type, answstr, from);
244   if (lookup_result != 0) {   if (lookup_result != 0) {
245   outr.flags = 3 | 0x0400; //name do not exist and auth   outr_flags = 3 | 0x0400; // name do not exist and auth
246   goto empty_packet;   goto empty_packet;
247   }   }
248   if (type == REQ_A) {    // return an address   if (type == REQ_A) {    // return an address
249   struct in_addr a;   struct in_addr a; // NB! its "struct { unsigned __long__ s_addr; }"
250   if (!inet_aton((char*)answstr, &a)) {//dotted dec to long conv   uint32_t v32;
251   outr.flags = 1; /* Frmt err */   if (!inet_aton((char*)answstr, &a)) { //dotted dec to long conv
252     outr_flags = 1; /* Frmt err */
253   goto empty_packet;   goto empty_packet;
254   }   }
255   memcpy(answstr, &a.s_addr, 4); // save before a disappears   v32 = a.s_addr; /* in case long != int */
256   outr.rlen = 4; // uint32_t IP   memcpy(answstr, &v32, 4);
257   }   outr_rlen = 4; // uint32_t IP
258   else   } else
259   outr.rlen = strlen((char *)answstr) + 1; // a host name   outr_rlen = strlen((char *)answstr) + 1; // a host name
260   outr.r = answstr; // 32 bit ip or a host name   outr_flags |= 0x0400; /* authority-bit */
  outr.flags |= 0x0400; /* authority-bit */  
261   // we have an answer   // we have an answer
262   head->nansw = htons(1);   head->nansw = htons(1);
263    
264   // copy query block to answer block   // copy query block to answer block
265   len = answb - from;   memcpy(answb, from, querystr_len);
266   memcpy(answb, from, len);   answb += querystr_len;
  next += len;  
267    
268   // and append answer rr   // and append answer rr
269   *(uint32_t *) next = htonl(ttl);  // FIXME: unaligned accesses??
270   next += 4;   *(uint32_t *) answb = htonl(ttl);
271   *(uint16_t *) next = htons(outr.rlen);   answb += 4;
272   next += 2;   *(uint16_t *) answb = htons(outr_rlen);
273   memcpy(next, (void *)answstr, outr.rlen);   answb += 2;
274   next += outr.rlen;   memcpy(answb, answstr, outr_rlen);
275     answb += outr_rlen;
276    
277   empty_packet:   empty_packet:
278    
279   flags = ntohs(head->flags);   flags = ntohs(head->flags);
280   // clear rcode and RA, set responsebit and our new flags   // clear rcode and RA, set responsebit and our new flags
281   flags |= (outr.flags & 0xff80) | 0x8000;   flags |= (outr_flags & 0xff80) | 0x8000;
282   head->flags = htons(flags);   head->flags = htons(flags);
283   head->nauth = head->nadd = htons(0);   head->nauth = head->nadd = 0;
284   head->nquer = htons(1);   head->nquer = htons(1);
285    
286   packet_len = next - (void *)buf;   packet_len = answb - buf;
287   return packet_len;   return packet_len;
288  }  }
289    
290  /*  /*
291   * Exit on signal   * Exit on signal
292   */   */
293  static void interrupt(int x)  static void interrupt(int sig)
294  {  {
295   unlink(LOCK_FILE);   /* unlink("/var/run/dnsd.lock"); */
296   bb_error_msg("interrupt, exiting\n");   bb_error_msg("interrupt, exiting\n");
297   exit(2);   kill_myself_with_sig(sig);
298  }  }
299    
300  int dnsd_main(int argc, char **argv)  int dnsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
301    int dnsd_main(int argc UNUSED_PARAM, char **argv)
302  {  {
303   char *listen_interface = NULL;   const char *listen_interface = "0.0.0.0";
304   char *sttl, *sport;   char *sttl, *sport;
305   len_and_sockaddr *lsa;   len_and_sockaddr *lsa, *from, *to;
306     unsigned lsa_size;
307   int udps;   int udps;
308   uint16_t port = 53;   uint16_t port = 53;
309   uint8_t buf[MAX_PACK_LEN];   /* Paranoid sizing: querystring x2 + ttl + outr_rlen + answstr */
310     /* I'd rather see process_packet() fixed instead... */
311     uint8_t buf[MAX_PACK_LEN * 2 + 4 + 2 + (MAX_NAME_LEN+1)];
312    
313   getopt32(argc, argv, "i:c:t:p:dv", &listen_interface, &fileconf, &sttl, &sport);   getopt32(argv, "i:c:t:p:dv", &listen_interface, &fileconf, &sttl, &sport);
314   //if (option_mask32 & 0x1) // -i   //if (option_mask32 & 0x1) // -i
315   //if (option_mask32 & 0x2) // -c   //if (option_mask32 & 0x2) // -c
316   if (option_mask32 & 0x4) // -t   if (option_mask32 & 0x4) // -t
317   ttl = xatou_range(sttl, 1, 0xffffffff);   ttl = xatou_range(sttl, 1, 0xffffffff);
318   if (option_mask32 & 0x8) // -p   if (option_mask32 & 0x8) // -p
319   port = xatou_range(sttl, 1, 0xffff);   port = xatou_range(sport, 1, 0xffff);
320    
321   if (OPT_verbose) {   if (OPT_verbose) {
322   bb_info_msg("listen_interface: %s", listen_interface);   bb_info_msg("listen_interface: %s", listen_interface);
# Line 350  int dnsd_main(int argc, char **argv) Line 325  int dnsd_main(int argc, char **argv)
325   }   }
326    
327   if (OPT_daemon) {   if (OPT_daemon) {
328  //FIXME: NOMMU will NOT set LOGMODE_SYSLOG!   bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
329  #ifdef BB_NOMMU   openlog(applet_name, LOG_PID, LOG_DAEMON);
  /* reexec for vfork() do continue parent */  
  vfork_daemon_rexec(1, 0, argc, argv, "-d");  
 #else  
  xdaemon(1, 0);  
 #endif  
330   logmode = LOGMODE_SYSLOG;   logmode = LOGMODE_SYSLOG;
331   }   }
332    
333   dnsentryinit();   dnsentryinit();
334    
335   signal(SIGINT, interrupt);   signal(SIGINT, interrupt);
336   signal(SIGPIPE, SIG_IGN);   bb_signals(0
337   signal(SIGHUP, SIG_IGN);   /* why? + (1 << SIGPIPE) */
338     + (1 << SIGHUP)
339  #ifdef SIGTSTP  #ifdef SIGTSTP
340   signal(SIGTSTP, SIG_IGN);   + (1 << SIGTSTP)
341  #endif  #endif
342  #ifdef SIGURG  #ifdef SIGURG
343   signal(SIGURG, SIG_IGN);   + (1 << SIGURG)
344  #endif  #endif
345     , SIG_IGN);
346    
347     lsa = xdotted2sockaddr(listen_interface, port);
348     udps = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
349     xbind(udps, &lsa->u.sa, lsa->len);
350     socket_want_pktinfo(udps); /* needed for recv_from_to to work */
351     lsa_size = LSA_LEN_SIZE + lsa->len;
352     from = xzalloc(lsa_size);
353     to = xzalloc(lsa_size);
354    
  lsa = host2sockaddr(listen_interface, port);  
  udps = xsocket(lsa->sa.sa_family, SOCK_DGRAM, 0);  
  xbind(udps, &lsa->sa, lsa->len);  
  // xlisten(udps, 50); - ?!! DGRAM sockets are never listened on I think?  
355   bb_info_msg("Accepting UDP packets on %s",   bb_info_msg("Accepting UDP packets on %s",
356   xmalloc_sockaddr2dotted(&lsa->sa, lsa->len));   xmalloc_sockaddr2dotted(&lsa->u.sa));
357    
358   while (1) {   while (1) {
  fd_set fdset;  
359   int r;   int r;
360     /* Try to get *DEST* address (to which of our addresses
361   FD_ZERO(&fdset);   * this query was directed), and reply from the same address.
362   FD_SET(udps, &fdset);   * Or else we can exhibit usual UDP ugliness:
363   // Block until a message arrives   * [ip1.multihomed.ip2] <=  query to ip1  <= peer
364  // FIXME: Fantastic. select'ing on just one fd??   * [ip1.multihomed.ip2] => reply from ip2 => peer (confused) */
365  // Why no just block on it doing recvfrom() ?   memcpy(to, lsa, lsa_size);
366   r = select(udps + 1, &fdset, NULL, NULL, NULL);   r = recv_from_to(udps, buf, MAX_PACK_LEN + 1, 0, &from->u.sa, &to->u.sa, lsa->len);
367   if (r < 0)   if (r < 12 || r > MAX_PACK_LEN) {
368   bb_perror_msg_and_die("select error");   bb_error_msg("invalid packet size");
369   if (r == 0)   continue;
  bb_perror_msg_and_die("select spurious return");  
   
  /* Can this test ever be false? - yes */  
  if (FD_ISSET(udps, &fdset)) {  
  socklen_t fromlen = lsa->len;  
 // FIXME: need to get *DEST* address (to which of our addresses  
 // this query was directed), and reply from the same address.  
 // Or else we can exhibit usual UDP ugliness:  
 // [ip1.multihomed.ip2] <=  query to ip1  <= peer  
 // [ip1.multihomed.ip2] => reply from ip2 => peer (confused)  
  r = recvfrom(udps, buf, sizeof(buf), 0, &lsa->sa, &fromlen);  
  if (OPT_verbose)  
  bb_info_msg("Got UDP packet");  
   
  if (r < 12 || r > 512) {  
  bb_error_msg("invalid packet size");  
  continue;  
  }  
  if (r <= 0)  
  continue;  
  r = process_packet(buf);  
  if (r <= 0)  
  continue;  
  sendto(udps, buf, r, 0, &lsa->sa, fromlen);  
370   }   }
371     if (OPT_verbose)
372     bb_info_msg("Got UDP packet");
373     buf[r] = '\0'; /* paranoia */
374     r = process_packet(buf);
375     if (r <= 0)
376     continue;
377     send_to_from(udps, buf, r, 0, &from->u.sa, &to->u.sa, lsa->len);
378   }   }
379     return 0;
380  }  }

Legend:
Removed from v.532  
changed lines
  Added in v.816