]> git.meshlink.io Git - catta/blobdiff - server.c
some preliminary work for adding legacy unicast and unicast response support
[catta] / server.c
index b02fdad76736cafab5b4eaa41bf41bb827ec5dc5..dff3968e55d4d7e7a87347fcf4d6f9a69dd0a3f8 100644 (file)
--- a/server.c
+++ b/server.c
@@ -77,7 +77,7 @@ static void cleanup_dead(flxServer *s) {
     }
 }
 
-static void handle_query_key(flxServer *s, flxKey *k, flxInterface *i, const flxAddress *a) {
+static void handle_query_key(flxServer *s, flxKey *k, flxInterface *i, const flxAddress *a, guint16 port, gboolean legacy_unicast, gboolean unicast_response) {
     flxEntry *e;
     gchar *txt;
     
@@ -112,13 +112,22 @@ static void withdraw_entry(flxServer *s, flxEntry *e) {
     g_assert(s);
     g_assert(e);
 
-    e->dead = TRUE;
-    s->need_entry_cleanup = TRUE;
-
-    flx_goodbye_entry(s, e, FALSE);
     
-    if (e->group)
-        flx_entry_group_run_callback(e->group, FLX_ENTRY_GROUP_COLLISION);
+    if (e->group) {
+        flxEntry *k;
+        
+        for (k = e->group->entries; k; k = k->by_group_next) {
+            flx_goodbye_entry(s, k, FALSE);
+            k->dead = TRUE;
+        }
+        
+        flx_entry_group_change_state(e->group, FLX_ENTRY_GROUP_COLLISION);
+    } else {
+        flx_goodbye_entry(s, e, FALSE);
+        e->dead = TRUE;
+    }
+
+    s->need_entry_cleanup = TRUE;
 }
 
 static void incoming_probe(flxServer *s, flxRecord *record, flxInterface *i) {
@@ -131,26 +140,30 @@ static void incoming_probe(flxServer *s, flxRecord *record, flxInterface *i) {
 
     t = flx_record_to_string(record);
 
+/*     g_message("PROBE: [%s]", t); */
+    
     for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
         n = e->by_key_next;
-        
-        if (e->dead || !flx_record_equal_no_ttl(record, e->record))
+
+        if (e->dead || flx_record_equal_no_ttl(record, e->record))
             continue;
 
         if (flx_entry_registering(s, e, i)) {
-            
-            if (flx_record_lexicographical_compare(record, e->record) > 0) {
+            gint cmp;
+
+            if ((cmp = flx_record_lexicographical_compare(record, e->record)) > 0) {
                 withdraw_entry(s, e);
                 g_message("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
-            } else
+            } else if (cmp < 0)
                 g_message("Recieved conflicting probe [%s]. Local host won.", t);
+
         }
     }
 
     g_free(t);
 }
 
-static void handle_query(flxServer *s, flxDnsPacket *p, flxInterface *i, const flxAddress *a) {
+static void handle_query(flxServer *s, flxDnsPacket *p, flxInterface *i, const flxAddress *a, guint16 port, gboolean legacy_unicast) {
     guint n;
     
     g_assert(s);
@@ -161,17 +174,18 @@ static void handle_query(flxServer *s, flxDnsPacket *p, flxInterface *i, const f
     /* Handle the questions */
     for (n = flx_dns_packet_get_field(p, FLX_DNS_FIELD_QDCOUNT); n > 0; n --) {
         flxKey *key;
+        gboolean unicast_response = FALSE;
 
-        if (!(key = flx_dns_packet_consume_key(p))) {
+        if (!(key = flx_dns_packet_consume_key(p, &unicast_response))) {
             g_warning("Packet too short (1)");
             return;
         }
 
-        handle_query_key(s, key, i, a);
+        handle_query_key(s, key, i, a, port, legacy_unicast, unicast_response);
         flx_key_unref(key);
     }
 
-    /* Known Answer Suppresion */
+    /* Known Answer Suppression */
     for (n = flx_dns_packet_get_field(p, FLX_DNS_FIELD_ANCOUNT); n > 0; n --) {
         flxRecord *record;
         gboolean unique = FALSE;
@@ -202,7 +216,7 @@ static void handle_query(flxServer *s, flxDnsPacket *p, flxInterface *i, const f
     }
 }
 
-static gboolean handle_conflict(flxServer *s, flxInterface *i, flxRecord *record, const flxAddress *a) {
+static gboolean handle_conflict(flxServer *s, flxInterface *i, flxRecord *record, gboolean unique, const flxAddress *a) {
     gboolean valid = TRUE;
     flxEntry *e, *n;
     gchar *t;
@@ -213,6 +227,8 @@ static gboolean handle_conflict(flxServer *s, flxInterface *i, flxRecord *record
 
     t = flx_record_to_string(record);
 
+/*     g_message("CHECKING FOR CONFLICT: [%s]", t); */
+
     for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
         n = e->by_key_next;
 
@@ -224,17 +240,20 @@ static gboolean handle_conflict(flxServer *s, flxInterface *i, flxRecord *record
             gboolean equal = flx_record_equal_no_ttl(record, e->record);
                 
             /* Check whether there is a unique record conflict */
-            if (!equal && (e->flags & FLX_ENTRY_UNIQUE)) {
+            if (!equal && ((e->flags & FLX_ENTRY_UNIQUE) || unique)) {
+                gint cmp;
                 
                 /* The lexicographically later data wins. */
-                if (flx_record_lexicographical_compare(record, e->record) > 0) {
-                    withdraw_entry(s, e);
+                if ((cmp = flx_record_lexicographical_compare(record, e->record)) > 0) {
                     g_message("Recieved conflicting record [%s]. Local host lost. Withdrawing.", t);
-                } else {
+                    withdraw_entry(s, e);
+                } else if (cmp < 0) {
                     /* Tell the other host that our entry is lexicographically later */
+
+                    g_message("Recieved conflicting record [%s]. Local host won. Refreshing.", t);
+
                     valid = FALSE;
                     flx_interface_post_response(i, a, e->record, e->flags & FLX_ENTRY_UNIQUE, TRUE);
-                    g_message("Recieved conflicting record [%s]. Local host won. Refreshing.", t);
                 }
                 
                 /* Check wheter there is a TTL conflict */
@@ -244,6 +263,18 @@ static gboolean handle_conflict(flxServer *s, flxInterface *i, flxRecord *record
                 flx_interface_post_response(i, a, e->record, e->flags & FLX_ENTRY_UNIQUE, TRUE);
                 g_message("Recieved record with bad TTL [%s]. Refreshing.", t);
             }
+            
+        } else if (flx_entry_registering(s, e, i)) {
+
+            if (!flx_record_equal_no_ttl(record, e->record) && ((e->flags & FLX_ENTRY_UNIQUE) || unique)) {
+
+                /* We are currently registering a matching record, but
+                 * someone else already claimed it, so let's
+                 * withdraw */
+                
+                g_message("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
+                withdraw_entry(s, e);
+            }
         }
     }
 
@@ -276,7 +307,7 @@ static void handle_response(flxServer *s, flxDnsPacket *p, flxInterface *i, cons
             g_message("Handling response: %s", txt = flx_record_to_string(record));
             g_free(txt);
             
-            if (handle_conflict(s, i, record, a)) {
+            if (handle_conflict(s, i, record, cache_flush, a)) {
                 flx_cache_update(i->cache, record, cache_flush, a);
                 flx_packet_scheduler_incoming_response(i->scheduler, record);
             }
@@ -289,36 +320,30 @@ static void handle_response(flxServer *s, flxDnsPacket *p, flxInterface *i, cons
 static void dispatch_packet(flxServer *s, flxDnsPacket *p, struct sockaddr *sa, gint iface, gint ttl) {
     flxInterface *i;
     flxAddress a;
+    guint16 port;
     
     g_assert(s);
     g_assert(p);
     g_assert(sa);
     g_assert(iface > 0);
 
-    g_message("new packet recieved.");
-
     if (!(i = flx_interface_monitor_get_interface(s->monitor, iface, sa->sa_family))) {
         g_warning("Recieved packet from invalid interface.");
         return;
     }
 
-    if (ttl != 255) {
-        g_warning("Recieved packet with invalid TTL on interface '%s.%i'.", i->hardware->name, i->protocol);
-        if (!s->ignore_bad_ttl)
-            return;
-    }
+    g_message("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol);
 
     if (sa->sa_family == AF_INET6) {
-        static const unsigned char ipv4_in_ipv6[] = {
+        static const guint8 ipv4_in_ipv6[] = {
             0x00, 0x00, 0x00, 0x00,
             0x00, 0x00, 0x00, 0x00,
             0xFF, 0xFF, 0xFF, 0xFF };
 
-        if (memcmp(((struct sockaddr_in6*) sa)->sin6_addr.s6_addr, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0) {
+        /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
 
-            /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
+        if (memcmp(((struct sockaddr_in6*) sa)->sin6_addr.s6_addr, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0)
             return;
-        }
     }
 
     if (flx_dns_packet_check_valid(p) < 0) {
@@ -326,19 +351,46 @@ static void dispatch_packet(flxServer *s, flxDnsPacket *p, struct sockaddr *sa,
         return;
     }
 
+    port = flx_port_from_sockaddr(sa);
     flx_address_from_sockaddr(sa, &a);
 
     if (flx_dns_packet_is_query(p)) {
+        gboolean legacy_unicast = FALSE;
 
         if (flx_dns_packet_get_field(p, FLX_DNS_FIELD_QDCOUNT) == 0 ||
             flx_dns_packet_get_field(p, FLX_DNS_FIELD_ARCOUNT) != 0) {
             g_warning("Invalid query packet.");
             return;
         }
-                
-        handle_query(s, p, i, &a);    
+
+        if (port != FLX_MDNS_PORT) {
+            /* Legacy Unicast */
+
+            if ((flx_dns_packet_get_field(p, FLX_DNS_FIELD_ANCOUNT) != 0 ||
+                 flx_dns_packet_get_field(p, FLX_DNS_FIELD_NSCOUNT) != 0)) {
+                g_warning("Invalid legacy unicast query packet.");
+                return;
+            }
+        
+            legacy_unicast = TRUE;
+        }
+
+        handle_query(s, p, i, &a, port, legacy_unicast);
+        
         g_message("Handled query");
     } else {
+
+        if (port != FLX_MDNS_PORT) {
+            g_warning("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
+            return;
+        }
+
+        if (ttl != 255) {
+            g_warning("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
+            if (!s->ignore_bad_ttl)
+                return;
+        }
+
         if (flx_dns_packet_get_field(p, FLX_DNS_FIELD_QDCOUNT) != 0 ||
             flx_dns_packet_get_field(p, FLX_DNS_FIELD_ANCOUNT) == 0 ||
             flx_dns_packet_get_field(p, FLX_DNS_FIELD_NSCOUNT) != 0) {
@@ -420,7 +472,7 @@ static void add_default_entries(flxServer *s) {
     uname(&utsname);
     r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
     r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
-    flx_server_add(s, NULL, 0, AF_UNSPEC, FLX_ENTRY_UNIQUE | FLX_ENTRY_NOANNOUNCE | FLX_ENTRY_NOPROBE, r);
+    flx_server_add(s, NULL, 0, AF_UNSPEC, FLX_ENTRY_UNIQUE, r);
     flx_record_unref(r);
 
     /* Add localhost entries */
@@ -450,7 +502,7 @@ flxServer *flx_server_new(GMainContext *c) {
     s->need_entry_cleanup = s->need_group_cleanup = FALSE;
     
     s->fd_ipv4 = flx_open_socket_ipv4();
-    s->fd_ipv6 = -1 /*flx_open_socket_ipv6() */; 
+    s->fd_ipv6 = flx_open_socket_ipv6();
     
     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
         g_critical("Failed to create IP sockets.\n");
@@ -686,25 +738,39 @@ void flx_server_add_address(
     g_free(n);
 }
 
-void flx_server_add_text_va(
+void flx_server_add_text_strlst(
     flxServer *s,
     flxEntryGroup *g,
     gint interface,
     guchar protocol,
     flxEntryFlags flags,
     const gchar *name,
-    va_list va) {
+    flxStringList *strlst) {
 
     flxRecord *r;
     
     g_assert(s);
     
     r = flx_record_new_full(name ? name : s->hostname, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_TXT);
-    r->data.txt.string_list = flx_string_list_new_va(va);
+    r->data.txt.string_list = strlst;
     flx_server_add(s, g, interface, protocol, flags, r);
     flx_record_unref(r);
 }
 
+void flx_server_add_text_va(
+    flxServer *s,
+    flxEntryGroup *g,
+    gint interface,
+    guchar protocol,
+    flxEntryFlags flags,
+    const gchar *name,
+    va_list va) {
+    
+    g_assert(s);
+
+    flx_server_add_text_strlst(s, g, interface, protocol, flags, name, flx_string_list_new_va(va));
+}
+
 void flx_server_add_text(
     flxServer *s,
     flxEntryGroup *g,
@@ -745,8 +811,7 @@ static void escape_service_name(gchar *d, guint size, const gchar *s) {
     *(d++) = 0;
 }
 
-
-void flx_server_add_service_va(
+void flx_server_add_service_strlst(
     flxServer *s,
     flxEntryGroup *g,
     gint interface,
@@ -756,7 +821,7 @@ void flx_server_add_service_va(
     const gchar *domain,
     const gchar *host,
     guint16 port,
-    va_list va) {
+    flxStringList *strlst) {
 
     gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
     flxRecord *r;
@@ -779,20 +844,39 @@ void flx_server_add_service_va(
     snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
     snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
     
-    flx_server_add_ptr(s, g, interface, protocol, FALSE, ptr_name, svc_name);
+    flx_server_add_ptr(s, g, interface, protocol, FLX_ENTRY_NULL, ptr_name, svc_name);
 
     r = flx_record_new_full(svc_name, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_SRV);
     r->data.srv.priority = 0;
     r->data.srv.weight = 0;
     r->data.srv.port = port;
     r->data.srv.name = flx_normalize_name(host);
-    flx_server_add(s, g, interface, protocol, TRUE, r);
+    flx_server_add(s, g, interface, protocol, FLX_ENTRY_UNIQUE, r);
     flx_record_unref(r);
 
-    flx_server_add_text_va(s, g, interface, protocol, FALSE, svc_name, va);
+    flx_server_add_text_strlst(s, g, interface, protocol, FLX_ENTRY_UNIQUE, svc_name, strlst);
 
     snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
-    flx_server_add_ptr(s, g, interface, protocol, FALSE, enum_ptr, ptr_name);
+    flx_server_add_ptr(s, g, interface, protocol, FLX_ENTRY_NULL, enum_ptr, ptr_name);
+}
+
+void flx_server_add_service_va(
+    flxServer *s,
+    flxEntryGroup *g,
+    gint interface,
+    guchar protocol,
+    const gchar *type,
+    const gchar *name,
+    const gchar *domain,
+    const gchar *host,
+    guint16 port,
+    va_list va){
+
+    g_assert(s);
+    g_assert(type);
+    g_assert(name);
+
+    flx_server_add_service(s, g, interface, protocol, type, name, domain, host, port, flx_string_list_new_va(va));
 }
 
 void flx_server_add_service(
@@ -862,18 +946,15 @@ void flx_server_post_response(flxServer *s, gint interface, guchar protocol, flx
     flx_interface_monitor_walk(s->monitor, interface, protocol, post_response_callback, &tmpdata);
 }
 
-void flx_entry_group_run_callback(flxEntryGroup *g, flxEntryGroupStatus status) {
+void flx_entry_group_change_state(flxEntryGroup *g, flxEntryGroupState state) {
     g_assert(g);
 
+    g->state = state;
+    
     if (g->callback) {
-        g->callback(g->server, g, status, g->userdata);
+        g->callback(g->server, g, state, g->userdata);
         return;
     }
-
-    if (status == FLX_ENTRY_GROUP_COLLISION)
-        flx_entry_group_free(g);
-
-    /* Ignore the rest */
 }
 
 flxEntryGroup *flx_entry_group_new(flxServer *s, flxEntryGroupCallback callback, gpointer userdata) {
@@ -886,7 +967,7 @@ flxEntryGroup *flx_entry_group_new(flxServer *s, flxEntryGroupCallback callback,
     g->callback = callback;
     g->userdata = userdata;
     g->dead = FALSE;
-    g->status = FLX_ENTRY_GROUP_UNCOMMITED;
+    g->state = FLX_ENTRY_GROUP_UNCOMMITED;
     g->n_probing = 0;
     FLX_LLIST_HEAD_INIT(flxEntry, g->entries);
 
@@ -908,10 +989,10 @@ void flx_entry_group_commit(flxEntryGroup *g) {
     g_assert(g);
     g_assert(!g->dead);
 
-    if (g->status != FLX_ENTRY_GROUP_UNCOMMITED)
+    if (g->state != FLX_ENTRY_GROUP_UNCOMMITED)
         return;
 
-    flx_entry_group_run_callback(g, g->status = FLX_ENTRY_GROUP_REGISTERING);
+    flx_entry_group_change_state(g, FLX_ENTRY_GROUP_REGISTERING);
     flx_announce_group(g->server, g);
     flx_entry_group_check_probed(g, FALSE);
 }
@@ -921,13 +1002,13 @@ gboolean flx_entry_commited(flxEntry *e) {
     g_assert(!e->dead);
 
     return !e->group ||
-        e->group->status == FLX_ENTRY_GROUP_REGISTERING ||
-        e->group->status == FLX_ENTRY_GROUP_ESTABLISHED;
+        e->group->state == FLX_ENTRY_GROUP_REGISTERING ||
+        e->group->state == FLX_ENTRY_GROUP_ESTABLISHED;
 }
 
-flxEntryGroupStatus flx_entry_group_get_status(flxEntryGroup *g) {
+flxEntryGroupState flx_entry_group_get_state(flxEntryGroup *g) {
     g_assert(g);
     g_assert(!g->dead);
 
-    return g->status;
+    return g->state;
 }