File indexing completed on 2024-05-19 04:06:36

0001 /*
0002  * Copyright (C) 2005-2008  Justin Karneges
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the
0006  * "Software"), to deal in the Software without restriction, including
0007  * without limitation the rights to use, copy, modify, merge, publish,
0008  * distribute, sublicense, and/or sell copies of the Software, and to
0009  * permit persons to whom the Software is furnished to do so, subject to
0010  * the following conditions:
0011  *
0012  * The above copyright notice and this permission notice shall be included
0013  * in all copies or substantial portions of the Software.
0014  *
0015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
0016  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0017  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
0018  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
0019  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
0020  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
0021  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0022  */
0023 
0024 #include "jdns_p.h"
0025 
0026 #include <time.h>
0027 #ifdef WIN32
0028 # include <winsock2.h>
0029 #endif
0030 
0031 #include "jdns_packet.h"
0032 #include "jdns_mdnsd.h"
0033 
0034 #define JDNS_UDP_UNI_OUT_MAX  512
0035 #define JDNS_UDP_UNI_IN_MAX   16384
0036 #define JDNS_UDP_MUL_OUT_MAX  9000
0037 #define JDNS_UDP_MUL_IN_MAX   16384
0038 
0039 /* cache no more than 7 days */
0040 #define JDNS_TTL_MAX          (86400 * 7)
0041 #define JDNS_CACHE_MAX        16384
0042 #define JDNS_CNAME_MAX        16
0043 #define JDNS_QUERY_MAX        4096
0044 
0045 /*---------------------------------------------------------------------------- */
0046 /* util */
0047 /*---------------------------------------------------------------------------- */
0048 
0049 /* declare this here, but implement it later after we define jdns_session_t */
0050 static void _debug_line(jdns_session_t *s, const char *format, ...);
0051 
0052 static unsigned char _hex_nibble(unsigned char c)
0053 {
0054     if(c <= 9)
0055         return '0' + c;
0056     else if(c <= 15)
0057         return 'a' + (c - 10);
0058     else
0059         return '?';
0060 }
0061 
0062 static void _hex_byte(unsigned char c, unsigned char *dest)
0063 {
0064     dest[0] = _hex_nibble((unsigned char)(c >> 4));
0065     dest[1] = _hex_nibble((unsigned char)(c & 0x0f));
0066 }
0067 
0068 static jdns_string_t *_make_printable(const unsigned char *str, int size)
0069 {
0070     unsigned char *buf;
0071     int n, i;
0072     jdns_string_t *out;
0073 
0074     if(size == 0)
0075     {
0076         out = jdns_string_new();
0077         jdns_string_set_cstr(out, "");
0078         return out;
0079     }
0080 
0081     /* make room for the largest possible result */
0082     buf = (unsigned char *)malloc(size * 4);
0083     i = 0;
0084     for(n = 0; n < size; ++n)
0085     {
0086         unsigned char c = str[n];
0087         if(c == '\\')
0088         {
0089             buf[i++] = '\\';
0090             buf[i++] = '\\';
0091         }
0092         else if(c >= 0x20 && c < 0x7f)
0093         {
0094             buf[i++] = c;
0095         }
0096         else
0097         {
0098             buf[i++] = '\\';
0099             buf[i++] = 'x';
0100             _hex_byte(c, buf + i);
0101             i += 2;
0102         }
0103     }
0104 
0105     out = jdns_string_new();
0106     jdns_string_set(out, buf, i);
0107     free(buf);
0108     return out;
0109 }
0110 
0111 static jdns_string_t *_make_printable_str(const jdns_string_t *str)
0112 {
0113     return _make_printable(str->data, str->size);
0114 }
0115 
0116 static jdns_string_t *_make_printable_cstr(const char *str)
0117 {
0118     return _make_printable((const unsigned char *)str, strlen(str));
0119 }
0120 
0121 static unsigned char *_fix_input(const unsigned char *in)
0122 {
0123     unsigned char *out;
0124     int len;
0125 
0126     /* truncate */
0127     len = _ustrlen(in);
0128     if(len > 254)
0129         len = 254;
0130 
0131     /* add a dot to the end if needed */
0132     if(in[len - 1] != '.' && len < 254)
0133     {
0134         out = (unsigned char *)malloc(len + 2); /* a dot and a zero */
0135         memcpy(out, in, len);
0136         out[len] = '.';
0137         out[len+1] = 0;
0138         ++len;
0139     }
0140     else
0141     {
0142         out = (unsigned char *)malloc(len + 1); /* a zero */
0143         memcpy(out, in, len);
0144         out[len] = 0;
0145     }
0146 
0147     return out;
0148 }
0149 
0150 static const char *_qtype2str(int qtype)
0151 {
0152     const char *str;
0153     switch(qtype)
0154     {
0155         case JDNS_RTYPE_A:     str = "A";     break;
0156         case JDNS_RTYPE_AAAA:  str = "AAAA";  break;
0157         case JDNS_RTYPE_MX:    str = "MX";    break;
0158         case JDNS_RTYPE_SRV:   str = "SRV";   break;
0159         case JDNS_RTYPE_CNAME: str = "CNAME"; break;
0160         case JDNS_RTYPE_PTR:   str = "PTR";   break;
0161         case JDNS_RTYPE_TXT:   str = "TXT";   break;
0162         case JDNS_RTYPE_HINFO: str = "HINFO"; break;
0163         case JDNS_RTYPE_NS:    str = "NS";    break;
0164         case JDNS_RTYPE_ANY:   str = "ANY";   break;
0165         default:               str = "";      break;
0166     }
0167     return str;
0168 }
0169 
0170 static jdns_response_t *_packet2response(const jdns_packet_t *packet, const unsigned char *qname, int qtype, int classmask)
0171 {
0172     int n;
0173     jdns_response_t *r;
0174 
0175     r = jdns_response_new();
0176     for(n = 0; n < packet->answerRecords->count; ++n)
0177     {
0178         jdns_packet_resource_t *res = (jdns_packet_resource_t *)packet->answerRecords->item[n];
0179         jdns_rr_t *rr;
0180         int put_in_answer;
0181         if((res->qclass & classmask) != 0x0001)
0182             continue;
0183         rr = jdns_rr_from_resource(res, packet);
0184         if(!rr)
0185             continue;
0186         /* if qname is set, restrict answers to those that match */
0187         /*  the question */
0188         put_in_answer = 1;
0189         if(qname)
0190         {
0191             /* name must match. type must either match or be CNAME, */
0192             /*   unless the query was for any type */
0193             if((qtype != JDNS_RTYPE_ANY && res->qtype != qtype && res->qtype != JDNS_RTYPE_CNAME) || !jdns_domain_cmp(res->qname->data, qname))
0194             {
0195                 /* put unusable records in additional section */
0196                 put_in_answer = 0;
0197             }
0198         }
0199         if(put_in_answer)
0200             jdns_response_append_answer(r, rr);
0201         else
0202             jdns_response_append_additional(r, rr);
0203         jdns_rr_delete(rr);
0204     }
0205     for(n = 0; n < packet->authorityRecords->count; ++n)
0206     {
0207         jdns_packet_resource_t *res = (jdns_packet_resource_t *)packet->authorityRecords->item[n];
0208         jdns_rr_t *rr;
0209         if((res->qclass & classmask) != 0x0001)
0210             continue;
0211         rr = jdns_rr_from_resource(res, packet);
0212         if(!rr)
0213             continue;
0214         jdns_response_append_authority(r, rr);
0215         jdns_rr_delete(rr);
0216     }
0217     for(n = 0; n < packet->additionalRecords->count; ++n)
0218     {
0219         jdns_packet_resource_t *res = (jdns_packet_resource_t *)packet->additionalRecords->item[n];
0220         jdns_rr_t *rr;
0221         if((res->qclass & classmask) != 0x0001)
0222             continue;
0223         rr = jdns_rr_from_resource(res, packet);
0224         if(!rr)
0225             continue;
0226         jdns_response_append_additional(r, rr);
0227         jdns_rr_delete(rr);
0228     }
0229     return r;
0230 }
0231 
0232 /* size must be 1 to 16 */
0233 static void _print_hexdump_line(jdns_session_t *s, const unsigned char *buf, int size)
0234 {
0235     char line[67]; /* 3 * 16 + 2 + 16 + zero byte */
0236     int n;
0237 
0238     memset(line, ' ', 66);
0239     line[66] = 0;
0240     if(size > 16)
0241         size = 16;
0242     for(n = 0; n < size; ++n)
0243     {
0244         unsigned char c = buf[n];
0245         _hex_byte(c, ((unsigned char *)line) + n * 3);
0246         line[n * 3 + 2] = ' ';
0247         if(c >= 0x20 && c < 0x7f)
0248             line[50 + n] = c;
0249         else
0250             line[50 + n] = '.';
0251     }
0252     _debug_line(s, "  %s", line);
0253 }
0254 
0255 static void _print_hexdump(jdns_session_t *s, const unsigned char *buf, int size)
0256 {
0257     int n;
0258     int lines;
0259     int at, len;
0260 
0261     lines = size / 16;
0262     if(size % 16 != 0)
0263         ++lines;
0264     for(n = 0; n < lines; ++n)
0265     {
0266         at = n * 16;
0267         if(at + 16 <= size)
0268             len = 16;
0269         else
0270             len = size - at;
0271         _print_hexdump_line(s, buf + at, len);
0272     }
0273 }
0274 
0275 static void _print_packet_resources(jdns_session_t *s, const jdns_list_t *reslist)
0276 {
0277     int n;
0278     for(n = 0; n < reslist->count; ++n)
0279     {
0280         jdns_packet_resource_t *r;
0281         jdns_string_t *str;
0282         r = (jdns_packet_resource_t *)reslist->item[n];
0283         str = _make_printable_str(r->qname);
0284         _debug_line(s, "    %04x/%04x [%s] ttl=%ld size=%d", r->qclass, r->qtype, str->data, r->ttl, r->rdlength);
0285         jdns_string_delete(str);
0286     }
0287 }
0288 
0289 static void _print_packet(jdns_session_t *s, const jdns_packet_t *packet)
0290 {
0291     int n;
0292     _debug_line(s, "Packet:");
0293     _debug_line(s, "  id:   %d", packet->id);
0294     _debug_line(s, "  opts: qr:%d, opcode:%d, aa:%d, tc:%d, rd:%d, ra:%d, z:%d, rcode:%d",
0295         packet->opts.qr, packet->opts.opcode, packet->opts.aa, packet->opts.tc,
0296         packet->opts.rd, packet->opts.ra, packet->opts.z, packet->opts.rcode);
0297     _debug_line(s, "  qdcount=%d, ancount=%d, nscount=%d, arcount=%d",
0298         packet->qdcount, packet->ancount, packet->nscount, packet->arcount);
0299     if(packet->questions->count > 0)
0300     {
0301         _debug_line(s, "  questions: (class/type name)");
0302         for(n = 0; n < packet->questions->count; ++n)
0303         {
0304             jdns_packet_question_t *q;
0305             jdns_string_t *str;
0306             q = (jdns_packet_question_t *)packet->questions->item[n];
0307             str = _make_printable_str(q->qname);
0308             _debug_line(s, "    %04x/%04x [%s]", q->qclass, q->qtype, str->data);
0309             jdns_string_delete(str);
0310         }
0311     }
0312     if(packet->answerRecords->count > 0)
0313     {
0314         _debug_line(s, "  answerRecords: (class/type owner ttl size)");
0315         _print_packet_resources(s, packet->answerRecords);
0316     }
0317     if(packet->authorityRecords->count > 0)
0318     {
0319         _debug_line(s, "  authorityRecords: (class/type owner ttl size)");
0320         _print_packet_resources(s, packet->authorityRecords);
0321     }
0322     if(packet->additionalRecords->count > 0)
0323     {
0324         _debug_line(s, "  additionalRecords: (class/type owner ttl size)");
0325         _print_packet_resources(s, packet->additionalRecords);
0326     }
0327 }
0328 
0329 static void _print_rr(jdns_session_t *s, const jdns_rr_t *rr, const unsigned char *owner)
0330 {
0331     int n;
0332     jdns_string_t *ownerstr;
0333 
0334     ownerstr = jdns_string_new();
0335 
0336     /* not the expected owner? */
0337     if(!owner || !jdns_domain_cmp(owner, rr->owner))
0338     {
0339         unsigned char *buf;
0340         jdns_string_t *str = _make_printable_cstr((const char *)rr->owner);
0341         buf = (unsigned char *)malloc(str->size + 3); /* " [%s]" */
0342         buf[0] = ' ';
0343         buf[1] = '[';
0344         memcpy(buf + 2, str->data, str->size);
0345         buf[str->size + 2] = ']';
0346         jdns_string_set(ownerstr, buf, str->size + 3);
0347         jdns_string_delete(str);
0348         free(buf);
0349     }
0350     else
0351         jdns_string_set_cstr(ownerstr, "");
0352 
0353     switch(rr->type)
0354     {
0355         case JDNS_RTYPE_A:
0356         {
0357             _debug_line(s, "    A: [%s] (ttl=%d)%s", rr->data.address->c_str, rr->ttl, ownerstr->data);
0358             break;
0359         }
0360         case JDNS_RTYPE_AAAA:
0361         {
0362             _debug_line(s, "    AAAA: [%s] (ttl=%d)%s", rr->data.address->c_str, rr->ttl, ownerstr->data);
0363             break;
0364         }
0365         case JDNS_RTYPE_MX:
0366         {
0367             jdns_string_t *str = _make_printable_cstr((const char *)rr->data.server->name);
0368             _debug_line(s, "    MX: [%s] priority=%d (ttl=%d)%s", str->data, rr->data.server->priority, rr->ttl, ownerstr->data);
0369             jdns_string_delete(str);
0370             break;
0371         }
0372         case JDNS_RTYPE_SRV:
0373         {
0374             jdns_string_t *str = _make_printable_cstr((const char *)rr->data.server->name);
0375             _debug_line(s, "    SRV: [%s] port=%d priority=%d weight=%d (ttl=%d)%s", str->data, rr->data.server->port, rr->data.server->priority, rr->data.server->weight, rr->ttl, ownerstr->data);
0376             jdns_string_delete(str);
0377             break;
0378         }
0379         case JDNS_RTYPE_CNAME:
0380         {
0381             jdns_string_t *str = _make_printable_cstr((const char *)rr->data.name);
0382             _debug_line(s, "    CNAME: [%s] (ttl=%d)%s", str->data, rr->ttl, ownerstr->data);
0383             jdns_string_delete(str);
0384             break;
0385         }
0386         case JDNS_RTYPE_PTR:
0387         {
0388             jdns_string_t *str = _make_printable_cstr((const char *)rr->data.name);
0389             _debug_line(s, "    PTR: [%s] (ttl=%d)%s", str->data, rr->ttl, ownerstr->data);
0390             jdns_string_delete(str);
0391             break;
0392         }
0393         case JDNS_RTYPE_TXT:
0394         {
0395             _debug_line(s, "    TXT: count=%d (ttl=%d)%s", rr->data.texts->count, rr->ttl, ownerstr->data);
0396             for(n = 0; n < rr->data.texts->count; ++n)
0397             {
0398                 jdns_string_t *str, *pstr;
0399                 str = rr->data.texts->item[n];
0400                 pstr = _make_printable_str(str);
0401                 _debug_line(s, "      len=%d [%s]", str->size, pstr->data);
0402                 jdns_string_delete(pstr);
0403             }
0404             break;
0405         }
0406         case JDNS_RTYPE_HINFO:
0407         {
0408             jdns_string_t *cpu, *os;
0409             cpu = _make_printable_str(rr->data.hinfo.cpu);
0410             os = _make_printable_str(rr->data.hinfo.os);
0411             _debug_line(s, "    HINFO: [%s] [%s] (ttl=%d)%s", cpu->data, os->data, rr->ttl, ownerstr->data);
0412             jdns_string_delete(cpu);
0413             jdns_string_delete(os);
0414             break;
0415         }
0416         case JDNS_RTYPE_NS:
0417         {
0418             jdns_string_t *str = _make_printable_cstr((const char *)rr->data.name);
0419             _debug_line(s, "    NS: [%s] (ttl=%d)%s", str->data, rr->ttl, ownerstr->data);
0420             jdns_string_delete(str);
0421             break;
0422         }
0423         default:
0424         {
0425             _debug_line(s, "    Unknown (%d): %d bytes (ttl=%d)%s", rr->type, rr->rdlength, rr->ttl, ownerstr->data);
0426             break;
0427         }
0428     }
0429     jdns_string_delete(ownerstr);
0430 }
0431 
0432 static void _print_records(jdns_session_t *s, const jdns_response_t *r, const unsigned char *owner)
0433 {
0434     int n;
0435     _debug_line(s, "Records:");
0436     _debug_line(s, "  Answer Records: %d", r->answerCount);
0437     for(n = 0; n < r->answerCount; ++n)
0438         _print_rr(s, r->answerRecords[n], owner);
0439     _debug_line(s, "  Authority Records: %d", r->authorityCount);
0440     for(n = 0; n < r->authorityCount; ++n)
0441         _print_rr(s, r->authorityRecords[n], owner);
0442     _debug_line(s, "  Additional Records: %d", r->additionalCount);
0443     for(n = 0; n < r->additionalCount; ++n)
0444         _print_rr(s, r->additionalRecords[n], owner);
0445 }
0446 
0447 static int _min(int a, int b)
0448 {
0449     return (a < b) ? a : b;
0450 }
0451 
0452 /*---------------------------------------------------------------------------- */
0453 /* jdns_event */
0454 /*---------------------------------------------------------------------------- */
0455 jdns_event_t *jdns_event_new()
0456 {
0457     jdns_event_t *e = alloc_type(jdns_event_t);
0458     e->response = 0;
0459     return e;
0460 }
0461 
0462 void jdns_event_delete(jdns_event_t *e)
0463 {
0464     if(!e)
0465         return;
0466     jdns_response_delete(e->response);
0467     jdns_free(e);
0468 }
0469 
0470 /*---------------------------------------------------------------------------- */
0471 /* jdns - internal types */
0472 /*---------------------------------------------------------------------------- */
0473 typedef struct list_item
0474 {
0475     void (*dtor)(void *);
0476 } list_item_t;
0477 
0478 typedef struct list
0479 {
0480     int count;
0481     list_item_t **item;
0482 } list_t;
0483 
0484 list_t *list_new()
0485 {
0486     list_t *l = alloc_type(list_t);
0487     l->count = 0;
0488     l->item = 0;
0489     return l;
0490 }
0491 
0492 void list_delete(list_t *l)
0493 {
0494     int n;
0495     if(!l)
0496         return;
0497     for(n = 0; n < l->count; ++n)
0498         l->item[n]->dtor(l->item[n]);
0499     if(l->item)
0500         free(l->item);
0501     jdns_free(l);
0502 }
0503 
0504 void list_insert(list_t *l, void *item, int pos)
0505 {
0506     list_item_t *i = (list_item_t *)item;
0507     if(!l->item)
0508         l->item = (list_item_t **)malloc(sizeof(list_item_t *));
0509     else
0510         l->item = (list_item_t **)realloc(l->item, sizeof(list_item_t *) * (l->count + 1));
0511     if(pos != -1)
0512         memmove(l->item + pos + 1, l->item + pos, (l->count - pos) * sizeof(list_item_t *));
0513     else
0514         pos = l->count;
0515     l->item[pos] = i;
0516     ++l->count;
0517 }
0518 
0519 void list_remove(list_t *l, void *item)
0520 {
0521     int n;
0522     list_item_t *i = (list_item_t *)item;
0523     int pos = -1;
0524     for(n = 0; n < l->count; ++n)
0525     {
0526         if(l->item[n] == i)
0527         {
0528             pos = n;
0529             break;
0530         }
0531     }
0532     if(pos == -1)
0533         return;
0534 
0535     i->dtor(i);
0536     if(l->count > 1)
0537     {
0538         memmove(l->item + pos, l->item + pos + 1, (l->count - pos - 1) * sizeof(list_item_t *));
0539         --l->count;
0540     }
0541     else
0542     {
0543         free(l->item);
0544         l->item = 0;
0545         l->count = 0;
0546     }
0547 }
0548 
0549 typedef struct name_server
0550 {
0551     void (*dtor)(struct name_server *);
0552     int id;
0553     jdns_address_t *address;
0554     int port;
0555 } name_server_t;
0556 
0557 void name_server_delete(name_server_t *ns);
0558 
0559 name_server_t *name_server_new()
0560 {
0561     name_server_t *ns = alloc_type(name_server_t);
0562     ns->dtor = name_server_delete;
0563     ns->address = 0;
0564     return ns;
0565 }
0566 
0567 void name_server_delete(name_server_t *ns)
0568 {
0569     if(!ns)
0570         return;
0571     jdns_address_delete(ns->address);
0572     jdns_free(ns);
0573 }
0574 
0575 int _intarray_indexOf(int *array, int count, int val)
0576 {
0577     int n;
0578     for(n = 0; n < count; ++n)
0579     {
0580         if(array[n] == val)
0581             return n;
0582     }
0583     return -1;
0584 }
0585 
0586 int _intarray_add(int **array, int *count, int val)
0587 {
0588     int *p;
0589     if(!*array)
0590         p = (int *)malloc(sizeof(int));
0591     else
0592         p = (int *)realloc(*array, sizeof(int) * (*count + 1));
0593     if(!p)
0594         return 0;
0595     *array = p;
0596     (*array)[*count] = val;
0597     ++(*count);
0598     return 1;
0599 }
0600 
0601 void _intarray_remove(int **array, int *count, int pos)
0602 {
0603     int *p;
0604     if(*count > 1)
0605     {
0606         memmove(*array + pos, *array + pos + 1, (*count - pos - 1) * sizeof(int));
0607         --(*count);
0608         p = (int *)realloc(*array, sizeof(int) * (*count));
0609         if(p)
0610             *array = p;
0611     }
0612     else
0613     {
0614         free(*array);
0615         *array = 0;
0616         *count = 0;
0617     }
0618 }
0619 
0620 typedef struct query
0621 {
0622     void (*dtor)(struct query *);
0623 
0624     int id;
0625 
0626     /* user request ids */
0627     int req_ids_count;
0628     int *req_ids;
0629 
0630     /* packet id */
0631     int dns_id;
0632 
0633     /* what we are looking up */
0634     unsigned char *qname;
0635     int qtype;
0636 
0637     /* how many transmission attempts we have done.  note this */
0638     /*  is not actually how many packets have been sent, since */
0639     /*  it is possible for the first transmission to send many */
0640     /*  at once.  this variable lets us decide when to give up. */
0641     /*  (idea taken from qdns). */
0642     /* set to -1 to deactivate (stop sending packets) */
0643     int step;
0644 
0645     /* which nameservers we've tried (stored as a list of ids) */
0646     int servers_tried_count;
0647     int *servers_tried;
0648 
0649     /* which servers we shouldn't try again */
0650     int servers_failed_count;
0651     int *servers_failed;
0652 
0653     /* flag to indicate whether or not we've tried all available */
0654     /*  nameservers already.  this means that all future */
0655     /*  transmissions are likely repeats, and should be slowed */
0656     /*  down. */
0657     int retrying;
0658 
0659     /* holds a timeout for the next step (time_start == -1 means no timer) */
0660     int time_start;
0661     int time_next;
0662 
0663     /* whether or not to look in the cache for this query */
0664     int trycache;
0665 
0666     /* cname subquerying.  only cname_parent or cname_child may be set, */
0667     /*  never both. */
0668     int cname_chain_count;
0669     struct query *cname_parent;
0670     struct query *cname_child;
0671 
0672     /* accumulates known multicast records to prevent duplicates */
0673     jdns_response_t *mul_known;
0674 } query_t;
0675 
0676 void query_delete(query_t *q);
0677 
0678 query_t *query_new()
0679 {
0680     query_t *q = alloc_type(query_t);
0681     q->dtor = query_delete;
0682     q->req_ids_count = 0;
0683     q->req_ids = 0;
0684     q->qname = 0;
0685     q->servers_tried_count = 0;
0686     q->servers_tried = 0;
0687     q->servers_failed_count = 0;
0688     q->servers_failed = 0;
0689     q->cname_chain_count = 0;
0690     q->cname_parent = 0;
0691     q->cname_child = 0;
0692     q->mul_known = 0;
0693     return q;
0694 }
0695 
0696 void query_delete(query_t *q)
0697 {
0698     if(!q)
0699         return;
0700     if(q->req_ids)
0701         free(q->req_ids);
0702     if(q->qname)
0703         free(q->qname);
0704     if(q->servers_tried)
0705         free(q->servers_tried);
0706     if(q->servers_failed)
0707         free(q->servers_failed);
0708     jdns_response_delete(q->mul_known);
0709     jdns_free(q);
0710 }
0711 
0712 int query_have_req_id(const query_t *q, int req_id)
0713 {
0714     if(_intarray_indexOf(q->req_ids, q->req_ids_count, req_id) != -1)
0715         return 1;
0716     return 0;
0717 }
0718 
0719 void query_add_req_id(query_t *q, int req_id)
0720 {
0721     _intarray_add(&q->req_ids, &q->req_ids_count, req_id);
0722 }
0723 
0724 void query_remove_req_id(query_t *q, int req_id)
0725 {
0726     int pos;
0727 
0728     pos = _intarray_indexOf(q->req_ids, q->req_ids_count, req_id);
0729     if(pos != -1)
0730         _intarray_remove(&q->req_ids, &q->req_ids_count, pos);
0731 }
0732 
0733 int query_server_tried(const query_t *q, int ns_id)
0734 {
0735     if(_intarray_indexOf(q->servers_tried, q->servers_tried_count, ns_id) != -1)
0736         return 1;
0737     return 0;
0738 }
0739 
0740 void query_add_server_tried(query_t *q, int ns_id)
0741 {
0742     _intarray_add(&q->servers_tried, &q->servers_tried_count, ns_id);
0743 }
0744 
0745 int query_server_failed(const query_t *q, int ns_id);
0746 
0747 void query_clear_servers_tried(query_t *q)
0748 {
0749     int n;
0750 
0751     /* all failed servers must continue to be considered tried servers, so */
0752     /*   only clear tried servers that haven't failed */
0753     for(n = 0; n < q->servers_tried_count; ++n)
0754     {
0755         if(!query_server_failed(q, q->servers_tried[n]))
0756         {
0757             _intarray_remove(&q->servers_tried, &q->servers_tried_count, n);
0758             --n; /* adjust position */
0759         }
0760     }
0761 }
0762 
0763 int query_server_failed(const query_t *q, int ns_id)
0764 {
0765     if(_intarray_indexOf(q->servers_failed, q->servers_failed_count, ns_id) != -1)
0766         return 1;
0767     return 0;
0768 }
0769 
0770 void query_add_server_failed(query_t *q, int ns_id)
0771 {
0772     _intarray_add(&q->servers_failed, &q->servers_failed_count, ns_id);
0773 }
0774 
0775 void query_name_server_gone(query_t *q, int ns_id)
0776 {
0777     int pos;
0778 
0779     pos = _intarray_indexOf(q->servers_tried, q->servers_tried_count, ns_id);
0780     if(pos != -1)
0781         _intarray_remove(&q->servers_tried, &q->servers_tried_count, pos);
0782 
0783     pos = _intarray_indexOf(q->servers_failed, q->servers_failed_count, ns_id);
0784     if(pos != -1)
0785         _intarray_remove(&q->servers_failed, &q->servers_failed_count, pos);
0786 }
0787 
0788 typedef struct datagram
0789 {
0790     void (*dtor)(struct datagram *);
0791     int handle;
0792     jdns_address_t *dest_address;
0793     int dest_port;
0794     unsigned char *data;
0795     int size;
0796 
0797     /* query association */
0798     query_t *query;
0799     int query_send_type; /* 0 == normal, 1 == first step send-all */
0800 
0801     /* name server association */
0802     int ns_id;
0803 } datagram_t;
0804 
0805 void datagram_delete(datagram_t *a);
0806 
0807 datagram_t *datagram_new()
0808 {
0809     datagram_t *a = alloc_type(datagram_t);
0810     a->dtor = datagram_delete;
0811     a->dest_address = 0;
0812     a->data = 0;
0813     a->size = 0;
0814     a->query = 0;
0815     return a;
0816 }
0817 
0818 void datagram_delete(datagram_t *a)
0819 {
0820     if(!a)
0821         return;
0822     jdns_address_delete(a->dest_address);
0823     if(a->data)
0824         free(a->data);
0825     jdns_free(a);
0826 }
0827 
0828 typedef struct cache_item
0829 {
0830     void (*dtor)(struct cache_item *);
0831     unsigned char *qname;
0832     int qtype;
0833     int time_start;
0834     int ttl;
0835     jdns_rr_t *record; /* if zero, nxdomain is assumed */
0836 } cache_item_t;
0837 
0838 void cache_item_delete(cache_item_t *e);
0839 
0840 cache_item_t *cache_item_new()
0841 {
0842     cache_item_t *a = alloc_type(cache_item_t);
0843     a->dtor = cache_item_delete;
0844     a->qname = 0;
0845     a->record = 0;
0846     return a;
0847 }
0848 
0849 void cache_item_delete(cache_item_t *a)
0850 {
0851     if(!a)
0852         return;
0853     if(a->qname)
0854         free(a->qname);
0855     jdns_rr_delete(a->record);
0856     jdns_free(a);
0857 }
0858 
0859 typedef struct event
0860 {
0861     void (*dtor)(struct event *);
0862     jdns_event_t *event;
0863 } event_t;
0864 
0865 void event_delete(event_t *e);
0866 
0867 event_t *event_new()
0868 {
0869     event_t *e = alloc_type(event_t);
0870     e->dtor = event_delete;
0871     e->event = 0;
0872     return e;
0873 }
0874 
0875 void event_delete(event_t *e)
0876 {
0877     if(!e)
0878         return;
0879     jdns_event_delete(e->event);
0880     jdns_free(e);
0881 }
0882 
0883 typedef struct published_item
0884 {
0885     void (*dtor)(struct published_item *);
0886     int id;
0887     int mode;
0888     unsigned char *qname;
0889     int qtype;
0890     mdnsdr rec;
0891     jdns_rr_t *rr;
0892 } published_item_t;
0893 
0894 void published_item_delete(published_item_t *a);
0895 
0896 published_item_t *published_item_new()
0897 {
0898     published_item_t *a = alloc_type(published_item_t);
0899     a->dtor = published_item_delete;
0900     a->qname = 0;
0901     a->rec = 0;
0902     a->rr = 0;
0903     return a;
0904 }
0905 
0906 void published_item_delete(published_item_t *a)
0907 {
0908     if(!a)
0909         return;
0910     if(a->qname)
0911         free(a->qname);
0912     jdns_rr_delete(a->rr);
0913     jdns_free(a);
0914 }
0915 
0916 /*---------------------------------------------------------------------------- */
0917 /* jdns */
0918 /*---------------------------------------------------------------------------- */
0919 struct jdns_session
0920 {
0921     jdns_callbacks_t cb;
0922     int mode;
0923     int shutdown;
0924     int next_qid;
0925     int next_req_id;
0926     int last_time;
0927     int next_timer;
0928     int next_name_server_id;
0929     int handle;
0930     int handle_readable, handle_writable;
0931     int port;
0932     list_t *name_servers;
0933     list_t *queries;
0934     list_t *outgoing;
0935     list_t *events;
0936     list_t *cache;
0937 
0938     /* for blocking req_ids from reuse until user explicitly releases */
0939     int do_hold_req_ids;
0940     int held_req_ids_count;
0941     int *held_req_ids;
0942 
0943     /* mdns */
0944     mdnsd mdns;
0945     list_t *published;
0946     jdns_address_t *maddr;
0947 };
0948 
0949 jdns_session_t *jdns_session_new(jdns_callbacks_t *callbacks)
0950 {
0951     jdns_session_t *s = alloc_type(jdns_session_t);
0952     memcpy(&s->cb, callbacks, sizeof(jdns_callbacks_t));
0953     s->shutdown = 0;
0954     s->next_qid = 0;
0955     s->next_req_id = 1;
0956     s->last_time = 0;
0957     s->next_timer = 0;
0958     s->next_name_server_id = 0;
0959     s->handle = 0;
0960     s->handle_readable = 0;
0961     s->handle_writable = 1;
0962     s->port = 0;
0963     s->name_servers = list_new();
0964     s->queries = list_new();
0965     s->outgoing = list_new();
0966     s->events = list_new();
0967     s->cache = list_new();
0968 
0969     s->do_hold_req_ids = 0;
0970     s->held_req_ids_count = 0;
0971     s->held_req_ids = 0;
0972 
0973     s->mdns = 0;
0974     s->published = list_new();
0975     s->maddr = 0;
0976 
0977     return s;
0978 }
0979 
0980 void jdns_session_delete(jdns_session_t *s)
0981 {
0982     if(!s)
0983         return;
0984     if(s->handle)
0985         s->cb.udp_unbind(s, s->cb.app, s->handle);
0986     list_delete(s->name_servers);
0987     list_delete(s->queries);
0988     list_delete(s->outgoing);
0989     list_delete(s->events);
0990     list_delete(s->cache);
0991 
0992     if(s->held_req_ids)
0993         free(s->held_req_ids);
0994 
0995     if(s->mdns)
0996         mdnsd_free(s->mdns);
0997 
0998     list_delete(s->published);
0999     jdns_address_delete(s->maddr);
1000 
1001     free(s);
1002 }
1003 
1004 /* declare some internal functions */
1005 static int _callback_time_now(mdnsd d, void *arg);
1006 static int _callback_rand_int(mdnsd d, void *arg);
1007 
1008 static void _append_event(jdns_session_t *s, jdns_event_t *event);
1009 static void _append_event_and_hold_id(jdns_session_t *s, jdns_event_t *event);
1010 static void _remove_name_server_datagrams(jdns_session_t *s, int ns_id);
1011 static void _remove_query_datagrams(jdns_session_t *s, const query_t *q);
1012 
1013 static int _unicast_query(jdns_session_t *s, const unsigned char *name, int qtype);
1014 static void _unicast_cancel(jdns_session_t *s, query_t *q);
1015 static int _multicast_query(jdns_session_t *s, const unsigned char *name, int qtype);
1016 static void _multicast_cancel(jdns_session_t *s, int req_id);
1017 static int _multicast_publish(jdns_session_t *s, int mode, const jdns_rr_t *rr);
1018 static void _multicast_update_publish(jdns_session_t *s, int id, const jdns_rr_t *rr);
1019 static void _multicast_cancel_publish(jdns_session_t *s, int id);
1020 static void _multicast_flush(jdns_session_t *s);
1021 
1022 static int jdns_step_unicast(jdns_session_t *s, int now);
1023 static int jdns_step_multicast(jdns_session_t *s, int now);
1024 
1025 static void _hold_req_id(jdns_session_t *s, int req_id)
1026 {
1027     int pos;
1028 
1029     /* make sure we don't hold an id twice */
1030     pos = _intarray_indexOf(s->held_req_ids, s->held_req_ids_count, req_id);
1031     if(pos != -1)
1032         return;
1033 
1034     _intarray_add(&s->held_req_ids, &s->held_req_ids_count, req_id);
1035 }
1036 
1037 static void _unhold_req_id(jdns_session_t *s, int req_id)
1038 {
1039     int pos;
1040 
1041     pos = _intarray_indexOf(s->held_req_ids, s->held_req_ids_count, req_id);
1042     if(pos != -1)
1043         _intarray_remove(&s->held_req_ids, &s->held_req_ids_count, pos);
1044 }
1045 
1046 static void _set_hold_ids_enabled(jdns_session_t *s, int enabled)
1047 {
1048     if(enabled && !s->do_hold_req_ids)
1049     {
1050         s->do_hold_req_ids = 1;
1051     }
1052     else if(!enabled && s->do_hold_req_ids)
1053     {
1054         s->do_hold_req_ids = 0;
1055 
1056         if(s->held_req_ids)
1057             free(s->held_req_ids);
1058         s->held_req_ids = 0;
1059         s->held_req_ids_count = 0;
1060     }
1061 }
1062 
1063 static int _int_wrap(int *src, int start)
1064 {
1065     int x;
1066     x = (*src)++;
1067     if(*src < start)
1068         *src = start;
1069     return x;
1070 }
1071 
1072 /* starts at 0 */
1073 static int get_next_qid(jdns_session_t *s)
1074 {
1075     int n, id;
1076     id = -1;
1077     while(id == -1)
1078     {
1079         id = _int_wrap(&s->next_qid, 0);
1080         for(n = 0; n < s->queries->count; ++n)
1081         {
1082             if(((query_t *)s->queries->item[n])->id == id)
1083             {
1084                 id = -1;
1085                 break;
1086             }
1087         }
1088     }
1089     return id;
1090 }
1091 
1092 /* starts at 1 */
1093 static int get_next_req_id(jdns_session_t *s)
1094 {
1095     int n, k, id;
1096     id = -1;
1097     while(id == -1)
1098     {
1099         id = _int_wrap(&s->next_req_id, 1);
1100 
1101         /* no query using this? */
1102         for(n = 0; n < s->queries->count; ++n)
1103         {
1104             query_t *q = (query_t *)s->queries->item[n];
1105             for(k = 0; k < q->req_ids_count; ++k)
1106             {
1107                 if(q->req_ids[k] == id)
1108                 {
1109                     id = -1;
1110                     break;
1111                 }
1112             }
1113             if(id == -1)
1114                 break;
1115         }
1116 
1117         /* no publish using this? */
1118         for(n = 0; n < s->published->count; ++n)
1119         {
1120             if(((published_item_t *)s->published->item[n])->id == id)
1121             {
1122                 id = -1;
1123                 break;
1124             }
1125         }
1126 
1127         /* successful unicast queries or any kind of error result in */
1128         /*   events for actions that are no longer active.  we need */
1129         /*   to make sure ids for these actions are not reassigned */
1130         /*   until the user explicitly releases them */
1131         for(n = 0; n < s->held_req_ids_count; ++n)
1132         {
1133             if(s->held_req_ids[n] == id)
1134             {
1135                 id = -1;
1136                 break;
1137             }
1138         }
1139     }
1140     return id;
1141 }
1142 
1143 /* random number fitting in 16 bits */
1144 static int get_next_dns_id(jdns_session_t *s)
1145 {
1146     int n, id, active_ids;
1147     active_ids = 0;
1148     id = -1;
1149     while(id == -1)
1150     {
1151         /* use random number for dns id */
1152         id = s->cb.rand_int(s, s->cb.app) & 0xffff;
1153 
1154         for(n = 0; n < s->queries->count; ++n)
1155         {
1156             query_t *q = (query_t *)s->queries->item[n];
1157             if(q->dns_id != -1)
1158             {
1159                 ++active_ids;
1160                 if(active_ids >= JDNS_QUERY_MAX)
1161                     return -1;
1162 
1163                 if(q->dns_id == id)
1164                 {
1165                     id = -1;
1166                     break;
1167                 }
1168             }
1169         }
1170     }
1171     return id;
1172 }
1173 
1174 /* starts at 0 */
1175 static int get_next_name_server_id(jdns_session_t *s)
1176 {
1177     int n, id;
1178     id = -1;
1179     while(id == -1)
1180     {
1181         id = _int_wrap(&s->next_name_server_id, 0);
1182         for(n = 0; n < s->name_servers->count; ++n)
1183         {
1184             if(((name_server_t *)s->name_servers->item[n])->id == id)
1185             {
1186                 id = -1;
1187                 break;
1188             }
1189         }
1190     }
1191     return id;
1192 }
1193 
1194 int jdns_init_unicast(jdns_session_t *s, const jdns_address_t *addr, int port)
1195 {
1196     int ret;
1197     s->mode = 0;
1198     ret = s->cb.udp_bind(s, s->cb.app, addr, port, 0);
1199     if(ret <= 0)
1200         return 0;
1201     s->handle = ret;
1202     s->port = port;
1203     return 1;
1204 }
1205 
1206 int jdns_init_multicast(jdns_session_t *s, const jdns_address_t *addr, int port, const jdns_address_t *maddr)
1207 {
1208     int ret;
1209     s->mode = 1;
1210     ret = s->cb.udp_bind(s, s->cb.app, addr, port, maddr);
1211     if(ret <= 0)
1212         return 0;
1213     s->handle = ret;
1214     s->port = port;
1215     s->maddr = jdns_address_copy(maddr);
1216 
1217     /* class 1.  note: frame size is ignored by the jdns version of mdnsd */
1218     s->mdns = mdnsd_new(0x0001, 1000, s->port, _callback_time_now, _callback_rand_int, s);
1219     return 1;
1220 }
1221 
1222 void jdns_shutdown(jdns_session_t *s)
1223 {
1224     if(s->shutdown == 0)
1225         s->shutdown = 1; /* request shutdown */
1226 }
1227 
1228 void jdns_set_nameservers(jdns_session_t *s, const jdns_nameserverlist_t *nslist)
1229 {
1230     int n, k;
1231 
1232     /* removed? */
1233     for(k = 0; k < s->name_servers->count; ++k)
1234     {
1235         name_server_t *ns = (name_server_t *)(s->name_servers->item[k]);
1236         int found = 0;
1237         for(n = 0; n < nslist->count; ++n)
1238         {
1239             jdns_nameserver_t *i = (jdns_nameserver_t *)nslist->item[n];
1240             if(jdns_address_cmp(ns->address, i->address) && ns->port == i->port)
1241             {
1242                 found = 1;
1243                 break;
1244             }
1245         }
1246         if(!found)
1247         {
1248             int i;
1249             int ns_id;
1250 
1251             /* remove any pending packets to this nameserver */
1252             _remove_name_server_datagrams(s, ns->id);
1253 
1254             _debug_line(s, "ns [%s:%d] (id=%d) removed", ns->address->c_str, ns->port, ns->id);
1255             ns_id = ns->id;
1256             list_remove(s->name_servers, ns);
1257             --k; /* adjust position */
1258             for(i = 0; i < s->queries->count; ++i)
1259                 query_name_server_gone((query_t *)s->queries->item[i], ns_id);
1260         }
1261     }
1262 
1263     /* added? */
1264     for(n = 0; n < nslist->count; ++n)
1265     {
1266         name_server_t *ns;
1267         jdns_nameserver_t *i;
1268         int found;
1269 
1270         i = (jdns_nameserver_t *)nslist->item[n];
1271         found = 0;
1272         for(k = 0; k < s->name_servers->count; ++k)
1273         {
1274             ns = (name_server_t *)(s->name_servers->item[k]);
1275             if(jdns_address_cmp(ns->address, i->address) && ns->port == i->port)
1276             {
1277                 found = 1;
1278                 break;
1279             }
1280         }
1281         if(found)
1282         {
1283             _debug_line(s, "ns [%s:%d] (id=%d) still present", ns->address->c_str, ns->port, ns->id);
1284         }
1285         else
1286         {
1287             ns = name_server_new();
1288             ns->id = get_next_name_server_id(s);
1289             ns->address = jdns_address_copy(i->address);
1290             ns->port = i->port;
1291             list_insert(s->name_servers, ns, -1);
1292             _debug_line(s, "ns [%s:%d] (id=%d) added", ns->address->c_str, ns->port, ns->id);
1293         }
1294     }
1295 
1296     /* no nameservers? */
1297     if(nslist->count == 0)
1298     {
1299         _debug_line(s, "nameserver count is zero, invalidating any queries");
1300 
1301         /* invalidate all of the queries! */
1302         for(n = 0; n < s->queries->count; ++n)
1303         {
1304             query_t *q = (query_t *)s->queries->item[n];
1305 
1306             /* report event to any requests listening */
1307             for(k = 0; k < q->req_ids_count; ++k)
1308             {
1309                 jdns_event_t *event = jdns_event_new();
1310                 event->type = JDNS_EVENT_RESPONSE;
1311                 event->id = q->req_ids[k];
1312                 event->status = JDNS_STATUS_TIMEOUT;
1313                 _append_event_and_hold_id(s, event);
1314             }
1315 
1316             /* this line is probably redundant, but just for */
1317             /*  consistency we'll do it... */
1318             _remove_query_datagrams(s, q);
1319 
1320             list_remove(s->queries, q);
1321             --n; /* adjust position */
1322         }
1323     }
1324 }
1325 
1326 void jdns_probe(jdns_session_t *s)
1327 {
1328     if(s->mode != 1)
1329         return;
1330 
1331     _multicast_flush(s);
1332 }
1333 
1334 int jdns_query(jdns_session_t *s, const unsigned char *name, int rtype)
1335 {
1336     if(s->mode == 0)
1337         return _unicast_query(s, name, rtype);
1338     else
1339         return _multicast_query(s, name, rtype);
1340 }
1341 
1342 static void _remove_events(jdns_session_t *s, int event_type, int id)
1343 {
1344     int n;
1345     for(n = 0; n < s->events->count; ++n)
1346     {
1347         event_t *e = (event_t *)s->events->item[n];
1348         if(e->event->type == event_type && e->event->id == id)
1349         {
1350             list_remove(s->events, e);
1351             --n; /* adjust position */
1352         }
1353     }
1354 }
1355 
1356 void jdns_cancel_query(jdns_session_t *s, int id)
1357 {
1358     int n;
1359 
1360     _unhold_req_id(s, id);
1361 
1362     /* remove any events associated with the query.  this avoids any */
1363     /*   possibility that stale events from one query are mistaken to be */
1364     /*   events resulting from a later query that happened to reuse the */
1365     /*   id.  it also means we don't deliver events for cancelled queries, */
1366     /*   which can simplify application logic. */
1367     _remove_events(s, JDNS_EVENT_RESPONSE, id);
1368 
1369     /* multicast */
1370     if(s->mode == 1)
1371     {
1372         _multicast_cancel(s, id);
1373         return;
1374     }
1375 
1376     /* unicast */
1377     for(n = 0; n < s->queries->count; ++n)
1378     {
1379         query_t *q = (query_t *)s->queries->item[n];
1380         if(query_have_req_id(q, id))
1381         {
1382             query_remove_req_id(q, id);
1383 
1384             /* note: calling _unicast_cancel might remove an item */
1385             /*  from s->queries, thereby screwing up our iterator */
1386             /*  position, but that's ok because we just break */
1387             /*  anyway. */
1388 
1389             /* if no one else is depending on this request, then take action */
1390             if(q->req_ids_count == 0 && !q->cname_parent)
1391             {
1392                 /* remove a possible cname child */
1393                 if(q->cname_child && q->cname_child->req_ids_count == 0)
1394                 {
1395                     q->cname_child->cname_parent = 0;
1396                     _unicast_cancel(s, q->cname_child);
1397                     q->cname_child = 0;
1398                 }
1399 
1400                 _unicast_cancel(s, q);
1401             }
1402             break;
1403         }
1404     }
1405 }
1406 
1407 int jdns_publish(jdns_session_t *s, int mode, const jdns_rr_t *rr)
1408 {
1409     return _multicast_publish(s, mode, rr);
1410 }
1411 
1412 void jdns_update_publish(jdns_session_t *s, int id, const jdns_rr_t *rr)
1413 {
1414     _multicast_update_publish(s, id, rr);
1415 }
1416 
1417 void jdns_cancel_publish(jdns_session_t *s, int id)
1418 {
1419     _unhold_req_id(s, id);
1420 
1421     _remove_events(s, JDNS_EVENT_PUBLISH, id);
1422 
1423     _multicast_cancel_publish(s, id);
1424 }
1425 
1426 int jdns_step(jdns_session_t *s)
1427 {
1428     int now, passed;
1429     int ret;
1430 
1431     /* session is shut down */
1432     if(s->shutdown == 2)
1433         return 0;
1434 
1435     now = s->cb.time_now(s, s->cb.app);
1436     passed = now - s->last_time;
1437 
1438     _debug_line(s, "passed: %d", passed);
1439 
1440     if(s->mode == 0)
1441         ret = jdns_step_unicast(s, now);
1442     else
1443         ret = jdns_step_multicast(s, now);
1444 
1445     s->last_time = now;
1446     return ret;
1447 }
1448 
1449 int jdns_next_timer(jdns_session_t *s)
1450 {
1451     return s->next_timer;
1452 }
1453 
1454 void jdns_set_handle_readable(jdns_session_t *s, int handle)
1455 {
1456     (void)handle;
1457     s->handle_readable = 1;
1458 }
1459 
1460 void jdns_set_handle_writable(jdns_session_t *s, int handle)
1461 {
1462     (void)handle;
1463     s->handle_writable = 1;
1464 }
1465 
1466 jdns_event_t *jdns_next_event(jdns_session_t *s)
1467 {
1468     jdns_event_t *event = 0;
1469     if(s->events->count > 0)
1470     {
1471         event_t *e = (event_t *)s->events->item[0];
1472         event = e->event;
1473         e->event = 0;
1474         list_remove(s->events, e);
1475     }
1476     return event;
1477 }
1478 
1479 void jdns_set_hold_ids_enabled(jdns_session_t *s, int enabled)
1480 {
1481     _set_hold_ids_enabled(s, enabled);
1482 }
1483 
1484 /*---------------------------------------------------------------------------- */
1485 /* jdns - internal functions */
1486 /*---------------------------------------------------------------------------- */
1487 
1488 /* we don't have vsnprintf on windows, so don't pass anything enormous to */
1489 /*   this function.  the plan is that no line should exceed 1000 bytes, */
1490 /*   although _print_rr() might get close.  a 2048 byte buffer should be */
1491 /*   plenty then. */
1492 void _debug_line(jdns_session_t *s, const char *format, ...)
1493 {
1494     char *buf = (char *)malloc(2048);
1495     va_list ap;
1496     va_start(ap, format);
1497     jdns_vsprintf_s(buf, 2048, format, ap);
1498     va_end(ap);
1499     s->cb.debug_line(s, s->cb.app, buf);
1500     free(buf);
1501 }
1502 
1503 int _callback_time_now(mdnsd d, void *arg)
1504 {
1505     jdns_session_t *s = (jdns_session_t *)arg;
1506     (void)d;
1507     /* offset the time, mdnsd doesn't like starting at 0 */
1508     return s->cb.time_now(s, s->cb.app) + 120 * 1000;
1509 }
1510 
1511 int _callback_rand_int(mdnsd d, void *arg)
1512 {
1513     jdns_session_t *s = (jdns_session_t *)arg;
1514     (void)d;
1515     return s->cb.rand_int(s, s->cb.app);
1516 }
1517 
1518 void _append_event(jdns_session_t *s, jdns_event_t *event)
1519 {
1520     event_t *e = event_new();
1521     e->event = event;
1522     list_insert(s->events, e, -1);
1523 }
1524 
1525 void _append_event_and_hold_id(jdns_session_t *s, jdns_event_t *event)
1526 {
1527     if(s->do_hold_req_ids)
1528         _hold_req_id(s, event->id);
1529     _append_event(s, event);
1530 }
1531 
1532 void _remove_name_server_datagrams(jdns_session_t *s, int ns_id)
1533 {
1534     int n;
1535     for(n = 0; n < s->outgoing->count; ++n)
1536     {
1537         datagram_t *a = (datagram_t *)s->outgoing->item[n];
1538         if(a->ns_id == ns_id)
1539         {
1540             list_remove(s->outgoing, a);
1541             --n; /* adjust position */
1542         }
1543     }
1544 }
1545 
1546 void _remove_query_datagrams(jdns_session_t *s, const query_t *q)
1547 {
1548     int n;
1549     for(n = 0; n < s->outgoing->count; ++n)
1550     {
1551         datagram_t *a = (datagram_t *)s->outgoing->item[n];
1552         if(a->query == q)
1553         {
1554             list_remove(s->outgoing, a);
1555             --n; /* adjust position */
1556         }
1557     }
1558 }
1559 
1560 void _process_message(jdns_session_t *s, jdns_packet_t *p, int now, query_t *q, name_server_t *ns);
1561 
1562 /* return 1 if 'q' should be deleted, 0 if not */
1563 int _process_response(jdns_session_t *s, jdns_response_t *r, int nxdomain, query_t *q);
1564 
1565 jdns_response_t *_cache_get_response(jdns_session_t *s, const unsigned char *qname, int qtype, int *_lowest_timeleft)
1566 {
1567     int n;
1568     int lowest_timeleft = -1;
1569     int now = s->cb.time_now(s, s->cb.app);
1570     jdns_response_t *r = 0;
1571     for(n = 0; n < s->cache->count; ++n)
1572     {
1573         cache_item_t *i = (cache_item_t *)s->cache->item[n];
1574         if(jdns_domain_cmp(i->qname, qname) && i->qtype == qtype)
1575         {
1576             int passed, timeleft;
1577 
1578             if(!r)
1579                 r = jdns_response_new();
1580 
1581             if(i->record)
1582                 jdns_response_append_answer(r, jdns_rr_copy(i->record));
1583 
1584             passed = now - i->time_start;
1585             timeleft = (i->ttl * 1000) - passed;
1586             if(lowest_timeleft == -1 || timeleft < lowest_timeleft)
1587                 lowest_timeleft = timeleft;
1588         }
1589     }
1590     if(_lowest_timeleft)
1591         *_lowest_timeleft = lowest_timeleft;
1592     return r;
1593 }
1594 
1595 query_t *_get_query(jdns_session_t *s, const unsigned char *qname, int qtype, int unique)
1596 {
1597     int n;
1598     query_t *q;
1599     jdns_string_t *str;
1600 
1601     if(!unique)
1602     {
1603         /* check for existing queries */
1604         for(n = 0; n < s->queries->count; ++n)
1605         {
1606             q = (query_t *)s->queries->item[n];
1607             if(jdns_domain_cmp(q->qname, qname) && q->qtype == qtype)
1608             {
1609                 /* if it is inactive, just nuke it */
1610                 if(q->step == -1)
1611                 {
1612                     _remove_query_datagrams(s, q);
1613                     list_remove(s->queries, q);
1614                     --n; /* adjust position */
1615                 }
1616                 /* otherwise, latch onto the first one we find */
1617                 else
1618                 {
1619                     str = _make_printable_cstr((const char *)q->qname);
1620                     _debug_line(s, "[%d] reusing query for: [%s] [%s]", q->id, _qtype2str(qtype), str->data);
1621                     jdns_string_delete(str);
1622                     return q;
1623                 }
1624             }
1625         }
1626     }
1627 
1628     q = query_new();
1629     q->id = get_next_qid(s);
1630     q->qname = _ustrdup(qname);
1631     q->qtype = qtype;
1632     q->step = 0;
1633     q->dns_id = -1;
1634     q->time_start = 0;
1635     q->time_next = 0;
1636     q->trycache = 1;
1637     q->retrying = 0;
1638     list_insert(s->queries, q, -1);
1639 
1640     str = _make_printable_cstr((const char *)q->qname);
1641     _debug_line(s, "[%d] querying: [%s] [%s]", q->id, _qtype2str(qtype), str->data);
1642     jdns_string_delete(str);
1643     return q;
1644 }
1645 
1646 int _unicast_query(jdns_session_t *s, const unsigned char *name, int qtype)
1647 {
1648     unsigned char *qname;
1649     query_t *q;
1650     int req_id;
1651     jdns_string_t *str;
1652 
1653     str = _make_printable_cstr((const char *)name);
1654     _debug_line(s, "query input: [%s]", str->data);
1655     jdns_string_delete(str);
1656 
1657     qname = _fix_input(name);
1658 
1659     q = _get_query(s, qname, qtype, 0);
1660     req_id = get_next_req_id(s);
1661     query_add_req_id(q, req_id);
1662     free(qname);
1663     return req_id;
1664 }
1665 
1666 void _unicast_cancel(jdns_session_t *s, query_t *q)
1667 {
1668     /* didn't even do a step yet?  just remove it */
1669     if(q->step == 0)
1670     {
1671         _remove_query_datagrams(s, q);
1672         list_remove(s->queries, q);
1673     }
1674     /* otherwise, just deactivate */
1675     else
1676     {
1677         /* deactivate and remain in the background for */
1678         /*  1 minute.  this will allow us to cache a */
1679         /*  reply, even if the user is not currently */
1680         /*  interested. */
1681         q->step = -1;
1682         q->time_start = s->cb.time_now(s, s->cb.app);
1683         q->time_next = 60000;
1684     }
1685 }
1686 
1687 void _queue_packet(jdns_session_t *s, query_t *q, const name_server_t *ns, int recurse, int query_send_type)
1688 {
1689     jdns_packet_t *packet;
1690     datagram_t *a;
1691 
1692     packet = jdns_packet_new();
1693     packet->id = q->dns_id;
1694     packet->opts.rd = recurse; /* recursion desired */
1695     {
1696         jdns_packet_question_t *question = jdns_packet_question_new();
1697         question->qname = jdns_string_new();
1698         jdns_string_set_cstr(question->qname, (const char *)q->qname);
1699         question->qtype = q->qtype;
1700         question->qclass = 0x0001;
1701         jdns_list_insert(packet->questions, question, -1);
1702         jdns_packet_question_delete(question);
1703     }
1704     if(!jdns_packet_export(packet, JDNS_UDP_UNI_OUT_MAX))
1705     {
1706         _debug_line(s, "outgoing packet export error, not sending");
1707         jdns_packet_delete(packet);
1708         return;
1709     }
1710 
1711     a = datagram_new();
1712     a->handle = s->handle;
1713     a->dest_address = jdns_address_copy(ns->address);
1714     a->dest_port = ns->port;
1715     a->data = jdns_copy_array(packet->raw_data, packet->raw_size);
1716     a->size = packet->raw_size;
1717     a->query = q;
1718     a->query_send_type = query_send_type;
1719     a->ns_id = ns->id;
1720 
1721     jdns_packet_delete(packet);
1722 
1723     list_insert(s->outgoing, a, -1);
1724 }
1725 
1726 /* return 1 if packets still need to be written */
1727 int _unicast_do_writes(jdns_session_t *s, int now);
1728 
1729 /* return 1 if packets still need to be read */
1730 int _unicast_do_reads(jdns_session_t *s, int now);
1731 
1732 int jdns_step_unicast(jdns_session_t *s, int now)
1733 {
1734     int n;
1735     int need_read = 0;
1736     int need_write = 0;
1737     int smallest_time = -1;
1738     int flags;
1739 
1740     if(s->shutdown == 1)
1741     {
1742         jdns_event_t *event = jdns_event_new();
1743         event->type = JDNS_EVENT_SHUTDOWN;
1744         _append_event(s, event);
1745         s->shutdown = 2;
1746         return 0;
1747     }
1748 
1749     /* expire cached items */
1750     for(n = 0; n < s->cache->count; ++n)
1751     {
1752         cache_item_t *i = (cache_item_t *)s->cache->item[n];
1753         if(now >= i->time_start + (i->ttl * 1000))
1754         {
1755             jdns_string_t *str = _make_printable_cstr((const char *)i->qname);
1756             _debug_line(s, "cache exp [%s]", str->data);
1757             jdns_string_delete(str);
1758             list_remove(s->cache, i);
1759             --n; /* adjust position */
1760         }
1761     }
1762 
1763     need_write = _unicast_do_writes(s, now);
1764     need_read = _unicast_do_reads(s, now);
1765 
1766     /* calculate next timer (based on queries and cache) */
1767     for(n = 0; n < s->queries->count; ++n)
1768     {
1769         query_t *q = (query_t *)(s->queries->item[n]);
1770         if(q->time_start != -1)
1771         {
1772             int qpassed = now - q->time_start;
1773             int timeleft = q->time_next - qpassed;
1774             if(timeleft < 0)
1775                 timeleft = 0;
1776 
1777             if(smallest_time == -1 || timeleft < smallest_time)
1778                 smallest_time = timeleft;
1779         }
1780     }
1781     for(n = 0; n < s->cache->count; ++n)
1782     {
1783         cache_item_t *i = (cache_item_t *)(s->cache->item[n]);
1784         int passed = now - i->time_start;
1785         int timeleft = (i->ttl * 1000) - passed;
1786         if(timeleft < 0)
1787             timeleft = 0;
1788 
1789         if(smallest_time == -1 || timeleft < smallest_time)
1790             smallest_time = timeleft;
1791     }
1792 
1793     flags = 0;
1794     if(smallest_time != -1)
1795     {
1796         flags |= JDNS_STEP_TIMER;
1797         s->next_timer = smallest_time;
1798 
1799         /* offset it a little bit, so that the user doesn't call */
1800         /*  us too early, resulting in a no-op and another timer */
1801         /*  of 1 millisecond. */
1802         s->next_timer += 2;
1803     }
1804     if(need_read || need_write)
1805         flags |= JDNS_STEP_HANDLE;
1806     return flags;
1807 }
1808 
1809 int _unicast_do_writes(jdns_session_t *s, int now)
1810 {
1811     int need_write = 0;
1812     int n, k;
1813 
1814     for(n = 0; n < s->queries->count; ++n)
1815     {
1816         query_t *q;
1817         int qpassed, timeleft;
1818         int giveup;
1819         name_server_t *ns;
1820         int already_sending;
1821 
1822         q = (query_t *)s->queries->item[n];
1823 
1824         /* nothing to do */
1825         if(q->time_start == -1)
1826             continue;
1827 
1828         qpassed = now - q->time_start;
1829         timeleft = q->time_next - qpassed;
1830         if(timeleft < 0)
1831             timeleft = 0;
1832         _debug_line(s, "[%d] time_start/next=%d/%d (left=%d)", q->id, q->time_start, q->time_next, timeleft);
1833         if(timeleft > 0)
1834             continue;
1835 
1836         if(q->trycache)
1837         {
1838             /* is it cached? */
1839             int lowest_timeleft;
1840             int qtype = q->qtype;
1841             jdns_response_t *r;
1842 
1843             r = _cache_get_response(s, q->qname, qtype, &lowest_timeleft);
1844 
1845             /* not found?  try cname */
1846             if(!r)
1847             {
1848                 qtype = JDNS_RTYPE_CNAME;
1849                 r = _cache_get_response(s, q->qname, qtype, &lowest_timeleft);
1850             }
1851 
1852             if(r)
1853             {
1854                 int nxdomain;
1855 
1856                 _debug_line(s, "[%d] using cached answer", q->id);
1857 
1858                 /* are any of the records about to expire in 3 minutes? */
1859                 /*  assume the client is interested in this record and */
1860                 /*  query it again "in the background" */
1861                 if(lowest_timeleft < (3 * 60 * 1000))
1862                 {
1863                     query_t *new_q;
1864 
1865                     _debug_line(s, "requerying for cached item about to expire");
1866 
1867                     new_q = _get_query(s, q->qname, q->qtype, 1);
1868                     new_q->retrying = 1; /* slow it down */
1869                     new_q->trycache = 0; /* don't use the cache for this */
1870                 }
1871 
1872                 nxdomain = r->answerCount == 0 ? 1 : 0;
1873                 if(_process_response(s, r, nxdomain, q))
1874                 {
1875                     _remove_query_datagrams(s, q);
1876                     list_remove(s->queries, q);
1877                     --n; /* adjust position */
1878                 }
1879                 continue;
1880             }
1881         }
1882 
1883         /* inactive */
1884         if(q->step == -1)
1885         {
1886             /* time up on an inactive query?  remove it */
1887             _debug_line(s, "removing inactive query");
1888             _remove_query_datagrams(s, q);
1889             list_remove(s->queries, q);
1890             --n; /* adjust position */
1891             continue;
1892         }
1893 
1894         giveup = 0;
1895 
1896         /* too many tries, give up */
1897         if(q->step == 8)
1898             giveup = 1;
1899 
1900         /* no nameservers, give up */
1901         /*  (this would happen if someone removed all nameservers */
1902         /*   during a query) */
1903         if(s->name_servers->count == 0)
1904             giveup = 1;
1905 
1906         if(giveup)
1907         {
1908             /* report event to any requests listening */
1909             for(k = 0; k < q->req_ids_count; ++k)
1910             {
1911                 jdns_event_t *event = jdns_event_new();
1912                 event->type = JDNS_EVENT_RESPONSE;
1913                 event->id = q->req_ids[k];
1914                 event->status = JDNS_STATUS_TIMEOUT;
1915                 _append_event_and_hold_id(s, event);
1916             }
1917 
1918             /* report error to parent */
1919             if(q->cname_parent)
1920             {
1921                 /* report event to any requests listening */
1922                 query_t *cq = q->cname_parent;
1923                 for(k = 0; k < cq->req_ids_count; ++k)
1924                 {
1925                     jdns_event_t *event = jdns_event_new();
1926                     event->type = JDNS_EVENT_RESPONSE;
1927                     event->id = cq->req_ids[k];
1928                     event->status = JDNS_STATUS_TIMEOUT;
1929                     _append_event_and_hold_id(s, event);
1930                 }
1931                 list_remove(s->queries, cq);
1932             }
1933 
1934             _remove_query_datagrams(s, q);
1935             list_remove(s->queries, q);
1936             --n; /* adjust position */
1937             continue;
1938         }
1939 
1940         /* assign a packet id if we don't have one yet */
1941         if(q->dns_id == -1)
1942         {
1943             q->dns_id = get_next_dns_id(s);
1944 
1945             /* couldn't get an id? */
1946             if(q->dns_id == -1)
1947             {
1948                 _debug_line(s, "unable to reserve packet id");
1949 
1950                 /* report event to any requests listening */
1951                 for(k = 0; k < q->req_ids_count; ++k)
1952                 {
1953                     jdns_event_t *event = jdns_event_new();
1954                     event->type = JDNS_EVENT_RESPONSE;
1955                     event->id = q->req_ids[k];
1956                     event->status = JDNS_STATUS_ERROR;
1957                     _append_event_and_hold_id(s, event);
1958                 }
1959 
1960                 /* report error to parent */
1961                 if(q->cname_parent)
1962                 {
1963                     /* report event to any requests listening */
1964                     query_t *cq = q->cname_parent;
1965                     for(k = 0; k < cq->req_ids_count; ++k)
1966                     {
1967                         jdns_event_t *event = jdns_event_new();
1968                         event->type = JDNS_EVENT_RESPONSE;
1969                         event->id = cq->req_ids[k];
1970                         event->status = JDNS_STATUS_ERROR;
1971                         _append_event_and_hold_id(s, event);
1972                     }
1973                     list_remove(s->queries, cq);
1974                 }
1975 
1976                 _remove_query_datagrams(s, q);
1977                 list_remove(s->queries, q);
1978                 --n; /* adjust position */
1979                 continue;
1980             }
1981         }
1982 
1983         /* out of name servers? */
1984         if(q->servers_tried_count == s->name_servers->count)
1985         {
1986             /* clear the 'tried' list, and start over in retry mode */
1987             query_clear_servers_tried(q);
1988             q->retrying = 1;
1989         }
1990 
1991         /* find a nameserver that has not been tried */
1992         ns = 0;
1993         for(k = 0; k < s->name_servers->count; ++k)
1994         {
1995             name_server_t *i = (name_server_t *)s->name_servers->item[k];
1996             if(!query_server_tried(q, i->id))
1997             {
1998                 ns = i;
1999                 break;
2000             }
2001         }
2002 
2003         /* in theory, it is not possible for 'ns' to be null here */
2004 
2005         /* don't send the packet if there is already one in the queue */
2006         already_sending = 0;
2007         for(k = 0; k < s->outgoing->count; ++k)
2008         {
2009             datagram_t *a = (datagram_t *)s->outgoing->item[k];
2010             if(a->query == q && a->query_send_type == 0)
2011             {
2012                 already_sending = 1;
2013                 break;
2014             }
2015         }
2016 
2017         /* send the query, with recursion desired, normal query_send_type */
2018         if(!already_sending)
2019             _queue_packet(s, q, ns, 1, 0);
2020 
2021         query_add_server_tried(q, ns->id);
2022 
2023         /* if there is one query, then do a trick on the first step */
2024         /*if(s->queries->count == 1 && q->step == 0 && !q->retrying)
2025         {
2026             // query all other servers non-recursively 
2027             // note: if sending fails, there is no retry 
2028             for(k = 0; k < s->name_servers->count; ++k)
2029             {
2030                 name_server_t *i = (name_server_t *)s->name_servers->item[k];
2031                 if(!query_server_tried(q, i->id))
2032                 {
2033                     // last arg sets first-step query_send_type 
2034                     _queue_packet(s, q, i, 0, 1);
2035                 }
2036             }
2037         }*/
2038 
2039         /* out of name servers? */
2040         if(q->servers_tried_count == s->name_servers->count)
2041         {
2042             /* clear the 'tried' list, and start over in retry mode */
2043             query_clear_servers_tried(q);
2044             q->retrying = 1;
2045         }
2046 
2047         q->time_start = now;
2048         q->time_next = q->retrying ? 1500 : 800;
2049         ++q->step;
2050     }
2051 
2052     /* try to send queued outgoing packets */
2053     for(n = 0; n < s->outgoing->count; ++n)
2054     {
2055         datagram_t *a = (datagram_t *)s->outgoing->item[n];
2056         int ret;
2057 
2058         if(!s->handle_writable)
2059         {
2060             need_write = 1;
2061             break;
2062         }
2063 
2064         _debug_line(s, "SEND %s:%d (size=%d)", a->dest_address->c_str, a->dest_port, a->size);
2065         _print_hexdump(s, a->data, a->size);
2066 
2067         ret = s->cb.udp_write(s, s->cb.app, a->handle, a->dest_address, a->dest_port, a->data, a->size);
2068         if(ret == 0)
2069         {
2070             s->handle_writable = 0;
2071             need_write = 1;
2072             break;
2073         }
2074 
2075         list_remove(s->outgoing, a);
2076         --n; /* adjust position */
2077     }
2078 
2079     return need_write;
2080 }
2081 
2082 void _cache_add(jdns_session_t *s, const unsigned char *qname, int qtype, int time_start, int ttl, const jdns_rr_t *record)
2083 {
2084     cache_item_t *i;
2085     jdns_string_t *str;
2086     if(ttl == 0)
2087         return;
2088     if(s->cache->count >= JDNS_CACHE_MAX)
2089         return;
2090     i = cache_item_new();
2091     i->qname = _ustrdup(qname);
2092     i->qtype = qtype;
2093     i->time_start = time_start;
2094     i->ttl = ttl;
2095     if(record)
2096         i->record = jdns_rr_copy(record);
2097     list_insert(s->cache, i, -1);
2098 
2099     str = _make_printable_cstr((const char *)i->qname);
2100     _debug_line(s, "cache add [%s] for %d seconds", str->data, i->ttl);
2101     jdns_string_delete(str);
2102 }
2103 
2104 void _cache_remove_all_of_kind(jdns_session_t *s, const unsigned char *qname, int qtype)
2105 {
2106     int n;
2107     for(n = 0; n < s->cache->count; ++n)
2108     {
2109         cache_item_t *i = (cache_item_t *)s->cache->item[n];
2110         if(jdns_domain_cmp(i->qname, qname) && i->qtype == qtype)
2111         {
2112             jdns_string_t *str = _make_printable_cstr((const char *)i->qname);
2113             _debug_line(s, "cache del [%s]", str->data);
2114             jdns_string_delete(str);
2115             list_remove(s->cache, i);
2116             --n; /* adjust position */
2117         }
2118     }
2119 }
2120 
2121 int _unicast_do_reads(jdns_session_t *s, int now)
2122 {
2123     int need_read;
2124     int n, k;
2125 
2126     /* let's always ask for reads, just so the user doesn't have to */
2127     /*  worry about what should happen to incoming packets otherwise */
2128     need_read = 1;
2129 
2130     if(!s->handle_readable)
2131         return need_read;
2132 
2133     while(1)
2134     {
2135         unsigned char buf[JDNS_UDP_UNI_IN_MAX];
2136         int bufsize = JDNS_UDP_UNI_IN_MAX;
2137         int ret;
2138         jdns_packet_t *packet;
2139         jdns_address_t *addr;
2140         int port;
2141         query_t *q;
2142         name_server_t *ns;
2143 
2144         addr = jdns_address_new();
2145         ret = s->cb.udp_read(s, s->cb.app, s->handle, addr, &port, buf, &bufsize);
2146 
2147         /* no packet? */
2148         if(ret == 0)
2149         {
2150             s->handle_readable = 0;
2151             jdns_address_delete(addr);
2152             break;
2153         }
2154 
2155         _debug_line(s, "RECV %s:%d (size=%d)", addr->c_str, port, bufsize);
2156         _print_hexdump(s, buf, bufsize);
2157 
2158         if(!jdns_packet_import(&packet, buf, bufsize))
2159         {
2160             _debug_line(s, "error parsing packet / too large");
2161 
2162             jdns_address_delete(addr);
2163             continue;
2164         }
2165 
2166         _print_packet(s, packet);
2167 
2168         if(s->queries->count == 0)
2169         {
2170             _debug_line(s, "we have no queries");
2171 
2172             jdns_address_delete(addr);
2173             continue;
2174         }
2175 
2176         /* who does it belong to? */
2177         q = 0;
2178         ns = 0;
2179         for(n = 0; n < s->queries->count; ++n)
2180         {
2181             query_t *i = (query_t *)s->queries->item[n];
2182             if(i->dns_id == -1)
2183                 continue;
2184 
2185             if(i->dns_id == packet->id)
2186             {
2187                 q = i;
2188                 break;
2189             }
2190         }
2191 
2192         if(q)
2193         {
2194             /* what name server did it come from? */
2195             for(k = 0; k < s->name_servers->count; ++k)
2196             {
2197                 name_server_t *i = (name_server_t *)s->name_servers->item[k];
2198                 if(jdns_address_cmp(i->address, addr) && i->port == port)
2199                 {
2200                     ns = i;
2201                     break;
2202                 }
2203             }
2204 
2205             /* none? maybe that's because we're using unicast */
2206             /*   over multicast, where responses always come */
2207             /*   from an unexpected address */
2208             if(!ns && s->name_servers->count > 0)
2209             {
2210                 name_server_t *i;
2211                 jdns_address_t *m4, *m6;
2212 
2213                 i = (name_server_t *)s->name_servers->item[0];
2214                 m4 = jdns_address_multicast4_new();
2215                 m6 = jdns_address_multicast6_new();
2216                 if(jdns_address_cmp(i->address, m4) || jdns_address_cmp(i->address, m6))
2217                     ns = i;
2218                 jdns_address_delete(m4);
2219                 jdns_address_delete(m6);
2220             }
2221 
2222             /* if there is no suitable name server, don't accept */
2223             /*   as a reply */
2224             if(!ns)
2225                 q = 0;
2226         }
2227 
2228         jdns_address_delete(addr);
2229 
2230         /* no queries?  eat the packet */
2231         if(!q)
2232         {
2233             _debug_line(s, "no such query for packet");
2234             jdns_packet_delete(packet);
2235             continue;
2236         }
2237 
2238         _process_message(s, packet, now, q, ns);
2239         jdns_packet_delete(packet);
2240     }
2241 
2242     return need_read;
2243 }
2244 
2245 void _process_message(jdns_session_t *s, jdns_packet_t *packet, int now, query_t *q, name_server_t *ns)
2246 {
2247     int n;
2248     int authoritative;
2249     int truncated;
2250     int recursion_desired;
2251     int answer_section_ok;
2252     int nxdomain;
2253     jdns_response_t *r;
2254 
2255     if(packet->opts.opcode != 0)
2256     {
2257         _debug_line(s, "opcode != 0, discarding");
2258         return;
2259     }
2260 
2261     /* we don't test RA (recursion available) */
2262     /* we don't test the extra Z fields */
2263 
2264     authoritative = packet->opts.aa;
2265     truncated = packet->opts.tc;
2266     recursion_desired = packet->opts.rd;
2267     answer_section_ok = 0;
2268     if(packet->qdcount == packet->questions->count && packet->ancount == packet->answerRecords->count)
2269         answer_section_ok = 1;
2270     nxdomain = 0;
2271 
2272     r = 0;
2273 
2274     /* nxdomain */
2275     if(packet->opts.rcode == 3)
2276     {
2277         r = jdns_response_new();
2278         nxdomain = 1;
2279     }
2280     /* normal */
2281     else if(packet->opts.rcode == 0)
2282     {
2283         int at_least_something;
2284         int success;
2285 
2286         r = _packet2response(packet, q->qname, q->qtype, 0xffff);
2287         at_least_something = 0;
2288         if(r->answerCount > 0)
2289             at_least_something = 1;
2290         _print_records(s, r, q->qname);
2291 
2292         success = 0;
2293         if(at_least_something)
2294         {
2295             success = 1;
2296         }
2297         else
2298         {
2299             /* note: why does qdns care about recursion_desired here? */
2300             if(authoritative && recursion_desired)
2301                 success = 1;
2302         }
2303 
2304         if(!success)
2305         {
2306             jdns_response_delete(r);
2307             r = 0;
2308         }
2309     }
2310 
2311     /* caching */
2312     if(r)
2313     {
2314         if(nxdomain)
2315         {
2316             /* cache nxdomain for 1 minute */
2317             if(q->qtype != JDNS_RTYPE_ANY)
2318             {
2319                 _cache_remove_all_of_kind(s, q->qname, q->qtype);
2320                 _cache_add(s, q->qname, q->qtype, now, 60, 0);
2321             }
2322         }
2323         else
2324         {
2325             int cache_answers;
2326             int cache_additional;
2327 
2328             /* clear past items */
2329             _cache_remove_all_of_kind(s, q->qname, q->qtype);
2330 
2331             cache_answers = 1;
2332             cache_additional = 1;
2333 
2334             /* if truncated, we may not want to cache */
2335             if(truncated)
2336             {
2337                 cache_additional = 0;
2338                 if(!answer_section_ok)
2339                     cache_answers = 0;
2340             }
2341 
2342             if(cache_answers)
2343             {
2344                 for(n = 0; n < r->answerCount; ++n)
2345                 {
2346                     jdns_rr_t *record = r->answerRecords[n];
2347                     _cache_add(s, q->qname, record->type, now, _min(record->ttl, JDNS_TTL_MAX), record);
2348                 }
2349             }
2350 
2351             if(cache_additional)
2352             {
2353                 for(n = 0; n < r->additionalCount; ++n)
2354                 {
2355                     jdns_rr_t *record = r->additionalRecords[n];
2356                     _cache_add(s, record->owner, record->type, now, _min(record->ttl, JDNS_TTL_MAX), record);
2357                 }
2358             }
2359         }
2360     }
2361 
2362     /* don't pass authority/additional records upwards */
2363     if(r)
2364         jdns_response_remove_extra(r);
2365 
2366     /* this server returned an error? */
2367     if(!r && ns)
2368     {
2369         /* all failed servers must also be considered tried servers, */
2370         /*   so mark as tried if necessary.  this can happen if the */
2371         /*   tried list is cleared (to perform retrying) and then an */
2372         /*   error is received */
2373         if(!query_server_tried(q, ns->id))
2374             query_add_server_tried(q, ns->id);
2375 
2376         query_add_server_failed(q, ns->id);
2377     }
2378 
2379     if(_process_response(s, r, nxdomain, q))
2380     {
2381         _remove_query_datagrams(s, q);
2382         list_remove(s->queries, q);
2383     }
2384 
2385     jdns_response_delete(r);
2386 }
2387 
2388 int _process_response(jdns_session_t *s, jdns_response_t *r, int nxdomain, query_t *q)
2389 {
2390     int k;
2391 
2392     /* error */
2393     if(!r)
2394     {
2395         int all_errored;
2396 
2397         /* if not all servers have errored, ignore error */
2398         all_errored = 1;
2399         for(k = 0; k < s->name_servers->count; ++k)
2400         {
2401             name_server_t *ns = (name_server_t *)s->name_servers->item[k];
2402             if(!query_server_failed(q, ns->id))
2403             {
2404                 all_errored = 0;
2405                 break;
2406             }
2407         }
2408         if(!all_errored)
2409             return 0;
2410 
2411         /* report event to any requests listening */
2412         for(k = 0; k < q->req_ids_count; ++k)
2413         {
2414             jdns_event_t *event = jdns_event_new();
2415             event->type = JDNS_EVENT_RESPONSE;
2416             event->id = q->req_ids[k];
2417             event->status = JDNS_STATUS_ERROR;
2418             _append_event_and_hold_id(s, event);
2419         }
2420 
2421         /* report error to parent */
2422         if(q->cname_parent)
2423         {
2424             /* report event to any requests listening */
2425             query_t *cq = q->cname_parent;
2426             for(k = 0; k < cq->req_ids_count; ++k)
2427             {
2428                 jdns_event_t *event = jdns_event_new();
2429                 event->type = JDNS_EVENT_RESPONSE;
2430                 event->id = cq->req_ids[k];
2431                 event->status = JDNS_STATUS_ERROR;
2432                 _append_event_and_hold_id(s, event);
2433             }
2434             list_remove(s->queries, cq);
2435         }
2436 
2437         return 1;
2438     }
2439     /* nxdomain */
2440     else if(nxdomain)
2441     {
2442         /* report event to any requests listening */
2443         for(k = 0; k < q->req_ids_count; ++k)
2444         {
2445             jdns_event_t *event = jdns_event_new();
2446             event->type = JDNS_EVENT_RESPONSE;
2447             event->id = q->req_ids[k];
2448             event->status = JDNS_STATUS_NXDOMAIN;
2449             _append_event_and_hold_id(s, event);
2450         }
2451 
2452         /* report error to parent */
2453         if(q->cname_parent)
2454         {
2455             /* report event to any requests listening */
2456             query_t *cq = q->cname_parent;
2457             for(k = 0; k < cq->req_ids_count; ++k)
2458             {
2459                 jdns_event_t *event = jdns_event_new();
2460                 event->type = JDNS_EVENT_RESPONSE;
2461                 event->id = cq->req_ids[k];
2462                 event->status = JDNS_STATUS_ERROR;
2463                 _append_event_and_hold_id(s, event);
2464             }
2465             list_remove(s->queries, cq);
2466         }
2467 
2468         return 1;
2469     }
2470 
2471     /* all we got was a cname that we didn't ask for? */
2472     if(r->answerCount == 1 && r->answerRecords[0]->type == JDNS_RTYPE_CNAME && q->qtype != JDNS_RTYPE_CNAME)
2473     {
2474         query_t *new_q;
2475 
2476         _debug_line(s, "all we got was a cname, following the chain ...");
2477 
2478         /* max chain count, bail */
2479         if(q->cname_chain_count >= JDNS_CNAME_MAX)
2480         {
2481             /* report event to any requests listening */
2482             for(k = 0; k < q->req_ids_count; ++k)
2483             {
2484                 jdns_event_t *event = jdns_event_new();
2485                 event->type = JDNS_EVENT_RESPONSE;
2486                 event->id = q->req_ids[k];
2487                 event->status = JDNS_STATUS_ERROR;
2488                 _append_event_and_hold_id(s, event);
2489             }
2490 
2491             /* report error to parent */
2492             if(q->cname_parent)
2493             {
2494                 /* report event to any requests listening */
2495                 query_t *cq = q->cname_parent;
2496                 for(k = 0; k < cq->req_ids_count; ++k)
2497                 {
2498                     jdns_event_t *event = jdns_event_new();
2499                     event->type = JDNS_EVENT_RESPONSE;
2500                     event->id = cq->req_ids[k];
2501                     event->status = JDNS_STATUS_ERROR;
2502                     _append_event_and_hold_id(s, event);
2503                 }
2504                 list_remove(s->queries, cq);
2505             }
2506 
2507             return 1;
2508         }
2509 
2510         new_q = _get_query(s, r->answerRecords[0]->data.name, q->qtype, 1);
2511 
2512         /* is the current query a child query? (has a parent) */
2513         if(q->cname_parent)
2514         {
2515             /* if so, then set new_q as the new child */
2516             new_q->cname_chain_count = q->cname_chain_count + 1;
2517             new_q->cname_parent = q->cname_parent;
2518             new_q->cname_parent->cname_child = new_q;
2519 
2520             /* and delete the current query */
2521             return 1;
2522         }
2523         else
2524         {
2525             /* otherwise, the current query becomes a parent, with */
2526             /*   new_q set as the child */
2527             new_q->cname_chain_count = q->cname_chain_count + 1;
2528             new_q->cname_parent = q;
2529             q->cname_child = new_q;
2530             q->time_start = -1;
2531             q->dns_id = -1; /* don't handle responses */
2532         }
2533     }
2534 
2535     /* if this query now has a child, then don't report events or delete */
2536     if(q->cname_child)
2537         return 0;
2538 
2539     /* report event to any requests listening */
2540     for(k = 0; k < q->req_ids_count; ++k)
2541     {
2542         jdns_event_t *event = jdns_event_new();
2543         event->type = JDNS_EVENT_RESPONSE;
2544         event->id = q->req_ids[k];
2545         event->status = JDNS_STATUS_SUCCESS;
2546         event->response = jdns_response_copy(r);
2547         _append_event_and_hold_id(s, event);
2548     }
2549 
2550     /* report to parent */
2551     if(q->cname_parent)
2552     {
2553         /* report event to any requests listening */
2554         query_t *cq = q->cname_parent;
2555         for(k = 0; k < cq->req_ids_count; ++k)
2556         {
2557             jdns_event_t *event = jdns_event_new();
2558             event->type = JDNS_EVENT_RESPONSE;
2559             event->id = cq->req_ids[k];
2560             event->status = JDNS_STATUS_SUCCESS;
2561             event->response = jdns_response_copy(r);
2562             _append_event_and_hold_id(s, event);
2563         }
2564         list_remove(s->queries, cq);
2565     }
2566 
2567     return 1;
2568 }
2569 
2570 /*---------------------------------------------------------------------------- */
2571 /* jdns - multicast */
2572 /*---------------------------------------------------------------------------- */
2573 static jdns_rr_t *_mdnsda2rr(mdnsda a)
2574 {
2575     jdns_rr_t *rr;
2576 
2577     if(a->type == JDNS_RTYPE_ANY)
2578         return 0;
2579 
2580     /* for AAAA, TXT and HINFO, run the raw rdata through jdns_rr's parser */
2581     if(a->type == JDNS_RTYPE_AAAA || a->type == JDNS_RTYPE_TXT || a->type == JDNS_RTYPE_HINFO)
2582     {
2583         jdns_packet_resource_t *pr = jdns_packet_resource_new();
2584         pr->qname = jdns_string_new();
2585         jdns_string_set_cstr(pr->qname, (const char *)a->name);
2586         pr->qtype = a->type;
2587         pr->qclass = 0x0001; /* class is always 1 for us */
2588         if(a->ttl == 0)
2589             pr->ttl = 0;
2590         else
2591             pr->ttl = a->real_ttl;
2592         pr->rdata = jdns_copy_array(a->rdata, a->rdlen);
2593         pr->rdlength = a->rdlen;
2594 
2595         /* we don't need a reference for these types */
2596         rr = jdns_rr_from_resource(pr, 0);
2597     }
2598     /* else, pull the values out of 'a' directly */
2599     else
2600     {
2601         rr = jdns_rr_new();
2602         rr->owner = _ustrdup(a->name);
2603         rr->qclass = 0x0001; /* class is always 1 for us */
2604         if(a->ttl == 0)
2605             rr->ttl = 0;
2606         else
2607             rr->ttl = a->real_ttl;
2608 
2609         switch(a->type)
2610         {
2611             case JDNS_RTYPE_A:
2612             {
2613                 jdns_address_t *addr = jdns_address_new();
2614                 jdns_address_set_ipv4(addr, a->ip);
2615                 jdns_rr_set_A(rr, addr);
2616                 jdns_address_delete(addr);
2617                 break;
2618             }
2619             case JDNS_RTYPE_AAAA:
2620             {
2621                 /* covered earlier */
2622                 break;
2623             }
2624             case JDNS_RTYPE_MX:
2625             {
2626                 /* don't care about MX */
2627                 jdns_rr_delete(rr);
2628                 rr = 0;
2629                 break;
2630             }
2631             case JDNS_RTYPE_SRV:
2632             {
2633                 jdns_rr_set_SRV(rr, a->rdname, a->srv.port, a->srv.priority, a->srv.weight);
2634                 break;
2635             }
2636             case JDNS_RTYPE_CNAME:
2637             {
2638                 jdns_rr_set_CNAME(rr, a->rdname);
2639                 break;
2640             }
2641             case JDNS_RTYPE_PTR:
2642             {
2643                 jdns_rr_set_PTR(rr, a->rdname);
2644                 break;
2645             }
2646             case JDNS_RTYPE_TXT:
2647             {
2648                 /* covered earlier */
2649                 break;
2650             }
2651             case JDNS_RTYPE_HINFO:
2652             {
2653                 /* covered earlier */
2654                 break;
2655             }
2656             case JDNS_RTYPE_NS:
2657             {
2658                 /* don't care about NS */
2659                 jdns_rr_delete(rr);
2660                 rr = 0;
2661                 break;
2662             }
2663             default:
2664             {
2665                 jdns_rr_set_record(rr, a->type, a->rdata, a->rdlen);
2666                 break;
2667             }
2668         }
2669     }
2670 
2671     return rr;
2672 }
2673 
2674 static int _cmp_rdata(const jdns_rr_t *a, const jdns_rr_t *b)
2675 {
2676     if(a->rdlength != b->rdlength)
2677         return 0;
2678     if(memcmp(a->rdata, b->rdata, a->rdlength) != 0)
2679         return 0;
2680     return 1;
2681 }
2682 
2683 static int _cmp_rr(const jdns_rr_t *a, const jdns_rr_t *b)
2684 {
2685     if(a->type != b->type)
2686         return 0;
2687     if(!jdns_domain_cmp(a->owner, b->owner))
2688         return 0;
2689     switch(a->type)
2690     {
2691         case JDNS_RTYPE_A:
2692             if(!jdns_address_cmp(a->data.address, b->data.address))
2693                 return 0;
2694             break;
2695         case JDNS_RTYPE_AAAA:
2696             if(!_cmp_rdata(a, b))
2697                 return 0;
2698             break;
2699         case JDNS_RTYPE_MX:
2700             /* unsupported */
2701             return 0;
2702         case JDNS_RTYPE_SRV:
2703             if(a->data.server->port != b->data.server->port
2704                 || a->data.server->priority != b->data.server->priority
2705                 || a->data.server->weight != b->data.server->weight
2706                 || !jdns_domain_cmp(a->data.server->name, b->data.server->name)
2707             )
2708                 return 0;
2709             break;
2710         case JDNS_RTYPE_CNAME:
2711             if(!jdns_domain_cmp(a->data.name, b->data.name))
2712                 return 0;
2713             break;
2714         case JDNS_RTYPE_PTR:
2715             if(!jdns_domain_cmp(a->data.name, b->data.name))
2716                 return 0;
2717             break;
2718         case JDNS_RTYPE_TXT:
2719             if(!_cmp_rdata(a, b))
2720                 return 0;
2721             break;
2722         case JDNS_RTYPE_HINFO:
2723             if(!_cmp_rdata(a, b))
2724                 return 0;
2725             break;
2726         case JDNS_RTYPE_NS:
2727             /* unsupported */
2728             return 0;
2729         default:
2730             if(!_cmp_rdata(a, b))
2731                 return 0;
2732             break;
2733     }
2734     return 1;
2735 }
2736 
2737 int _multicast_query_ans(mdnsda a, void *arg)
2738 {
2739     int n;
2740     jdns_session_t *s;
2741     query_t *q;
2742     jdns_response_t *r;
2743     jdns_rr_t *rr;
2744     jdns_event_t *event;
2745 
2746     s = (jdns_session_t *)arg;
2747 
2748     /* what query is this for? */
2749     q = 0;
2750     for(n = 0; n < s->queries->count; ++n)
2751     {
2752         query_t *i = (query_t *)s->queries->item[n];
2753         if((i->qtype == JDNS_RTYPE_ANY || i->qtype == a->type) && jdns_domain_cmp(i->qname, a->name))
2754         {
2755             q = i;
2756             break;
2757         }
2758     }
2759 
2760     /* note: this can't happen, but we'll check anyway */
2761     if(!q)
2762     {
2763         _debug_line(s, "no such multicast query");
2764         return 0;
2765     }
2766 
2767     rr = _mdnsda2rr(a);
2768     if(!rr)
2769         return 0;
2770 
2771     /* add/remove as a known */
2772     if(rr->ttl == 0)
2773     {
2774         for(n = 0; n < q->mul_known->answerCount; ++n)
2775         {
2776             jdns_rr_t *k = q->mul_known->answerRecords[n];
2777             if(_cmp_rr(k, rr))
2778             {
2779                 jdns_response_remove_answer(q->mul_known, n);
2780                 break;
2781             }
2782         }
2783     }
2784     else
2785         jdns_response_append_answer(q->mul_known, rr);
2786 
2787     r = jdns_response_new();
2788     jdns_response_append_answer(r, rr);
2789     jdns_rr_delete(rr);
2790 
2791     /* report event to any requests listening */
2792     for(n = 0; n < q->req_ids_count; ++n)
2793     {
2794         event = jdns_event_new();
2795         event->type = JDNS_EVENT_RESPONSE;
2796         event->id = q->req_ids[n];
2797         event->status = JDNS_STATUS_SUCCESS;
2798         event->response = jdns_response_copy(r);
2799         _append_event(s, event);
2800     }
2801 
2802     jdns_response_delete(r);
2803     return 0;
2804 }
2805 
2806 query_t *_get_multicast_query(jdns_session_t *s, const unsigned char *qname, int qtype)
2807 {
2808     int n;
2809     query_t *q;
2810     jdns_string_t *str;
2811 
2812     /* check for existing queries */
2813     for(n = 0; n < s->queries->count; ++n)
2814     {
2815         q = (query_t *)s->queries->item[n];
2816         if(jdns_domain_cmp(q->qname, qname) && q->qtype == qtype)
2817         {
2818             str = _make_printable_cstr((const char *)q->qname);
2819             _debug_line(s, "[%d] reusing query for: [%s] [%s]", q->id, _qtype2str(qtype), str->data);
2820             jdns_string_delete(str);
2821             return q;
2822         }
2823     }
2824 
2825     q = query_new();
2826     q->id = get_next_qid(s);
2827     q->qname = _ustrdup(qname);
2828     q->qtype = qtype;
2829     q->step = 0;
2830     q->mul_known = jdns_response_new();
2831     list_insert(s->queries, q, -1);
2832 
2833     str = _make_printable_cstr((const char *)q->qname);
2834     _debug_line(s, "[%d] querying: [%s] [%s]", q->id, _qtype2str(qtype), str->data);
2835     jdns_string_delete(str);
2836     return q;
2837 }
2838 
2839 int _multicast_query(jdns_session_t *s, const unsigned char *name, int qtype)
2840 {
2841     unsigned char *qname;
2842     query_t *q;
2843     int req_id;
2844     jdns_string_t *str;
2845 
2846     str = _make_printable_cstr((const char *)name);
2847     _debug_line(s, "query input: [%s]", str->data);
2848     jdns_string_delete(str);
2849 
2850     /* add a dot to the end if needed */
2851     qname = _fix_input(name);
2852 
2853     q = _get_multicast_query(s, qname, qtype);
2854     req_id = get_next_req_id(s);
2855     query_add_req_id(q, req_id);
2856     free(qname);
2857 
2858     /* start the mdnsd_query if necessary */
2859     if(q->step == 0)
2860     {
2861         q->step = 1;
2862         mdnsd_query(s->mdns, (char *)q->qname, q->qtype, _multicast_query_ans, s);
2863     }
2864     else
2865     {
2866         int n;
2867 
2868         /* report the knowns */
2869         for(n = 0; n < q->mul_known->answerCount; ++n)
2870         {
2871             const jdns_rr_t *rr;
2872             jdns_response_t *r;
2873             jdns_event_t *event;
2874 
2875             rr = q->mul_known->answerRecords[n];
2876             r = jdns_response_new();
2877             jdns_response_append_answer(r, rr);
2878 
2879             event = jdns_event_new();
2880             event->type = JDNS_EVENT_RESPONSE;
2881             event->id = req_id;
2882             event->status = JDNS_STATUS_SUCCESS;
2883             event->response = r;
2884             _append_event(s, event);
2885         }
2886     }
2887     return req_id;
2888 }
2889 
2890 void _multicast_cancel(jdns_session_t *s, int req_id)
2891 {
2892     int n;
2893     for(n = 0; n < s->queries->count; ++n)
2894     {
2895         query_t *q = (query_t *)s->queries->item[n];
2896         if(query_have_req_id(q, req_id))
2897         {
2898             query_remove_req_id(q, req_id);
2899 
2900             /* if no one else is depending on this request, then take action */
2901             if(q->req_ids_count == 0)
2902             {
2903                 mdnsd_query(s->mdns, (char *)q->qname, q->qtype, NULL, 0);
2904                 list_remove(s->queries, q);
2905             }
2906             break;
2907         }
2908     }
2909 }
2910 
2911 void _multicast_pubresult(int result, char *name, int type, void *arg)
2912 {
2913     jdns_session_t *s;
2914     published_item_t *pub;
2915     jdns_event_t *event;
2916     int n;
2917 
2918     s = (jdns_session_t *)arg;
2919 
2920     /* find the associated pub item */
2921     pub = 0;
2922     for(n = 0; n < s->published->count; ++n)
2923     {
2924         published_item_t *i = (published_item_t *)s->published->item[n];
2925         if(strcmp((char *)i->qname, name) == 0 && i->qtype == type)
2926         {
2927             pub = i;
2928             break;
2929         }
2930     }
2931 
2932     /* note: this can't happen, but we'll check anyway */
2933     if(!pub)
2934     {
2935         _debug_line(s, "no such multicast published item");
2936         return;
2937     }
2938 
2939     if(result == 1)
2940     {
2941         jdns_string_t *str = _make_printable_cstr(name);
2942         _debug_line(s, "published name %s for type %d", str->data, type);
2943         jdns_string_delete(str);
2944 
2945         event = jdns_event_new();
2946         event->type = JDNS_EVENT_PUBLISH;
2947         event->id = pub->id;
2948         event->status = JDNS_STATUS_SUCCESS;
2949         _append_event(s, event);
2950     }
2951     else
2952     {
2953         jdns_string_t *str = _make_printable_cstr(name);
2954         _debug_line(s, "conflicting name detected %s for type %d", str->data, type);
2955         jdns_string_delete(str);
2956 
2957         event = jdns_event_new();
2958         event->type = JDNS_EVENT_PUBLISH;
2959         event->id = pub->id;
2960         event->status = JDNS_STATUS_CONFLICT;
2961         _append_event_and_hold_id(s, event);
2962 
2963         /* remove the item */
2964         list_remove(s->published, pub);
2965     }
2966 }
2967 
2968 static jdns_string_t *_create_text(const jdns_stringlist_t *texts)
2969 {
2970     jdns_string_t *out;
2971     int n;
2972     int total;
2973     unsigned char *buf;
2974 
2975     buf = 0;
2976     total = 0;
2977     for(n = 0; n < texts->count; ++n)
2978         total += texts->item[n]->size + 1;
2979     if(total > 0)
2980     {
2981         int at = 0;
2982         buf = (unsigned char *)malloc(total);
2983         for(n = 0; n < texts->count; ++n)
2984         {
2985             unsigned int len = texts->item[n]->size;
2986             buf[at++] = len;
2987             memcpy(buf + at, texts->item[n]->data, len);
2988             at += len;
2989         }
2990     }
2991 
2992     out = jdns_string_new();
2993     if(buf)
2994     {
2995         out->data = buf;
2996         out->size = total;
2997     }
2998     else
2999         jdns_string_set_cstr(out, "");
3000     return out;
3001 }
3002 
3003 static void _publish_applyrr_unknown(jdns_session_t *s, mdnsdr r, const jdns_rr_t *rr)
3004 {
3005     /* for unsupported/unknown, just take the rdata */
3006     /* note: for this to work, the app must explicitly set the rdata. */
3007     /*   if the record is MX or some other known but unsupported record */
3008     /*   type, setting the known fields is not enough */
3009     mdnsd_set_raw(s->mdns, r, (char *)rr->rdata, rr->rdlength);
3010 }
3011 
3012 static int _publish_applyrr(jdns_session_t *s, mdnsdr r, const jdns_rr_t *rr)
3013 {
3014     if(!rr->haveKnown)
3015     {
3016         _publish_applyrr_unknown(s, r, rr);
3017         return 1;
3018     }
3019 
3020     /* jdns_mdnsd supports: A, AAAA, SRV, CNAME, PTR, TXT, and HINFO */
3021     switch(rr->type)
3022     {
3023         case JDNS_RTYPE_A:
3024         {
3025             unsigned long int ip_net = htonl(rr->data.address->addr.v4);
3026             mdnsd_set_raw(s->mdns, r, (char *)&ip_net, 4);
3027             break;
3028         }
3029         case JDNS_RTYPE_AAAA:
3030         {
3031             mdnsd_set_raw(s->mdns, r, (char *)rr->data.address->addr.v6, 16);
3032             break;
3033         }
3034         case JDNS_RTYPE_SRV:
3035         {
3036             mdnsd_set_srv(s->mdns, r, rr->data.server->priority, rr->data.server->weight, rr->data.server->port, (char *)rr->data.server->name);
3037             break;
3038         }
3039         case JDNS_RTYPE_CNAME:
3040         {
3041             mdnsd_set_host(s->mdns, r, (char *)rr->data.name);
3042             break;
3043         }
3044         case JDNS_RTYPE_PTR:
3045         {
3046             mdnsd_set_host(s->mdns, r, (char *)rr->data.name);
3047             break;
3048         }
3049         case JDNS_RTYPE_TXT:
3050         {
3051             jdns_string_t *out = _create_text(rr->data.texts);
3052             mdnsd_set_raw(s->mdns, r, (char *)out->data, out->size);
3053             jdns_string_delete(out);
3054             break;
3055         }
3056         case JDNS_RTYPE_HINFO:
3057         {
3058             jdns_string_t *out;
3059             jdns_stringlist_t *list;
3060 
3061             list = jdns_stringlist_new();
3062             jdns_stringlist_append(list, rr->data.hinfo.cpu);
3063             jdns_stringlist_append(list, rr->data.hinfo.os);
3064             out = _create_text(list);
3065             jdns_stringlist_delete(list);
3066 
3067             mdnsd_set_raw(s->mdns, r, (char *)out->data, out->size);
3068             jdns_string_delete(out);
3069             break;
3070         }
3071         default:
3072         {
3073             _publish_applyrr_unknown(s, r, rr);
3074             break;
3075         }
3076     }
3077 
3078     return 1;
3079 }
3080 
3081 static void report_published(jdns_session_t *s, published_item_t *pub)
3082 {
3083     jdns_event_t *event;
3084     jdns_string_t *str;
3085 
3086     str = _make_printable_cstr((char *)pub->qname);
3087     _debug_line(s, "published name %s for type %d", str->data, pub->qtype);
3088     jdns_string_delete(str);
3089 
3090     event = jdns_event_new();
3091     event->type = JDNS_EVENT_PUBLISH;
3092     event->id = pub->id;
3093     event->status = JDNS_STATUS_SUCCESS;
3094     _append_event(s, event);
3095 }
3096 
3097 int _multicast_publish(jdns_session_t *s, int mode, const jdns_rr_t *rr)
3098 {
3099     mdnsdr r;
3100     published_item_t *pub;
3101     int next_id;
3102     jdns_event_t *event;
3103     int n;
3104 
3105     r = 0;
3106     next_id = get_next_req_id(s);
3107 
3108     /* see if we have an item with this name+type combination already */
3109     pub = 0;
3110     for(n = 0; n < s->published->count; ++n)
3111     {
3112         published_item_t *i = (published_item_t *)s->published->item[n];
3113         if(i->qtype == rr->type && jdns_domain_cmp(i->qname, rr->owner))
3114         {
3115             pub = i;
3116             break;
3117         }
3118     }
3119     if(pub)
3120         goto error;
3121 
3122     if(!jdns_rr_verify(rr))
3123         goto error;
3124 
3125     if(mode == JDNS_PUBLISH_UNIQUE)
3126         r = mdnsd_unique(s->mdns, (char *)rr->owner, rr->type, rr->ttl, _multicast_pubresult, s);
3127     else
3128         r = mdnsd_shared(s->mdns, (char *)rr->owner, rr->type, rr->ttl);
3129 
3130     if(!_publish_applyrr(s, r, rr))
3131         goto error;
3132 
3133     pub = published_item_new();
3134     pub->id = next_id;
3135     pub->mode = mode;
3136     pub->qname = _ustrdup(rr->owner);
3137     pub->qtype = rr->type;
3138     pub->rec = r;
3139     pub->rr = jdns_rr_copy(rr);
3140     list_insert(s->published, pub, -1);
3141 
3142     /* mdnsd doesn't report publish events for shared, so do that here */
3143     if(mode == JDNS_PUBLISH_SHARED)
3144         report_published(s, pub);
3145 
3146     return pub->id;
3147 
3148 error:
3149     _debug_line(s, "attempt to publish record, malformed, unsupported, or duplicate type");
3150 
3151     if(r)
3152     {
3153         /* don't publish */
3154         mdnsd_done(s->mdns, r);
3155     }
3156 
3157     /* send an error to the app */
3158     event = jdns_event_new();
3159     event->type = JDNS_EVENT_PUBLISH;
3160     event->id = next_id;
3161     event->status = JDNS_STATUS_ERROR;
3162     _append_event_and_hold_id(s, event);
3163 
3164     return next_id;
3165 }
3166 
3167 void _multicast_update_publish(jdns_session_t *s, int id, const jdns_rr_t *rr)
3168 {
3169     mdnsdr r;
3170     published_item_t *pub;
3171     int qtype;
3172     int n;
3173 
3174     pub = 0;
3175     for(n = 0; n < s->published->count; ++n)
3176     {
3177         published_item_t *i = (published_item_t *)s->published->item[n];
3178         if(i->id == id)
3179         {
3180             pub = i;
3181             break;
3182         }
3183     }
3184     if(!pub)
3185         return;
3186 
3187     qtype = pub->qtype;
3188     r = pub->rec;
3189 
3190     /* expire existing record.  this is mostly needed for shared records */
3191     /*   since unique records already have the cache flush bit and that */
3192     /*   should achieve the same result.  however, since Apple expires */
3193     /*   unique records before updates, so will we. */
3194     mdnsd_done(s->mdns, r);
3195     if(pub->mode == JDNS_PUBLISH_UNIQUE)
3196         r = mdnsd_unique(s->mdns, (char *)pub->rr->owner, pub->rr->type, rr->ttl, _multicast_pubresult, s);
3197     else
3198         r = mdnsd_shared(s->mdns, (char *)pub->rr->owner, pub->rr->type, rr->ttl);
3199     pub->rec = r;
3200 
3201     if(!_publish_applyrr(s, r, rr))
3202     {
3203         _debug_line(s, "attempt to update_publish an unsupported type");
3204         return;
3205     }
3206 }
3207 
3208 void _multicast_cancel_publish(jdns_session_t *s, int id)
3209 {
3210     int n;
3211     for(n = 0; n < s->published->count; ++n)
3212     {
3213         published_item_t *i = (published_item_t *)s->published->item[n];
3214         if(i->id == id)
3215         {
3216             mdnsd_done(s->mdns, i->rec);
3217             list_remove(s->published, i);
3218             break;
3219         }
3220     }
3221 }
3222 
3223 void _multicast_flush(jdns_session_t *s)
3224 {
3225     int n;
3226 
3227     /* to flush, we make like our queries and published items are all new. */
3228     /* we'll do this by destroying/creating the mdnsd object again (so it */
3229     /* is fresh) and then reapply all queries and published items to it. */
3230 
3231     /* start over with mdnsd */
3232     mdnsd_free(s->mdns);
3233     s->mdns = mdnsd_new(0x0001, 1000, s->port, _callback_time_now, _callback_rand_int, s);
3234 
3235     /* attempt to publish again */
3236     for(n = 0; n < s->published->count; ++n)
3237     {
3238         published_item_t *i;
3239         mdnsdr r;
3240 
3241         i = (published_item_t *)s->published->item[n];
3242         if(i->mode == JDNS_PUBLISH_UNIQUE)
3243             r = mdnsd_unique(s->mdns, (char *)i->rr->owner, i->rr->type, i->rr->ttl, _multicast_pubresult, s);
3244         else
3245             r = mdnsd_shared(s->mdns, (char *)i->rr->owner, i->rr->type, i->rr->ttl);
3246         _publish_applyrr(s, r, i->rr);
3247         i->rec = r;
3248     }
3249 
3250     /* restore the queries */
3251     for(n = 0; n < s->queries->count; ++n)
3252     {
3253         query_t *q = (query_t *)s->queries->item[n];
3254 
3255         /* issue the query */
3256         mdnsd_query(s->mdns, (char *)q->qname, q->qtype, _multicast_query_ans, s);
3257     }
3258 }
3259 
3260 int jdns_step_multicast(jdns_session_t *s, int now)
3261 {
3262     int need_read, need_write, smallest_time;
3263     struct mytimeval *tv;
3264     jdns_packet_t *packet;
3265     int flags;
3266 
3267     /* not used */
3268     (void)now;
3269 
3270     need_read = 0;
3271     need_write = 0;
3272 
3273     if(s->shutdown == 1)
3274         mdnsd_shutdown(s->mdns);
3275 
3276     while(1)
3277     {
3278         jdns_address_t *addr;
3279         unsigned short int port;
3280         int ret;
3281         unsigned char *buf;
3282         int buf_len;
3283 
3284         if(!mdnsd_out(s->mdns, &packet, &addr, &port))
3285             break;
3286 
3287         if(!s->handle_writable)
3288         {
3289             need_write = 1;
3290             jdns_address_delete(addr);
3291             break;
3292         }
3293 
3294         if(!jdns_packet_export(packet, JDNS_UDP_MUL_OUT_MAX))
3295         {
3296             _debug_line(s, "outgoing packet export error, not sending");
3297             jdns_packet_delete(packet);
3298             continue;
3299         }
3300 
3301         buf = packet->raw_data;
3302         buf_len = packet->raw_size;
3303 
3304         /* multicast */
3305         if(!addr)
3306         {
3307             addr = jdns_address_copy(s->maddr);
3308             port = s->port;
3309         }
3310 
3311         _debug_line(s, "SEND %s:%d (size=%d)", addr->c_str, port, buf_len);
3312         _print_hexdump(s, buf, buf_len);
3313 
3314         ret = s->cb.udp_write(s, s->cb.app, s->handle, addr, port, buf, buf_len);
3315 
3316         jdns_address_delete(addr);
3317         jdns_packet_delete(packet);
3318 
3319         /* if we can't write the packet, oh well */
3320         if(ret == 0)
3321         {
3322             s->handle_writable = 0;
3323             need_write = 1;
3324             break;
3325         }
3326     }
3327 
3328     if(s->shutdown == 1)
3329     {
3330         jdns_event_t *event = jdns_event_new();
3331         event->type = JDNS_EVENT_SHUTDOWN;
3332         _append_event(s, event);
3333         s->shutdown = 2;
3334         return 0;
3335     }
3336 
3337     /* let's always ask for reads, just so the user doesn't have to */
3338     /*  worry about what should happen to incoming packets otherwise */
3339     need_read = 1;
3340 
3341     if(s->handle_readable)
3342     {
3343         while(1)
3344         {
3345             unsigned char buf[JDNS_UDP_MUL_IN_MAX];
3346             int bufsize = JDNS_UDP_MUL_IN_MAX;
3347             int ret;
3348             jdns_address_t *addr;
3349             int port;
3350             jdns_response_t *r;
3351 
3352             addr = jdns_address_new();
3353             ret = s->cb.udp_read(s, s->cb.app, s->handle, addr, &port, buf, &bufsize);
3354 
3355             /* no packet? */
3356             if(ret == 0)
3357             {
3358                 s->handle_readable = 0;
3359                 jdns_address_delete(addr);
3360                 break;
3361             }
3362 
3363             _debug_line(s, "RECV %s:%d (size=%d)", addr->c_str, port, bufsize);
3364             _print_hexdump(s, buf, bufsize);
3365 
3366             if(!jdns_packet_import(&packet, buf, bufsize))
3367             {
3368                 _debug_line(s, "error parsing packet / too large");
3369 
3370                 jdns_address_delete(addr);
3371                 continue;
3372             }
3373 
3374             _print_packet(s, packet);
3375 
3376             r = _packet2response(packet, 0, 0, 0x7fff);
3377             _print_records(s, r, 0);
3378 
3379             mdnsd_in(s->mdns, packet, r, addr, (unsigned short)port);
3380 
3381             jdns_address_delete(addr);
3382             jdns_packet_delete(packet);
3383             jdns_response_delete(r);
3384         }
3385     }
3386 
3387     tv = mdnsd_sleep(s->mdns);
3388     smallest_time = tv->tv_sec * 1000 + tv->tv_usec / 1000;
3389 
3390     flags = 0;
3391     if(smallest_time != -1)
3392     {
3393         flags |= JDNS_STEP_TIMER;
3394         s->next_timer = smallest_time;
3395 
3396         /* offset it a little bit, so that the user doesn't call */
3397         /*  us too early, resulting in a no-op and another timer */
3398         /*  of 1 millisecond. */
3399         s->next_timer += 2;
3400     }
3401     if(need_read || need_write)
3402         flags |= JDNS_STEP_HANDLE;
3403     return flags;
3404 }