mdns.c

Go to the documentation of this file.
00001 /*##############################################################################
00002 
00003 nIP - nano IP stack
00004 
00005 Copyright (C) 2005 -
00006 Andreas Dittrich, dittrich@informatik.hu-berlin.de
00007 Jon Kowal, kowal@informatik.hu-berlin.de
00008 
00009 This program is free software; you can redistribute it and/or
00010 modify it under the terms of the GNU General Public License
00011 as published by the Free Software Foundation; either version 2
00012 of the License, or (at your option) any later version.
00013 
00014 This program is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00017 GNU General Public License for more details.
00018 
00019 You should have received a copy of the GNU General Public License
00020 along with this program; if not, write to the Free Software
00021 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00022 
00023 ##############################################################################*/
00024 /** @file mdns.c
00025  *
00026  * Standard compliant, minimal implementation of Multiast DNS.
00027  * @todo Make it standard compliant. ;) (timing, conflicts, local queries...)
00028  *
00029  */
00030 
00031 #include "nip_init.h"
00032 #include "app/mdns.h"
00033 #include "dispatcher.h"
00034 #include "net/udp.h"
00035 #include "app/ports.h"
00036 #include "inet.h"
00037 
00038 #if ( NIP_MDNS_ENABLE == 1 )
00039 
00040 
00041 
00042 struct nip_mdns_check    nip_mdns;
00043 
00044 nip_net_if_id_t          nip_mdns_if_id = NIP_NET_NO_IF;
00045 nip_udp_sock_id_t        nip_mdns_sock  = NIP_UDP_NO_SOCKET;
00046 nip_mdns_cache_id_t      nip_mdns_hostname = NIP_MDNS_NO_CACHE_ID;
00047 nip_mdns_cache_id_t      nip_mdns_host_ptr = NIP_MDNS_NO_CACHE_ID;
00048 nip_mdns_namestring_id_t nip_mdns_domain   = NIP_MDNS_NO_NAMESTRING;
00049 nip_mem_handle_t nip_mdns_cs      = NIP_MEM_NULL; /**< cache subscriptions */
00050 nip_mem_ptr_t    nip_mdns_cache   = {NIP_MEM_NULL,NULL}; /**< MDNS cache */
00051 nip_mem_handle_t nip_mdns_names   = NIP_MEM_NULL; /**< MDNS name strings */
00052 nip_mem_ptr_t    nip_mdns_queries = {NIP_MEM_NULL,NULL}; /**< MDNS queries */
00053 
00054 void nip_mdns_register_hostname( struct nip_dns_string *name );
00055 void nip_mdns_disp_check_queries( void );
00056 nip_error_t nip_mdns_parse_packet( nip_udp_buf_type_t udp_buffer,
00057 nip_mdns_packet_task_t task, nip_mem_ptr_t *p, nip_mdns_qa_offset_t section );
00058 
00059 /** initialize mdns memory
00060  *
00061  * @return NIP_E_OK or NIP_E_OUT_OF_MEMORY
00062  */
00063 nip_error_t nip_mdns_init( void )
00064 {
00065         // use union to save memory
00066         union
00067         {
00068                 struct nip_udp_sock_addr addr;
00069                 // The decision on how to initialize strings is tough, as it either eats up
00070                 // RAM or program memory, both of which we don't have much. Currently we
00071                 // follow the pro-RAM con-Program-Memory approach, that is to initialize all
00072                 // chars separately, to allocate the string locally.
00073                 /// @todo check on moving fixed strings to the EEPROM and reading from there.
00074                 uint8_t  local[6];
00075                 uint8_t  avr[4];
00076                 uint8_t  mc_addr[4];
00077         } tmp;
00078         nip_mem_ptr_t name_ptr;
00079 
00080         // init buffer space
00081         if ( nip_mdns_cs    == NIP_MEM_NULL )
00082                 nip_mdns_cs    = nip_mem_alloc( 0, 0, 0, NULL );
00083         if ( nip_mdns_cache.id == NIP_MEM_NULL )
00084                 nip_mdns_cache.id = nip_mem_alloc( 0, 0, 0, NULL );
00085         if ( nip_mdns_names == NIP_MEM_NULL )
00086                 nip_mdns_names = nip_mem_alloc( 0, 0, 0, NULL );
00087         if ( nip_mdns_queries.id == NIP_MEM_NULL )
00088                 nip_mdns_queries.id = nip_mem_alloc( 0, 0, 0, NULL );
00089 
00090         // register names
00091         if ( nip_mdns_domain == NIP_MDNS_NO_NAMESTRING )
00092         {
00093                 tmp.local[0] = 5;
00094                 tmp.local[1] = 'l';
00095                 tmp.local[2] = 'o';
00096                 tmp.local[3] = 'c';
00097                 tmp.local[4] = 'a';
00098                 tmp.local[5] = 'l';
00099                 name_ptr.id  = NIP_MEM_NULL;
00100                 name_ptr.ptr = tmp.local;
00101                 nip_mdns_domain = NIP_MDNS_REG_STRING( &name_ptr );
00102         }
00103 
00104         if ( nip_mdns_domain   == NIP_MDNS_NO_NAMESTRING
00105           || nip_mdns_cache.id == NIP_MEM_NULL
00106           || nip_mdns_cs       == NIP_MEM_NULL )
00107         {
00108                 return NIP_E_OUT_OF_MEMORY;
00109         }
00110 
00111         // Don't continue if MDNS is down
00112         if ( nip_mdns.stat == NIP_MDNS_STAT_DOWN )
00113                 return NIP_E_NOT_CONFIGURED;
00114 
00115         // register hostname -- if necessary
00116         if ( nip_mdns_hostname == NIP_MDNS_NO_CACHE_ID )
00117         {
00118                 tmp.avr[0] = 3;
00119 //              tmp.avr[1] = 0xC3; // Unicode for Ä
00120 //              tmp.avr[2] = 0x84; // Unicode for Ä
00121                 tmp.avr[1] = 'A';
00122                 tmp.avr[2] = 'V';
00123                 tmp.avr[3] = 'R';
00124 
00125                 /// @todo get hostname from EEPROM?
00126                 nip_mdns_register_hostname( (struct nip_dns_string *)tmp.avr );
00127                 if ( nip_error != NIP_E_OK )
00128                         return nip_error;
00129         }
00130 
00131         // init UDP socket
00132         if ( nip_mdns_sock == NIP_UDP_NO_SOCKET && nip_mdns_if_id != NIP_NET_NO_IF )
00133         {
00134                 // only listen on configured interfacew
00135                 tmp.addr.port = NIP_UDP_PORT_MDNS;
00136                 nip_memcpy( tmp.addr.ip, NIP_IP_ADDR( nip_mdns_if_id ), 4 );
00137 /*              tmp.addr.ip[0] = 0;
00138                 tmp.addr.ip[1] = 0;
00139                 tmp.addr.ip[2] = 0;
00140                 tmp.addr.ip[3] = 0;*/
00141                 nip_mdns_sock = nip_udp_socket(
00142                         &tmp.addr,
00143                         NIP_UDP_SOCK_FLG_LISTENING | NIP_UDP_SOCK_FLG_NON_BLOCK
00144                 );
00145                 if ( nip_mdns_sock == NIP_UDP_NO_SOCKET )
00146                         return nip_error;
00147         }
00148 
00149         // Register Multicast Group
00150         tmp.mc_addr[0]=NIP_MDNS_ADDR0;
00151         tmp.mc_addr[1]=NIP_MDNS_ADDR1;
00152         tmp.mc_addr[2]=NIP_MDNS_ADDR2;
00153         tmp.mc_addr[3]=NIP_MDNS_ADDR3;
00154         return nip_ip_join_host_group( nip_mdns_if_id, tmp.mc_addr );
00155 }
00156 
00157 
00158 /** Perform dns name parsing-operation, depending on task parameter.
00159  * @param task
00160  *  - NIP_MDNS_NTASK_FIND   check the local name list and fill the given
00161  *    name pointer (if not NULL) if the search was sucessfull.
00162  *  - NIP_MDNS_NTASK_WRITE  write
00163  * @param name  pointer to dns name structure
00164  * @param buf   pointer to name buffer
00165  * @param size  size of buffer. May be 0 for ..WRITE-task, if memory block is
00166  *              being used.
00167  * operation will parse the name from
00168  */
00169 // nip_error_t nip_mdns_name_task( nip_mdns_name_task_t task, struct nip_mdns_name *name,
00170 // nip_mem_ptr_t *buf, nip_mem_size_t size )
00171 // {
00172 //      nip_mem_size_t  listsize;
00173 //      nip_error_t     res = NIP_E_OK;
00174 //
00175 //      listsize = nip_mem_buf_used( buf->id );
00176 //
00177 //      /// @todo call nip_mdns_register_name depending on task
00178 //
00179 //      return res;
00180 // }
00181 
00182 /** Compare UTF-8 namestrings, case insensitively, according to mDNS draft
00183  * chapter "18. Multicast DNS Character Set".
00184  *
00185  * @note n1 or n2 MUST NOT be NULL.
00186  *
00187  * @return NIP_E_OK or NIP_E_NO_MATCH
00188  */
00189 nip_error_t nip_mdns_name_cmp( struct nip_dns_string *n1, struct nip_dns_string *n2)
00190 {
00191         uint8_t pos;
00192         uint8_t utf8_head;
00193         uint8_t c1, c2;
00194 
00195         if ( n1->len == n2->len )
00196         {
00197                 pos = 0;
00198                 utf8_head = 0;
00199                 do
00200                 {
00201                         c1 =n1->s[pos]; c2 = n2->s[pos];
00202 
00203                         // check for UTF-8 multibyte-characters
00204                         if ( (utf8_head & 0x80) == 0 )
00205                                 utf8_head   = c1;
00206                         else
00207                                 utf8_head <<= 1;
00208 
00209                         // compare characters a-zA-Z case insensitively (convert to lower case)
00210                         // only exception: UTF-8 parts.
00211                         if ( (utf8_head & 0x80) == 0 )
00212                         {
00213                                 if ( c1 >= 'A' && c1 <='Z' ) c1 += 'a'-'A';
00214                                 if ( c2 >= 'A' && c2 <='Z' ) c2 += 'a'-'A';
00215                         }
00216 
00217                         if ( c1 != c2 )
00218                                 goto abort_no_match;
00219 
00220                 } while ( ++pos < n1->len );
00221                 return NIP_E_OK;
00222         }
00223 
00224         abort_no_match:
00225         return NIP_E_NO_MATCH;
00226 }
00227 
00228 /** Registers new MDNS namestring or retrieves already registered entry for that
00229  * name. The namestring's usage counter will be increased for every call of
00230  * nip_mdns_register_name(). This function may also be used to unregister
00231  * a previously registered namestring by providing a negative usage count.
00232  * @param id    if set, the usage counter for that namestring will be increased
00233  *              and name and len parameter will be ignored.
00234  * @param cnt   if > 1, id will be treated as array of namestrings to be
00235  *              registered. Must be 0 if id is not set.
00236  * @param name_ptr  pointer to buffer holding the name. If id is set, the found
00237  *              name(s) will be appended to that buffer, preceded by their
00238  *              length. The pointer has to point to a nip_dns_string structure.
00239  * @param len   length of name
00240  * @param usg   number of string usages to register (negative to unregister)
00241  * @return ID of namestring or NIP_MDNS_NO_NAMESTRING on error.
00242  *
00243  * @note If usg is 0 a simple lookup of the given namestring will be performed
00244  * and -- if found -- it's Id be returned.
00245  *
00246  * @note The function's result depends very much on the correct combination of
00247  * the various parameters. Those are chosen to be as minimal as possible, which
00248  * will most probably lead to erroneous usage of the function. You SHOULD NOT
00249  * call this function directly but use the provided macros, which, depending on
00250  * the provided function, require only a subset of the parameters to be set.
00251  *  - NIP_MDNS_REG_STRING
00252  *  - NIP_MDNS_REG_NAME
00253  *  - NIP_MDNS_NAME_USE
00254  *  - NIP_MDNS_UNREG_NAME
00255  *  - NIP_MDNS_CHECK_NAME
00256  *  - NIP_MDNS_WRITE_NAME
00257  */
00258 nip_mdns_namestring_id_t nip_mdns_register_name( nip_mdns_namestring_id_t *id,
00259 uint8_t cnt, nip_mem_ptr_t *name_ptr, nip_mdns_namestring_usg_t usg )
00260 {
00261         nip_mdns_namestring_id_t   res = NIP_MDNS_NO_NAMESTRING;
00262         nip_mdns_namestring_id_t   new_id = 1;
00263         nip_mem_ptr_t              current;
00264         nip_mem_ptr_t              name_src;
00265         nip_error_t                err;
00266         nip_mem_size_t             len;
00267         nip_mem_size_t             listsize;
00268         struct nip_dns_string      *name = NULL;
00269         uint8_t                    i = 0;
00270         struct nip_mdns_namestring *l; // pointer to list item and list end
00271 
00272         listsize = nip_mem_buf_used( nip_mdns_names );
00273 
00274         do
00275         {
00276                 current.id  = nip_mdns_names;
00277                 current.ptr = (void*)0;
00278 
00279                 // For all remaining operations a name string is required
00280                 if ( name == NULL && (name = nip_mem_ptr( name_ptr )) == NULL && id == NULL )
00281                 {
00282                         new_id = NIP_MDNS_NO_NAMESTRING;
00283                         break;
00284                 }
00285 
00286                 // search for name in list
00287                 while ( (l = nip_mem_ptr( &current ) ) != NULL )
00288                 {
00289                         // Search Namestring by ID
00290                         if ( id != NULL && ( name_ptr == NULL || usg == 0 ) )
00291                         {
00292                                 new_id = NIP_MDNS_NO_NAMESTRING;
00293                                 if ( id[i] == NIP_MDNS_NO_NAMESTRING )
00294                                         i = cnt;
00295                                 else if ( id[i] == l->id )
00296                                 {
00297                                         l->usg  += usg;
00298                                         name_src.id  = current.id;
00299                                         name_src.ptr = current.ptr + NIP_MDNS_STRINGOFF;
00300                                         len      = l->str.len;
00301                                         // release memory pointers so nip_mem_insert() can work
00302                                         // unbothered by locked memory blocks.
00303                                         l = NULL;
00304                                         nip_mem_release_block( current.id );
00305                                         nip_mem_release_ptr( name_ptr );
00306 
00307                                         // insert length/name to given buffer
00308                                         nip_mem_insert( name_ptr, &name_src, len+1 );
00309 
00310                                 }
00311                                 else
00312                                 {
00313                                         // check next name
00314                                         goto next;
00315                                 }
00316                                 // Reached end of name? -> Insert 0-Character
00317                                 // The user has put cnt==NIP_MDNS_MAX_NAME_DEPTH, which means he
00318                                 // wants to print an entire name, which will always be 0-padded.
00319                                 if ( i >= NIP_MDNS_MAX_NAME_DEPTH-1 )
00320                                 {
00321                                         name_src.id  = NIP_MEM_NULL;
00322                                         name_src.ptr = NULL;
00323                                         nip_mem_insert( name_ptr, &name_src, 1 );
00324                                 }
00325 
00326                                 break;
00327                         }
00328 
00329                         // compare names
00330                         if ( nip_mdns_name_cmp( &(l->str), name ) == NIP_E_OK )
00331                         {
00332                                 // found name -> increase usage pointer and return ID
00333                                 new_id = NIP_MDNS_NO_NAMESTRING;
00334                                 res     = l->id;
00335                                 l->usg += usg;
00336                                 break;
00337                         }
00338 
00339                         // check free ID
00340                         if ( new_id == l->id )
00341                         {
00342                                 new_id++;
00343                                 current.ptr = (void*)0;
00344                                 goto release;
00345                         }
00346 
00347                         next:
00348                                 // advance pointer to next name
00349                                 current.ptr += sizeof( struct nip_mdns_namestring ) + l->str.len;
00350 
00351                         release:
00352                                 nip_mem_release_block( current.id );
00353                                 l = NULL;
00354                 }
00355                 if ( l != NULL )
00356                         nip_mem_release_block( current.id );
00357 
00358                 // Add new String to list
00359                 if ( new_id != NIP_MDNS_NO_NAMESTRING && new_id < NIP_MDNS_MAX_NAMESTRINGS && usg != 0 )
00360                 {
00361                         len = name->len;
00362 
00363                         // release pointer temporarily, so it won't be blocking the following
00364                         // memory operation, which may include defragmentation.
00365                         nip_mem_release_ptr( name_ptr );
00366 
00367                         // End of name reached?
00368                         if ( len == 0 )
00369                         {
00370                                 id[i] = 0;
00371                                 goto quit;
00372                         }
00373 
00374 
00375                         // allocate memory for block
00376                         err = nip_mem_write( nip_mdns_names, NULL, sizeof( struct nip_mdns_namestring ) + len );
00377 
00378                         if ( err != NIP_E_OK )
00379                                 goto add_error;
00380 
00381                         // get new data pointers and make sure there's nothing wrong with them
00382                         name = nip_mem_ptr( name_ptr );
00383                         if ( name == NULL )
00384                                 goto add_error;
00385 
00386                         l = nip_mem_ptr( &current);
00387                         if ( l == NULL )
00388                                 goto add_error;
00389 
00390                         // fill header of new item
00391                         l->id  = new_id;
00392                         l->usg = usg;
00393                         l->str.len = name->len;
00394 
00395                         // copy name to list
00396                         nip_memcpy( l->str.s, name->s, name->len );
00397 
00398                         res = new_id;
00399                         if ( id != NULL )
00400                         {
00401                                 id[i] = new_id;
00402                         }
00403                         nip_mem_release_ptr( &current );
00404 
00405                         // forward name pointer
00406                         name_ptr->ptr += len+1;
00407                         name           = (struct nip_dns_string*)(((uint8_t*)name) + len+1);
00408 
00409                         continue;
00410 
00411                         add_error:
00412                                 // reset list size (delete all new items)
00413                                 nip_mem_set_used( nip_mdns_names, listsize );
00414                                 goto release_name;
00415                 }
00416         }
00417         while ( ++i < cnt );
00418 
00419 
00420         release_name:
00421 
00422         // release name pointer
00423         if ( name != NULL )
00424                 nip_mem_release_ptr( name_ptr );
00425 
00426         quit:
00427         return res;
00428 }
00429 
00430 /** Execute MDNS lookup query.
00431  * The query will be registered within the system and a query ID will be
00432  * returned. That query has to be ultimately closed by calling nip_mdns_close().
00433  * Answers to the query can be polled by calling nip_mdns_fetch_results().
00434  *
00435  * @param name pointer to DNS name
00436  * @param type DNS type
00437  *
00438  * @return query-ID or NIP_MDNS_NO_QUERY
00439  */
00440 nip_mdns_query_id_t nip_mdns_query( nip_mdns_query_type_t type,
00441 struct nip_mdns_name *rr_name, nip_mdns_type_t rr_type )
00442 {
00443         nip_mdns_query_id_t   res = NIP_MDNS_NO_QUERY;
00444         nip_mdns_query_id_t   new_id = 0;
00445         nip_mem_size_t        listsize;
00446         struct nip_mdns_query *l, *end; // pointer to list item and list end
00447         struct nip_mdns_query query;
00448 
00449         listsize = nip_mem_buf_used( nip_mdns_queries.id );
00450 
00451         // search for name in list
00452         l = nip_mem_obtain_ptr( nip_mdns_queries.id );
00453         if ( l == NULL )
00454         {
00455                 goto quit;
00456         }
00457 
00458         end = (void *)((uint8_t*)l + listsize );
00459         while ( l != end )
00460         {
00461                 // compare names.
00462                 if ( nip_memcmp( &l->rr_name, rr_name, sizeof( struct nip_mdns_name ) ) == 0 )
00463                 {
00464                         // Probes will only be registered once per name
00465                         if ( l->type == NIP_MDNS_QTYPE_PROBE )
00466                         {
00467                                 if ( type == NIP_MDNS_QTYPE_PROBE )
00468                                 {
00469                                         res  = (l->id & NIP_MDNS_QUERY_ID_MASK);
00470                                         break;
00471                                 }
00472                         }
00473 
00474                         // only register one remote instance per remote query
00475                         else if ( type == NIP_MDNS_QTYPE_REMOTE
00476                           || ( type == NIP_MDNS_QTYPE_LOCAL && l->type == NIP_MDNS_QTYPE_REMLOC))
00477                         {
00478                                 if ( l->type == NIP_MDNS_QTYPE_LOCAL )
00479                                         l->type = NIP_MDNS_QTYPE_REMLOC;
00480                                 res = (l->id & NIP_MDNS_QUERY_ID_MASK);
00481                                 break;
00482                         }
00483                 }
00484 
00485                 // check free ID
00486                 if ( (new_id & NIP_MDNS_QUERY_ID_MASK) == (l->id & NIP_MDNS_QUERY_ID_MASK) )
00487                 {
00488                         new_id++;
00489                         l = ( struct nip_mdns_query *)((uint8_t*)end - listsize );
00490                         continue;
00491                 }
00492 
00493                 // advance to next query
00494                 l = (void*) ((uint8_t *)l + sizeof( struct nip_mdns_query ));
00495         }
00496         nip_mem_release_block( nip_mdns_queries.id );
00497 
00498         if ( (res & NIP_MDNS_QUERY_ID_MASK) == NIP_MDNS_NO_QUERY )
00499         {
00500                 if ( (new_id & NIP_MDNS_QUERY_ID_MASK) < NIP_MDNS_MAX_QUERIES )
00501                 {
00502                         query.id       = new_id | NIP_MDNS_QUERY_ACTIVE;
00503                         query.type     = type;
00504                         query.rr_name  = *rr_name;
00505                         query.rr_type  = rr_type;
00506                         ///@todo set timer correctly
00507                         query.timer    = 0;
00508                         if ( NIP_E_OK == nip_mem_write(
00509                                         nip_mdns_queries.id,
00510                                         (uint8_t*) &query,
00511                                         sizeof( struct nip_mdns_query ))
00512                                 )
00513                         {
00514                                 res = new_id;
00515                         }
00516                         // register name usage
00517 //                      nip_mdns_register_name( query.rr_name.part, NIP_MDNS_MAX_NAME_DEPTH, NULL, 0, 1 );
00518                         NIP_MDNS_NAME_USE( query.rr_name.part, NIP_MDNS_MAX_NAME_DEPTH );
00519                 }
00520         }
00521 
00522 
00523         quit:
00524         return res;
00525 }
00526 
00527 /** Return size of cache record. This function is used to ease usage of
00528  * dynamically sized cache list items.
00529  */
00530 nip_mdns_record_size_t nip_mdns_record_size( struct nip_mdns_record *r )
00531 {
00532         // cache item size depends on item type, because content size may vary greatly
00533         nip_mdns_record_size_t off;
00534         switch ( r->type )
00535         {
00536                 case NIP_MDNS_TYPE_A:
00537                         off = NIP_MDNS_A_RECORD_SIZE;
00538                         break;
00539                 case NIP_MDNS_TYPE_SRV:
00540                         off = NIP_MDNS_SRV_RECORD_SIZE;
00541                         break;
00542                 case NIP_MDNS_TYPE_PTR:
00543                         off = NIP_MDNS_PTR_RECORD_SIZE;
00544                         break;
00545                 // add cases for any other record type here. Their content will be treated
00546                 // as string, without interpreting its meaning.
00547                 default:
00548                         off = NIP_MDNS_OTH_RECORD_SIZE;
00549         }
00550 
00551         return off;
00552 }
00553 
00554 /** Get pointer to specific cache entry.
00555  * @note You have to call nip_mem_release_block( nip_mdns_cache.id ) to release
00556  * the retrieved pointer and unlock the cache.
00557  *
00558  * @return Pointer to cache entry. On error NULL will be returned and nip_error
00559  * be set to one of the following values:
00560  *  - NIP_E_NOT_FOUND  no record found for given ID
00561  *  - NIP_E_LOCKED     cache locked, try again later
00562  */
00563 struct nip_mdns_cache *nip_mdns_cache_ptr( nip_mdns_cache_id_t id )
00564 {
00565         nip_mem_size_t         listsize;
00566         struct nip_mdns_cache  *l, *end; // pointer to list item and list end
00567 
00568         listsize = nip_mem_buf_used( nip_mdns_cache.id );
00569         l = nip_mem_obtain_ptr( nip_mdns_cache.id );
00570 
00571         if ( l == NULL )
00572         {
00573                 nip_error = NIP_E_LOCKED;
00574                 goto quit;
00575         }
00576 
00577         end = (void*)((uint8_t*)l + listsize );
00578         while ( l != end )
00579         {
00580                 // found entry? -> return pointer
00581                 if ( l->id == id )
00582                 {
00583                         return l;
00584                 }
00585 
00586                 // advance to next cache item
00587                 l = (struct nip_mdns_cache *)((uint8_t*)l
00588                       + NIP_MDNS_BASE_CACHE_SIZE
00589                       + nip_mdns_record_size( &l->record ));
00590         }
00591 
00592         nip_mem_release_block( nip_mdns_cache.id );
00593 
00594         nip_error = NIP_E_NOT_FOUND;
00595 
00596         quit:
00597         return NULL;
00598 }
00599 
00600 /** Perform mDNS cache operation. Type of operation depends on task parameter.
00601  *
00602  *  - The register operation will add a new authorative record.
00603  *  - The learn operation will add or update a remote DNS record.
00604  *  - The get operation will retrieve the record data for a given ID.
00605  *  - The search operation will return the Id of the existing cache record that
00606  *    matches certain attributes from the given record. The set of attributes
00607  *    chosen for the match depends on the record type.
00608  *  - The reset operation removes all learned entries from cache, clears the
00609  *    subscription array and resets all registered entries into PROBING state.
00610  *
00611  * @note This function will not trigger any dispatcher events or update the
00612  * subscription list as it's just an internal function to be called by other
00613  * mDNS functions.
00614  *
00615  * @param record  Pointer to DNS record to be added or searched
00616  * @param task    register, learn or search record or reset cache
00617  * @return Id of registered, updated or found cache entry. Will be set to
00618  * NIP_MDNS_NO_CACHE_ID on error, nip_error will be set to one of the following
00619  * then:
00620  *  - NIP_E_LOCKED             cache locked, try again later
00621  *  - NIP_E_OUT_OF_MEMORY      unable to allocate memory for new record
00622  *  - NIP_E_OUT_OF_RESSOURECES cache full
00623  *  - NIP_E_NOT_FOUND          search operation was unsuccessfull
00624  *  - NIP_E_EXISTS             register failed because record exists already
00625  *  - NIP_E_INTERNAL_ERROR     something went wrong where it shouldn't have
00626  */
00627 nip_error_t nip_mdns_cache_task( nip_mdns_task_t task,
00628 nip_mdns_cache_id_t *id, struct nip_mdns_record *record )
00629 {
00630         nip_mdns_cache_id_t    res = NIP_MDNS_NO_CACHE_ID;
00631         nip_mdns_cache_id_t    new_id = 1;
00632         nip_mem_size_t         listsize;
00633         nip_mem_ptr_t          list_ptr = { nip_mdns_cache.id, NULL };
00634         nip_mdns_record_size_t recsize;
00635         uint8_t                *start;
00636         struct nip_mdns_cache  *l; // pointer to list item
00637 
00638         listsize = nip_mem_buf_used( nip_mdns_cache.id );
00639         start = nip_mem_obtain_ptr( nip_mdns_cache.id );
00640         l     = (struct nip_mdns_cache *)start;
00641 
00642         if ( start == NULL )
00643         {
00644                 nip_error = NIP_E_LOCKED;
00645                 goto quit;
00646         }
00647 
00648         while ( (nip_mem_size_t)list_ptr.ptr != listsize )
00649         {
00650                 l = (struct nip_mdns_cache *)(start + (nip_mem_size_t)list_ptr.ptr);
00651                 recsize = nip_mdns_record_size( &l->record );
00652 
00653                 // perform cache reset if requested
00654                 if ( task == NIP_MDNS_TASK_RESET )
00655                 {
00656 
00657                         if ( l->stat & NIP_MDNS_CSTAT_AUTH_MASK )
00658                         {
00659                                 l->stat = NIP_MDNS_CSTAT_PROBING;
00660                                 l->age  = 0;
00661                         }
00662                         else
00663                                 l->stat = NIP_MDNS_CSTAT_DELETE;
00664 
00665                         goto next;
00666                 }
00667 
00668                 if ( task & NIP_MDNS_TASK_GET )
00669                 {
00670                         if ( l->id == *id )
00671                         {
00672                                 nip_memcpy( record, &l->record, recsize );
00673                                 if ( task == NIP_MDNS_TASK_GET_LEARNED )
00674                                         record->ttl -= l->age;
00675                                 nip_error = NIP_E_OK;
00676                                 goto release;
00677                         }
00678 
00679                         nip_error = NIP_E_NOT_FOUND;
00680                         goto next;
00681                 }
00682 
00683                 // For all other tasks: Check for matching record type and name
00684                 if ( l->record.type == record->type
00685                 && nip_memcmp( &l->record.name, &record->name, sizeof( struct nip_mdns_name) ) == 0 )
00686                 {
00687                         if ( record->type != NIP_MDNS_TYPE_PTR
00688                                 || nip_memcmp( &l->record.data, &record->data, sizeof(struct nip_mdns_rdata_a) ))
00689                         ///@todo support multiple entries for the same name?
00690                         {
00691                                 res = l->id;
00692                                 nip_error = NIP_E_EXISTS;
00693                                 break;
00694                         }
00695                 }
00696 
00697                 // check free ID
00698                 if ( new_id == l->id )
00699                 {
00700                         new_id++;
00701                         list_ptr.ptr = NULL;
00702                         continue;
00703                 }
00704 
00705                 next:
00706                         list_ptr.ptr += NIP_MDNS_BASE_CACHE_SIZE + recsize;
00707         }
00708 
00709         if ( task == NIP_MDNS_TASK_RESET )
00710         {
00711                 nip_error = NIP_E_OK;
00712                 goto release;
00713         }
00714 
00715 
00716         if ( ( res == NIP_MDNS_NO_CACHE_ID && (task & NIP_MDNS_TASK_REGISTER) )
00717           || task == NIP_MDNS_TASK_LEARN )
00718         {
00719                 recsize    = nip_mdns_record_size( record );
00720 
00721                 // register new record names
00722                 /// @todo nip_mdns_register_record_names( record, 1 )
00723                 NIP_MDNS_NAME_USE( record->name.part, NIP_MDNS_MAX_NAME_DEPTH );
00724                 /// @todo on error goto release
00725 
00726                 if ( res == NIP_MDNS_NO_CACHE_ID )
00727                 {
00728                         if ( new_id >= NIP_MDNS_MAX_CACHE_SIZE )
00729                         {
00730                                 nip_error = NIP_E_OUT_OF_RESSOURCES;
00731                                 goto release;
00732                         }
00733 
00734                         // release cache pointer, so it can be moved if necessary
00735                         nip_mem_release_block( nip_mdns_cache.id );
00736                         // add record to cache
00737                         if ( NIP_E_OK !=
00738                         nip_mem_write( nip_mdns_cache.id, NULL, NIP_MDNS_BASE_CACHE_SIZE + recsize ))
00739                         {
00740                                 nip_error = NIP_E_OUT_OF_MEMORY;
00741                                 goto quit;
00742                         }
00743 
00744                         // get cache pointer
00745                         l = nip_mem_ptr( &list_ptr );
00746                         if ( l == NULL )
00747                         {
00748                                 nip_error = NIP_E_INTERNAL_ERROR;
00749                                 // try to reset list size
00750                                 nip_mem_set_used( nip_mdns_cache.id, listsize );
00751                                 goto quit;
00752                         }
00753 
00754                         l->id = new_id;
00755                         res = new_id;
00756                 }
00757                 else
00758                 {
00759                         // Unregister old record names
00760                         /// @todo nip_mdns_register_record_names( l->record, -1 )
00761                         NIP_MDNS_UNREG_NAME( l->record.name.part, NIP_MDNS_MAX_NAME_DEPTH );
00762                         /// @todo on error, try to unregister new record names, set INTERNAL_ERROR
00763                         /// because name-count may now be inconsistant and goto release.
00764                 }
00765 
00766                 if ( task == NIP_MDNS_TASK_LEARN )
00767                 {
00768                         ///@todo set notification bit
00769                         l->stat |= NIP_MDNS_CSTAT_LEARNED;
00770                 }
00771                 else if ( task == NIP_MDNS_TASK_REGUNIQUE )
00772                 {
00773                         l->stat = NIP_MDNS_CSTAT_PROBING;
00774                 }
00775                 else
00776                 {
00777                         l->stat = NIP_MDNS_CSTAT_ANNOUNCING;
00778                 }
00779 
00780                 // fill cache structure
00781                 nip_memcpy( &l->record, record, recsize );
00782                 l->age  = 0;
00783 
00784                 // notify dispatcher
00785                 nip_disp_notify( NIP_DISP_CHECK_MDNS );
00786 
00787         }
00788         else if ( task == NIP_MDNS_TASK_SEARCH )
00789         {
00790                 if ( res == NIP_MDNS_NO_CACHE_ID )
00791                         nip_error = NIP_E_NOT_FOUND;
00792                 else
00793                         nip_error = NIP_E_OK;
00794         }
00795 
00796         release:
00797         nip_mem_release_block( nip_mdns_cache.id );
00798 
00799         quit:
00800         if ( id != NULL && res != NIP_MDNS_NO_CACHE_ID )
00801                 *id = res;
00802 
00803         return nip_error;
00804 }
00805 
00806 /** Edit query notification list
00807  *
00808  * @param task  whether to subscribe, unsubscribe or notify
00809  * @param c_id  ID of changed cache item or NIP_MDNS_NO_CACHE_ID to notify all queries.
00810  * @param q_id  ID of query item which is needed upon register.
00811  * @param nfy   Flag, who is to be notified. NIP_MDNS_NFY_LOCAL, or -_REMOTE
00812  *
00813  * @note for subscriptions, both c_id and q_id have to be set. No check will be
00814  * performed on this, so you'll corrupt the list if you feed it wrongly.
00815  */
00816 nip_error_t nip_mdns_notify(nip_mdns_nfy_task_t task, nip_mdns_cache_id_t c_id,
00817 nip_mdns_query_id_t q_id, uint8_t nfy )
00818 {
00819         struct nip_mdns_cache_subscription *cs, *end, new;
00820         nip_mem_size_t listsize;
00821         nip_error_t res = NIP_E_OK;
00822 
00823         listsize = nip_mem_buf_used( nip_mdns_cs );
00824         cs = nip_mem_obtain_ptr( nip_mdns_cs );
00825         if ( cs == NULL )
00826         {
00827                 res = NIP_E_LOCKED;
00828                 goto quit;
00829         }
00830 
00831         end = (void*)(((uint8_t*)cs) + listsize);
00832         while ( cs < end )
00833         {
00834                 // The following if-statement is TRUE for any matching combination of the
00835                 // given q_id and c_id parameters with the current cache subscription.
00836                 // E.g. q_id and c_id have to match both, if they're both set, or
00837                 //      q_id has to match               , if c_id is not set, and so on.
00838                 if( ( (q_id & NIP_MDNS_QUERY_ID_MASK) == (cs->q_id & NIP_MDNS_QUERY_ID_MASK)
00839                     && q_id != NIP_MDNS_NO_QUERY
00840                     &&(c_id == cs->c_id || c_id == NIP_MDNS_NO_CACHE_ID ))
00841                  || (  c_id == cs->c_id
00842                     && q_id == NIP_MDNS_NO_QUERY
00843                     && c_id != NIP_MDNS_NO_CACHE_ID)
00844                   )
00845                 {
00846                         if ( task & NIP_MDNS_NFYTASK_NOTIFY )
00847                                 cs->q_id |= nfy;
00848 
00849                         if ( task == NIP_MDNS_NFYTASK_NOTIFIED )
00850                                 cs->q_id &= ~nfy;
00851 
00852                         if ( task == NIP_MDNS_NFYTASK_SUBSCRIBE )
00853                                 break;
00854 
00855                         if ( task == NIP_MDNS_NFYTASK_UNSUBSCRIBE )
00856                         {
00857                                 // remove subscription
00858                                 nip_memmove( cs, cs+1, ((uint8_t*) --end) - ((uint8_t*) cs) );
00859                                 listsize -= sizeof( struct nip_mdns_cache_subscription );
00860                         }
00861                 }
00862 
00863                 cs++;
00864         }
00865 
00866         nip_mem_release_block( nip_mdns_cs );
00867 
00868         if ( task == NIP_MDNS_NFYTASK_SUBSCRIBE && cs == end )
00869         {
00870                 new.c_id     = c_id;
00871                 new.q_id     = q_id | nfy;
00872                 res = nip_mem_write( nip_mdns_cs, &new, sizeof( new ) );
00873                 goto quit;
00874         }
00875 
00876         if ( task == NIP_MDNS_NFYTASK_UNSUBSCRIBE )
00877         {
00878                 // set new subscription list size
00879                 nip_mem_set_used( nip_mdns_cs, listsize );
00880         }
00881 
00882         quit:
00883         return res;
00884 }
00885 
00886 
00887 /** Close query.
00888  */
00889 nip_error_t nip_mdns_close( nip_mdns_query_id_t r_id )
00890 {
00891         /// @todo implement
00892         return NIP_E_OK;
00893 }
00894 
00895 /** Fetch next result for lookup query. This function has to be called
00896  * repeatedly, to fetch all results until nip_mdns_close() is called.
00897  * @param r_id  ID of query to fetch result from
00898  * @return ID of cache entry to result. If no result was available
00899  * NIP_MDNS_NO_CACHE_ID will be returned and nip_error be set to NIP_E_AGAIN.
00900  */
00901 nip_mdns_cache_id_t nip_mdns_fetch_results( nip_mdns_query_id_t r_id )
00902 {
00903         /// @todo implement
00904         return NIP_MDNS_NO_CACHE_ID;
00905 }
00906 
00907 /** Unregister previosly registered DNS record.
00908  */
00909 nip_error_t nip_mdns_unregister( nip_mdns_cache_id_t id )
00910 {
00911         /// @todo implement
00912         return NIP_E_OK;
00913 }
00914 
00915 /** set new host name.
00916  *
00917  * nip_error variable contains error code.
00918  * @todo should this function or part of it be moved to dns-sd?
00919  */
00920 void nip_mdns_register_hostname( struct nip_dns_string *name )
00921 {
00922         nip_net_if_t *net_if = &nip_net_if_list[ nip_mdns_if_id ];
00923         struct nip_mdns_record dnsrec;
00924         nip_mem_ptr_t          name_ptr = { NIP_MEM_NULL, (uint8_t*)name };
00925 #if NIP_MDNS_CREATE_IN_ADDR_ARPA_PTR == 1
00926         uint8_t                name_str[8];
00927         uint8_t                i,r;  // integer and remainder
00928         uint8_t                part; // number of current name part
00929         uint8_t                name_pos;
00930 #endif
00931         // fill DNS record for hostname
00932         dnsrec.type    = NIP_MDNS_TYPE_A;
00933         // add A-record TTL
00934         dnsrec.ttl     = NIP_MDNS_DEFAULT_TTL_A;
00935 //      dnsrec.name.part[0] = nip_mdns_register_name( NULL, 0, &name_ptr, len, 1 );
00936         dnsrec.name.part[0] = NIP_MDNS_REG_STRING( &name_ptr );
00937         dnsrec.name.part[1] = nip_mdns_domain;
00938         dnsrec.name.part[2] = NIP_MDNS_NO_NAMESTRING;
00939         nip_memcpy( dnsrec.data.a.ip_addr, net_if->ip_conf.addr, 4 );
00940 
00941         if ( dnsrec.name.part[0] == NIP_MDNS_NO_NAMESTRING )
00942         {
00943                 goto quit;
00944         }
00945 
00946         // unregister current hostname
00947         if ( nip_mdns_hostname != NIP_MDNS_NO_CACHE_ID )
00948         {
00949                 nip_error = nip_mdns_unregister( nip_mdns_hostname );
00950                 if ( nip_error != NIP_E_OK )
00951                         goto unregister_hostname_usage;
00952 #if NIP_MDNS_CREATE_IN_ADDR_ARPA_PTR == 1
00953                 nip_error = nip_mdns_unregister( nip_mdns_host_ptr );
00954 #endif
00955         }
00956 
00957         // register DNS record for new hostname
00958 #if NIP_MDNS_DEFEND_HOSTNAME == 1
00959         NIP_MDNS_REGUNIQUE( &nip_mdns_hostname, &dnsrec );
00960 #else
00961         NIP_MDNS_REGISTER( &nip_mdns_hostname, &dnsrec );
00962 #endif
00963 
00964         unregister_hostname_usage:
00965         NIP_MDNS_UNREG_NAME( dnsrec.name.part, 1 );
00966 
00967 #if NIP_MDNS_CREATE_IN_ADDR_ARPA_PTR == 1
00968         // register reverse-ptr record
00969         if ( nip_mdns_hostname != NIP_MDNS_NO_CACHE_ID )
00970         {
00971                 // copy hostname to ptr-record
00972                 dnsrec.data.ptr.name.part[0] = dnsrec.name.part[0];
00973                 dnsrec.data.ptr.name.part[1] = dnsrec.name.part[1];
00974                 dnsrec.data.ptr.name.part[2] = dnsrec.name.part[2];
00975                 part = 0;
00976                 // set in-addr.arpa name
00977                 do
00978                 {
00979                         name_ptr.ptr = name_str;
00980                         // convert ip address too 4 namestrings and add them
00981                         if ( part < 4 )
00982                         {
00983                                 i = NIP_IP_ADDR( nip_mdns_if_id )[3-part];
00984                                 if      ( i>=100 ) name_pos = 3;
00985                                 else if ( i>=10  ) name_pos = 2;
00986                                 else               name_pos = 1;
00987                                 name_str[0] = name_pos;
00988                                 do {
00989                                         r = i%10;
00990                                         name_str[ name_pos-- ] = '0'+r;
00991                                 } while ((i/=10) != 0);
00992                         }
00993                         else if ( part == 4 )
00994                         {
00995                                 // add "in-addr" domain part
00996                                 name_str[0] = 7;
00997                                 name_str[1] = 'i';
00998                                 name_str[2] = 'n';
00999                                 name_str[3] = '-';
01000                                 name_str[4] = 'a';
01001                                 name_str[5] = 'd';
01002                                 name_str[6] = 'd';
01003                                 name_str[7] = 'r';
01004                         }
01005                         else if ( part == 5 )
01006                         {
01007                                 // add "arpa" domain part
01008                                 name_str[0] = 4;
01009                                 name_str[1] = 'a';
01010                                 name_str[2] = 'r';
01011                                 name_str[3] = 'p';
01012                                 name_str[4] = 'a';
01013                         }
01014                         dnsrec.name.part[ part ] = NIP_MDNS_REG_STRING( &name_ptr );
01015                         if ( dnsrec.name.part[ part ] == NIP_MDNS_NO_NAMESTRING )
01016                                 goto unregister_ptrname_usage;
01017                 } while (++part < 6 );
01018                 // set record type and TTL
01019                 dnsrec.type    = NIP_MDNS_TYPE_PTR;
01020                 dnsrec.ttl     = NIP_MDNS_DEFAULT_TTL_A;
01021 
01022                 // register DNS record for new hostname
01023                 NIP_MDNS_REGUNIQUE( &nip_mdns_host_ptr, &dnsrec );
01024                 /// @todo do something if pointer registration fails?
01025 
01026                 unregister_ptrname_usage:
01027                 NIP_MDNS_UNREG_NAME( dnsrec.name.part, 6 );
01028         }
01029 #endif
01030 
01031 //      nip_mdns_register_name( &dnsrec.name.part[0], -1, NULL, 0, -1 );
01032 
01033         quit:
01034         return;
01035 }
01036 
01037 /** Register host name and refresh cache
01038  */
01039 void nip_mdns_interface_up( nip_net_if_id_t if_id )
01040 {
01041 
01042         nip_mdns_if_id   = if_id;
01043         nip_mdns.stat   = NIP_MDNS_STAT_INIT;
01044 
01045         // reset cache
01046         NIP_MDNS_RESET_CACHE();
01047 
01048         nip_disp_notify( NIP_DISP_CHECK_MDNS );
01049 //      nip_dispatcher();
01050 }
01051 
01052 void nip_mdns_interface_down( void )
01053 {
01054         ///@todo send notifications to network
01055 
01056         ///@todo reset cache, clean up remote queries
01057 
01058         // close UDP socket
01059         nip_udp_close( nip_mdns_sock );
01060 
01061         // set status
01062         nip_mdns_if_id = NIP_NET_NO_IF;
01063         nip_mdns.stat  = NIP_MDNS_STAT_DOWN;
01064 }
01065 
01066 void nip_mdns_disp_check_cache( void )
01067 {
01068         nip_mem_size_t         listsize;    // size of list (in bytes)
01069         nip_mdns_record_size_t csize;       // size of current item's dns record
01070         struct nip_mdns_cache  *l;          // current list item
01071         struct nip_mdns_cache  c;
01072         nip_mdns_query_id_t    q_id;
01073         nip_mdns_query_type_t  q_type;
01074         uint8_t                *item, *pstart, *name, i;
01075         nip_mem_size_t         pstart_off;  // offset of packet start in block
01076         nip_mem_ptr_t          item_ptr, name_ptr;    // pointer to scan position in packet
01077         nip_error_t            res;
01078         nip_mdns_type_t        rtype;
01079 
01080 
01081         listsize = nip_mem_buf_used( nip_mdns_cache.id );
01082 
01083 
01084         while ( (nip_mem_size_t)nip_mdns_cache.ptr < listsize )
01085         {
01086                 l = nip_mem_ptr( &nip_mdns_cache );
01087                 if ( l == NULL )
01088                         goto quit;
01089 
01090                 csize = nip_mdns_record_size( &l->record ) + NIP_MDNS_BASE_CACHE_SIZE;
01091 
01092                 /** @todo remove items that are scheduled for deletion where all
01093                  * subscribers have been notified */
01094                 // if l->stat == DELETE && notificationcount = 0
01095                 // {
01096                 //      listsize -= recsize;
01097                 //      nip_memmove( l, (uint8_t*)l + recsize, listsize - (nip_mem_size_t)nip_mdns_cache.ptr);
01098                 //      nip_mem_release_ptr( &nip_mdns_cache );
01099                 //      nip_mem_set_used( nip_mdns_cache.id, listsize );
01100                 //      continue;
01101                 // }
01102 
01103                 // make local copy of cache item to work with
01104                 nip_memcpy( &c, l, csize);
01105 
01106                 nip_mem_release_ptr( &nip_mdns_cache );
01107 
01108                 // Check if current cache record correlates somehow with received query.
01109                 // Only authorative or probing records are relevant for this
01110                 if ( c.stat & NIP_MDNS_CSTAT_AUTH_BIT
01111                   && nip_mdns.recv & NIP_MDNS_PTYPE_QUERY )
01112                 {
01113                         // set start item_ptr before any item, so the next parse_packet call
01114                         // will get a pointer to the first item.
01115                         nip_udp_data_mem_ptr( nip_mdns_sock, NIP_UDP_RX_BUF, &item_ptr );
01116                         pstart_off   = (nip_mem_size_t)item_ptr.ptr;
01117 
01118                         // loop through queries in query-packet
01119                         do
01120                         {
01121                                 res = nip_mdns_parse_packet(
01122                                                 NIP_UDP_RX_BUF,
01123                                                 NIP_MDNS_PTASK_NEXT_ITEM,
01124                                                 &item_ptr,
01125                                                 NIP_MDNS_QDCOUNT_OFFSET );
01126 
01127                                 pstart = nip_udp_data_ptr( nip_mdns_sock, NIP_UDP_RX_BUF );
01128 
01129                                 if ( res == NIP_E_OK && pstart != NULL)
01130                                 {
01131                                         // parse query
01132                                         item = pstart + (nip_mem_size_t)item_ptr.ptr - pstart_off;
01133                                         name = item;
01134                                         i    = 0;
01135                                         name_ptr.id  = NIP_MEM_NULL;
01136                                         // check name
01137                                         do
01138                                         {
01139                                                 name_ptr.ptr = name;
01140 
01141                                                 // name pointer? follow
01142                                                 if ( *name & 0xC0 )
01143                                                 {
01144 //                                                      name = pstart + (ntohs(*((uint16_t*)name)) & 0x3F00 );
01145                                                         name = pstart + ((name[0]&0x3F)<<8) + name[1];
01146                                                 }
01147                                                 // end of name?
01148                                                 else if ( *name == 0x00 )
01149                                                 {
01150                                                         if ( (c.record.name.part[i] != NIP_MDNS_NO_NAMESTRING
01151                                                            && i != NIP_MDNS_MAX_NAME_DEPTH - 1 )
01152                                                           || i >= NIP_MDNS_MAX_NAME_DEPTH )
01153                                                                 res = NIP_E_NO_MATCH;
01154                                                 }
01155                                                 else
01156                                                 {
01157                                                         // compare namestrings
01158                                                         if ( i >= NIP_MDNS_MAX_NAME_DEPTH
01159                                                         || NIP_MDNS_CHECK_NAME( &name_ptr ) != c.record.name.part[i] )
01160                                                                 res = NIP_E_NO_MATCH;
01161                                                         name += *name + 1;
01162                                                         i++;
01163                                                 }
01164 
01165                                                 if ( *item < 0xC0 && *item != 0x00)
01166                                                         item += *item + 1;
01167 
01168                                                 // overflow protection for name, so broken packets wont send
01169                                                 // us in some infinte loop
01170                                                 if ( name >= pstart - pstart_off + nip_mdns.recv_size )
01171                                                 {
01172                                                         res = NIP_E_EOF;
01173                                                 }
01174 
01175                                         } while ( *name != 0x00 && res == NIP_E_OK );
01176                                         if (*item == 0x00 ) item++;
01177                                         else if (*item &  0xC0 ) item+=2;
01178 
01179                                         // check type and class
01180                                         rtype = ntohs(*(uint16_t*)item);
01181                                         if ( (rtype != c.record.type && rtype != NIP_MDNS_TYPE_ANY )
01182                                           || item[3] != NIP_DNS_CLASS_IN )
01183                                         {
01184                                                 res = NIP_E_NO_MATCH;
01185                                         }
01186                                         ///  @todo else check known answers
01187 
01188                                 }
01189 
01190                                 // release block
01191                                 nip_udp_ptr_release( nip_mdns_sock, NIP_UDP_RX_BUF );
01192 
01193                                 if ( res == NIP_E_OK )
01194                                 {
01195                                         // register remote query, we have auth answer for it in cache
01196                                         q_id = nip_mdns_query( NIP_MDNS_QTYPE_REMOTE, &c.record.name, c.record.type );
01197                                         if ( q_id != NIP_MDNS_NO_QUERY )
01198                                         {
01199                                                 // add cache subscription
01200                                                 NIP_MDNS_SUBSCRIBE_REMOTE( c.id, q_id );
01201                                         }
01202                                 }
01203 
01204                         } while ( res != NIP_E_EOF );
01205 
01206                                 /// @todo received Probe that we have Probe for?
01207                                         /// @todo -> handle probe conflict
01208 
01209                         /// @todo out-of-memory? -> goto release
01210                 }
01211 
01212 
01213                 /// @todo check if there are unnotified remote queries to this
01214 
01215                 /// @todo check if there's still a query interested in this item
01216                 /// @todo update age and possibly trigger delete or refresh
01217 
01218                 /// @todo check probe and announcement states of registered cache records
01219                 if ( c.stat == NIP_MDNS_CSTAT_PROBING
01220                   || NIP_MDNS_GET_AUTH_CSTAT( &c ) == NIP_MDNS_CSTAT_ANNOUNCING)
01221                 {
01222                         /// @todo allow announcement of non-unique records
01223                         if( c.age < NIP_MDNS_PROBE_COUNT + NIP_MDNS_ANNOUNCE_COUNT )
01224                         {
01225                                 q_type = NIP_MDNS_QTYPE_PROBE;
01226                                 if ( c.age >= NIP_MDNS_PROBE_COUNT )
01227                                 {
01228                                         q_type = NIP_MDNS_QTYPE_ANNOUNCE;
01229                                         if ( c.stat == NIP_MDNS_CSTAT_PROBING )
01230                                                 c.stat = NIP_MDNS_CSTAT_ANNOUNCING;
01231 //                                      NIP_MDNS_SET_AUTH_CSTAT( &c, NIP_MDNS_CSTAT_ANNOUNCING );
01232                                 }
01233 
01234                                 q_id = nip_mdns_query( q_type, &c.record.name, NIP_MDNS_TYPE_ANY );
01235 
01236                                 if ( q_id != NIP_MDNS_NO_QUERY
01237                                 && NIP_MDNS_SUBSCRIBE_REMOTE( c.id, q_id ) == NIP_E_OK )
01238                                 {
01239                                         // update timer (probe/announce counter)
01240                                         c.age++;
01241                                 }
01242                         }
01243                         else
01244                         {
01245                                 NIP_MDNS_SET_AUTH_CSTAT( &c, NIP_MDNS_CSTAT_UNIQUE_AUTH );
01246                         }
01247                 }
01248 
01249                 // write cache entry
01250                 l = nip_mem_ptr( &nip_mdns_cache );
01251                 if ( l == NULL )
01252                         goto quit;
01253 
01254                 nip_memcpy( l, &c, csize );
01255                 nip_mem_release_block( nip_mdns_cache.id );
01256 
01257                 /// @todo check if local hostname record changed and update all registered
01258                 ///       records if necessary
01259 
01260                 // increment list offset
01261                 nip_mdns_cache.ptr = (void*)((nip_mem_size_t)nip_mdns_cache.ptr + csize);
01262         }
01263 
01264         // Checked all Cache items? -> reset offset
01265         nip_mdns_cache.ptr = (void*)0;
01266 
01267         quit:
01268                 nip_mdns.stat = NIP_MDNS_STAT_QUERY_CHECK;
01269                 NIP_CURR_CMD  = &nip_mdns_disp_check_queries;
01270                 return;
01271 }
01272 
01273 /** Initialize Sending of packet.
01274  *
01275  * If successfull the global nip_mdns.send variable will be set to ptype. Only
01276  * exception is, if that variable is NIP_MDNS_PTYPE_ANSWCONTINUED. In that case
01277  * only ptypes == NIP_MDNS_PTYPE_QUERY will be accepted and nip_mdns.send will
01278  * remain untouched to allow further special handling of that situation.
01279  */
01280 nip_error_t nip_mdns_tx_init( nip_mdns_packet_type_t ptype )
01281 {
01282         struct nip_dns_header *header;
01283         nip_error_t           res = NIP_E_OK;
01284 
01285         if ( nip_mdns.send == NIP_MDNS_PTYPE_NONE
01286           || ( nip_mdns.send == NIP_MDNS_PTYPE_ANSWCONTINUED
01287             && ptype         == NIP_MDNS_PTYPE_QUERY ))
01288         {
01289                 res    = nip_udp_init_send ( nip_mdns_sock, NIP_DNS_HSIZE  );
01290                 res    = nip_udp_write     ( nip_mdns_sock, NULL, NIP_DNS_HSIZE  );
01291                 header = nip_udp_data_ptr  ( nip_mdns_sock, NIP_UDP_TX_BUF );
01292                 if ( res != NIP_E_OK || header == NULL )
01293                 {
01294                         // Come back and try again later. Probably out-of-memory
01295                         nip_udp_cancel_send( nip_mdns_sock );
01296                         res = NIP_E_AGAIN;
01297                         goto quit;
01298                 }
01299                 if ( nip_mdns.send != NIP_MDNS_PTYPE_ANSWCONTINUED )
01300                         nip_mdns.send = ptype;
01301 
01302                 if ( ptype == NIP_MDNS_PTYPE_RESPONSE )
01303                 {
01304                         header->qr_opcode_aa_tc_rd = ( NIP_DNS_QR_MASK | NIP_DNS_AA_MASK );
01305                 }
01306                 nip_udp_ptr_release( nip_mdns_sock, NIP_UDP_TX_BUF );
01307         } else
01308                 if ( ptype != nip_mdns.send )
01309                         res = NIP_E_AGAIN;
01310 
01311         quit:
01312         return res;
01313 }
01314 
01315 /** Add DNS Name/Type/Class to Buffer. The dst-pointer will be forward to the
01316  * end of the written name. That can serve as indication of the name length,
01317  * given that the original pointer has been saved by the callee.
01318  */
01319 nip_error_t nip_mdns_tx_name( nip_mem_ptr_t *dst, struct nip_mdns_name *rr_name,
01320 nip_mdns_type_t rr_type, uint8_t uclass_byte )
01321 {
01322         nip_error_t           res;
01323         nip_mem_ptr_t         typeclass_ptr;
01324         uint8_t               typeclass[4];
01325 
01326         // add name
01327         res = NIP_MDNS_WRITE_NAME( rr_name->part, NIP_MDNS_MAX_NAME_DEPTH, dst );
01328         if ( res != NIP_E_OK )
01329                 goto quit;
01330 
01331         typeclass[0] = 0;
01332         typeclass[1] = rr_type;
01333         typeclass[2] = uclass_byte;
01334         typeclass[3] = NIP_DNS_CLASS_IN;
01335         typeclass_ptr.id  = NIP_MEM_NULL;
01336         typeclass_ptr.ptr = typeclass;
01337 
01338         // add type and class
01339         res = nip_mem_insert( dst, &typeclass_ptr, 4 );
01340         /// @todo Handle error cases. For that backup the original dst-pointer and
01341         /// remove all data inbetween the old and the new pointer.
01342 
01343         quit:
01344         return res;
01345 }
01346 
01347 void nip_mdns_transmit( void )
01348 {
01349         struct nip_udp_sock_addr addr;
01350 
01351         // fill address structure
01352         addr.port  = NIP_UDP_PORT_MDNS;
01353         addr.ip[0] = NIP_MDNS_ADDR0;
01354         addr.ip[1] = NIP_MDNS_ADDR1;
01355         addr.ip[2] = NIP_MDNS_ADDR2;
01356         addr.ip[3] = NIP_MDNS_ADDR3;
01357 
01358         // no queries or answers in packet? -> discard, else transmit
01359         if ( nip_udp_transmit( nip_mdns_sock, &addr, nip_mdns_if_id ) == NIP_E_OK )
01360         {
01361                 nip_mdns.send = NIP_MDNS_PTYPE_NONE;
01362         }
01363 
01364 }
01365 
01366 /** Perform several tasks on DNS packets.
01367  *
01368  * For task NIP_MDNS_PTASK_NEXT_ITEM the return value may be NIP_E_EOF if there
01369  * were no more items in the given section beyond the given pointer p.
01370  */
01371 nip_error_t nip_mdns_parse_packet( nip_udp_buf_type_t udp_buffer,
01372 nip_mdns_packet_task_t task, nip_mem_ptr_t *p, nip_mdns_qa_offset_t section )
01373 {
01374         uint8_t  *pos, len, *end;
01375         uint16_t count;
01376         nip_error_t           res = NIP_E_OK;
01377         nip_mem_ptr_t         ptmp;
01378         struct nip_dns_header *header;
01379         nip_mem_size_t        pstart_off, bufsize;
01380         nip_mdns_qa_offset_t  sec = 0;
01381 
01382         nip_udp_data_mem_ptr( nip_mdns_sock, udp_buffer, &ptmp );
01383 
01384         bufsize    = nip_mem_buf_used( ptmp.id );
01385         header     = nip_mem_ptr( &ptmp );
01386         pstart_off = (nip_mem_size_t)ptmp.ptr;
01387         if ( header == NULL )
01388         {
01389                 res = nip_error;
01390                 goto quit;
01391         }
01392 
01393         // start right after header
01394         pos = (uint8_t*)(header+1);
01395         end = (uint8_t*)(header) + bufsize;
01396 
01397         // set Truncated-Flag
01398         if ( task == NIP_MDNS_PTASK_SET_TC )
01399         {
01400                 header->qr_opcode_aa_tc_rd |= NIP_DNS_TC_MASK;
01401                 goto release;
01402         }
01403 
01404 
01405         // move to specified section in packet
01406         do
01407         {
01408                 // get number of items in section
01409                 count = ntohs( header->qa_count[ sec ] );
01410 
01411                 // found section?
01412                 if ( sec == section )
01413                 {
01414                         if ( task == NIP_MDNS_PTASK_SECTION_PTR )
01415                         {
01416                                 goto release;
01417                         }
01418                         if ( task == NIP_MDNS_PTASK_INC_COUNTER )
01419                         {
01420                                 header->qa_count[ section ] = htons( count+1 );
01421                                 goto release;
01422                         }
01423                 }
01424 
01425                 // move to end of section (begin of next section)
01426                 while ( count > 0 )
01427                 {
01428                         ptmp.ptr = (void*)(pstart_off + pos - (uint8_t*)header);
01429 
01430                         if ( task == NIP_MDNS_PTASK_NEXT_ITEM && ptmp.ptr > p->ptr)
01431                                 goto release;
01432 
01433                         // name
01434                         do
01435                         {
01436                                 // forward pos pointer to skip name
01437                                 len = *pos;
01438                                 if ( len >= 0xC0 ) { pos += 2; break; }
01439                                 if ( len == 0x00 ) { pos += 1; break; }
01440                                 // overflow protection against bad packets
01441                                 if ( pos >= end )
01442                                 {
01443                                         res = NIP_E_EOF;
01444                                         goto release;
01445                                 }
01446                         } while ( pos+=len+1 );
01447 
01448                         // type and class
01449                         pos += 4;
01450 
01451                         // record data
01452                         if ( sec != NIP_MDNS_QDCOUNT_OFFSET )
01453                         {
01454                                 // TTL
01455                                 pos += 4;
01456                                 // Len + Data
01457                                 pos += 2 + *pos;
01458                         }
01459 
01460                         // overflow protection against bad packets
01461                         if ( pos >= end )
01462                         {
01463                                 res = NIP_E_EOF;
01464                                 goto release;
01465                         }
01466                         count--;
01467                 }
01468 
01469                 if ( task == NIP_MDNS_PTASK_NEXT_ITEM && sec == section )
01470                 {
01471                         // no more items in section. -> Error
01472                         res = NIP_E_EOF;
01473                         goto release;
01474                 }
01475 
01476         } while ( ++sec < NIP_MDNS_QA_COUNTERS_NUMBER );
01477 
01478         release:
01479                 nip_mem_release_ptr( &ptmp );
01480 
01481         // set return pointer
01482         if ( p != NULL && res == NIP_E_OK )
01483         {
01484                 p->id  = ptmp.id;
01485                 p->ptr = (void*)(pstart_off + pos - (uint8_t*)header);
01486         }
01487 
01488         quit:
01489         return res;
01490 }
01491 
01492 
01493 
01494 
01495 /** Check list of mDNS queries
01496  */
01497 void nip_mdns_disp_check_queries( void )
01498 {
01499         nip_mem_size_t         listsize;    // size of list (in bytes)
01500         struct nip_mdns_query  *l;          // current list item
01501         struct nip_mdns_query  q;           // query structure for "offline"-work
01502         struct nip_mdns_record r;           // record structure for "offline"-work
01503         uint8_t                count = 0;   // count items in transmit-packet
01504         nip_error_t            res = NIP_E_OK;
01505         nip_mem_ptr_t          cs_ptr, insert_ptr, src_ptr;
01506         nip_mem_size_t         section_off, head_off, len;
01507         nip_mdns_packet_type_t ptype;
01508         nip_mdns_qa_offset_t   answer_section;
01509         nip_mdns_cache_id_t    c_id;
01510         uint8_t                name_flag;
01511 //      nip_mdns_namestring_id_t *name;
01512         struct {
01513                         uint16_t ttl[2];
01514                         uint16_t len;
01515         } data_header;
01516         struct nip_mdns_cache_subscription *cs;
01517 
01518         while ( (l = nip_mem_ptr( &nip_mdns_queries )) != NULL )
01519         {
01520                 // make local copy of cache item to work with
01521                 nip_memcpy( &q, l, sizeof( struct nip_mdns_query ));
01522                 /** @todo remove items that have no more subscribers */
01523                 // if l->stat == DELETE && subscribercount = 0
01524                 // {
01525                 //      listsize -= sizeof( struct nip_mdns_query );
01526                 //      nip_memmove( l, (uint8_t*)l + sizeof( struct nip_mdns_query ), listsize - (nip_mem_size_t)nip_mdns_queries.ptr);
01527                 //      nip_mem_release_ptr( &nip_mdns_queries );
01528                 //      nip_mem_set_used( nip_mdns_queries.id, listsize );
01529                 //      continue;
01530                 // }
01531 
01532                 nip_mem_release_ptr( &nip_mdns_queries );
01533 
01534                 /// @todo loop answers a in Packet
01535                 /// @todo a saisfies q ?
01536                 ///          q=local? -> add a to cache, subscribe/notify q,a
01537                 ///          q=probe? -> CONFLICT -> select new name, update cache items
01538                 ///          q=remote & a=probe? -> schedule immediate response (timeout=0)
01539 
01540                 /// @todo update query timer
01541 
01542                 /// if timeout==0
01543                 ///          q=local?  -> @todo configure query
01544                 ///          @todo loop notifications,
01545                 ///              q=remote? -> @todo configure query response
01546                 ///              @todo add answers
01547                 answer_section = NIP_MDNS_QDCOUNT_OFFSET;
01548                 if ( q.timer == 0 && q.type & NIP_MDNS_QTYPE_TX_BIT )
01549                 {
01550                         // determine packet type
01551                         if ( q.type == NIP_MDNS_QTYPE_PROBE )
01552                         {
01553                                 ptype          = NIP_MDNS_PTYPE_PROBE;
01554                                 answer_section = NIP_MDNS_NSCOUNT_OFFSET;
01555                         }
01556                         else
01557                         {
01558                                 ptype = NIP_MDNS_PTYPE_QUERY;
01559                                 answer_section = NIP_MDNS_ANCOUNT_OFFSET;
01560                         }
01561 
01562                         // Can only transmit one probe at once, currently.
01563                         res = nip_mdns_tx_init( ptype );
01564 
01565                         if ( res == NIP_E_OK )
01566                         {
01567                                 // move pointer to query section
01568                                 nip_mdns_parse_packet(
01569                                    NIP_UDP_TX_BUF,
01570                                    NIP_MDNS_PTASK_SECTION_PTR,
01571                                    &insert_ptr,
01572                                    NIP_MDNS_QDCOUNT_OFFSET );
01573 
01574                                 // No new query will be created for NIP_MDNS_PTYPE_ANSWCONTINUED
01575                                 if ( nip_mdns.send != NIP_MDNS_PTYPE_ANSWCONTINUED )
01576                                 {
01577                                         // Add name to packet
01578                                         if ( insert_ptr.id == NIP_MEM_NULL )
01579                                                 goto quit;
01580                                         res = nip_mdns_tx_name( &insert_ptr, &q.rr_name, q.rr_type, 0 );
01581                                         if ( res != NIP_E_OK )
01582                                                 goto quit;
01583                                         // increment query-counter
01584                                         nip_mdns_parse_packet(
01585                                                 NIP_UDP_TX_BUF,
01586                                                 NIP_MDNS_PTASK_INC_COUNTER,
01587                                                 &insert_ptr,
01588                                                 NIP_MDNS_QDCOUNT_OFFSET );
01589                                         count++;
01590                                 }
01591                                 else
01592                                 {
01593                                         // continue as if a query has been created. That query was just
01594                                         // part of a previous packet.
01595                                         nip_mdns.send = NIP_MDNS_PTYPE_QUERY;
01596                                 }
01597                         }
01598                         else
01599                         {
01600                                 // clear answer selector
01601                                 answer_section = NIP_MDNS_QDCOUNT_OFFSET;
01602                         }
01603                 }
01604                 cs_ptr.id  = nip_mdns_cs;
01605                 cs_ptr.ptr = (void*)0;
01606                 src_ptr.id = NIP_MEM_NULL;
01607 
01608                 // check if query-answers need be sent
01609                 if (   answer_section != NIP_MDNS_QDCOUNT_OFFSET
01610                     || (   q.type & NIP_MDNS_QTYPE_REMOTE
01611                         && (res = nip_mdns_tx_init( NIP_MDNS_PTYPE_RESPONSE )) == NIP_E_OK
01612                         && (answer_section = NIP_MDNS_ANCOUNT_OFFSET) )
01613                         )
01614                 {
01615                         // move pointer to answer section
01616                         nip_mdns_parse_packet(
01617                                 NIP_UDP_TX_BUF,
01618                                 NIP_MDNS_PTASK_SECTION_PTR,
01619                                 &insert_ptr,
01620                                 answer_section );
01621 
01622                         // remember section offset
01623                         section_off = (nip_mem_size_t)insert_ptr.ptr;
01624 
01625 
01626                         // check subscriptions
01627                         // get subscription pointer
01628                         while ( (cs = nip_mem_ptr( &cs_ptr )) != NULL )
01629                         {
01630                                 // check for Remote subscriptions. Local subscriptions will not be
01631                                 // transmitted on the network as answers, as they mark the notification
01632                                 // state of the local user.
01633                                 c_id = NIP_MDNS_NO_CACHE_ID;
01634                                 if((cs->q_id & NIP_MDNS_NFY_REMOTE)
01635                                 && (cs->q_id & NIP_MDNS_QUERY_ID_MASK) == (q.id & NIP_MDNS_QUERY_ID_MASK))
01636                                 {
01637                                         c_id = cs->c_id;
01638                                 }
01639                                 nip_mem_release_block( nip_mdns_cs );
01640                                 if ( c_id == NIP_MDNS_NO_CACHE_ID )
01641                                         goto next_cs;
01642 
01643                                 insert_ptr.ptr = (void*)section_off;
01644                                 len            = 0;
01645 
01646                                 // get ressource record
01647                                 res = nip_mdns_cache_task( nip_mdns.send == NIP_MDNS_PTYPE_QUERY
01648                                         ? NIP_MDNS_TASK_GET_LEARNED
01649                                              : NIP_MDNS_TASK_GET
01650                                            , &c_id, &r );
01651                                 if ( res != NIP_E_OK )
01652                                         goto answer_abort;
01653 
01654                                 // add Ressource Name to packet
01655                                 name_flag = 0;
01656                                 if ( q.type == NIP_MDNS_QTYPE_ANNOUNCE )
01657                                         name_flag = NIP_DNS_CF_FLAG;
01658                                 res = nip_mdns_tx_name( &insert_ptr, &r.name, r.type, name_flag );
01659                                 if ( res != NIP_E_OK )
01660                                         goto answer_abort;
01661 
01662                                 // remember current offset for later inclusion of data header
01663                                 head_off = (nip_mem_size_t)insert_ptr.ptr;
01664 
01665                                 // add Ressource Record Data
01666                                 // special treatment of recognized types
01667                                 src_ptr.id  = NIP_MEM_NULL;
01668                                 src_ptr.ptr = (uint8_t*)&r.data;
01669                                 if ( r.type == NIP_MDNS_TYPE_A )
01670                                 {
01671                                         len = 4;
01672                                 }
01673                                 else
01674                                 if ( r.type == NIP_MDNS_TYPE_SRV )
01675                                 {
01676                                         len = 6;
01677                                         r.data.srv.priority = htons( r.data.srv.priority );
01678                                         r.data.srv.weight   = htons( r.data.srv.weight   );
01679                                         r.data.srv.port     = r.data.srv.port;
01680                                 }
01681                                 else
01682                                 if ( r.type == NIP_MDNS_TYPE_PTR )
01683                                 {
01684                                         res = NIP_MDNS_WRITE_NAME( r.data.ptr.name.part, NIP_MDNS_MAX_NAME_DEPTH, &insert_ptr );
01685                                         if ( res != NIP_E_OK )
01686                                                 goto answer_abort;
01687                                 }
01688                                 else
01689                                 {
01690                                         // add strings
01691                                         src_ptr.id  = r.data.oth.string;
01692                                         src_ptr.ptr = (void*)0;
01693                                         len = nip_mem_buf_used( r.data.oth.string );
01694                                 }
01695 
01696                                 res = nip_mem_insert( &insert_ptr, &src_ptr, len );
01697                                 if ( res != NIP_E_OK )
01698                                 {
01699                                         len = 0;
01700                                         goto answer_abort;
01701                                 }
01702 
01703                                 if ( r.type == NIP_MDNS_TYPE_SRV )
01704                                 {
01705                                         // add srv-name
01706                                         res = NIP_MDNS_WRITE_NAME( r.data.srv.target.part, NIP_MDNS_MAX_NAME_DEPTH, &insert_ptr );
01707                                         if ( res != NIP_E_OK )
01708                                                 goto answer_abort;
01709                                 }
01710 
01711                                 // add Ressource Record Header before data
01712                                 len                = (nip_mem_size_t)insert_ptr.ptr-head_off;
01713                                 data_header.ttl[0] = 0;
01714                                 data_header.ttl[1] = htons( r.ttl );
01715                                 data_header.len    = htons( len );
01716                                 insert_ptr.ptr     = (void*)head_off;
01717                                 src_ptr.id         = NIP_MEM_NULL;
01718                                 src_ptr.ptr        = (uint8_t*)&data_header;
01719                                 res = nip_mem_insert( &insert_ptr, &src_ptr, sizeof( data_header ) );
01720                                 if ( res != NIP_E_OK )
01721                                         goto answer_abort;
01722 
01723                                 // answer added successfully? -> count
01724                                 nip_mdns_parse_packet( NIP_UDP_TX_BUF, NIP_MDNS_PTASK_INC_COUNTER, &insert_ptr, answer_section  );
01725                                 count++;
01726 
01727                                 // unsubscribe probes, unnotify normal queries
01728                                 NIP_MDNS_QUERY_NOTIFIED( c_id, q.id );
01729 
01730                                 next_cs:
01731                                 // move on to next subscription
01732                                 cs_ptr.ptr += sizeof( struct nip_mdns_cache_subscription );
01733                         }
01734                         goto query_done;
01735 
01736                         answer_abort:
01737                                         // remove incomplete answer from packet
01738                                         len += (nip_mem_size_t)insert_ptr.ptr - section_off;
01739                                         insert_ptr.ptr = (void*) section_off;
01740                                         nip_mem_cut( &insert_ptr, len );
01741 
01742                                         // set NIP_MDNS_PTYPE_ANSWCONTINUED if necessary
01743                                         if ( nip_mdns.send == NIP_MDNS_PTYPE_QUERY )
01744                                         {
01745                                                 nip_mdns.send = NIP_MDNS_PTYPE_ANSWCONTINUED;
01746                                                 // Set Truncate-Bit in DNS header to signal more answers are
01747                                                 // coming.
01748                                                 nip_mdns_parse_packet( NIP_UDP_TX_BUF, NIP_MDNS_PTASK_SET_TC, &insert_ptr, 0 );
01749                                         }
01750                                 goto quit;
01751 
01752                 }
01753 
01754                 query_done:
01755                 // all answers sent? -> Update Query in list
01756 
01757                 // if query is not local, delete after transmission of all answers
01758                 if ( !(q.type & NIP_MDNS_QTYPE_LOCAL_BIT ) )
01759                 {
01760                         if ( NIP_E_OK == nip_mem_cut( &nip_mdns_queries, sizeof( struct nip_mdns_query ) ))
01761                         {
01762                                 // remove subscriptions
01763                                 NIP_MDNS_UNSUBSCRIBE_QUERY( q.id );
01764                         }
01765                         else
01766                         {
01767                                 // something went wrong, come back and try again, later.
01768                                 goto quit;
01769                         }
01770                 }
01771                 else
01772                 {
01773                         // save query data
01774                         l = nip_mem_ptr( &nip_mdns_queries );
01775                         if ( l == NULL )
01776                         {
01777                                 nip_error = NIP_E_LOCKED;
01778                                 goto quit;
01779                         }
01780                         nip_memcpy( l, &q, sizeof( struct nip_mdns_query ));
01781                         nip_mem_release_ptr( &nip_mdns_queries );
01782 
01783                         /// @todo remove REMOTE or PROBE Query und remove subscriptions
01784                         // increment list offset
01785                         nip_mdns_queries.ptr = (void*)((nip_mem_size_t)nip_mdns_queries.ptr + sizeof( struct nip_mdns_query ));
01786                 }
01787         }
01788 
01789 
01790         listsize = nip_mem_buf_used( nip_mdns_queries.id );
01791         if ( (nip_mem_size_t) nip_mdns_queries.ptr == listsize )
01792         {
01793                 // Checked all Cache items? -> reset offset
01794                 nip_mdns_queries.ptr = NULL;
01795 
01796                 // Reset mDNS status
01797                 nip_mdns.stat = NIP_MDNS_STAT_UP;
01798         }
01799 
01800         // Done with received Response-Packet -> release
01801         if ( nip_mdns.recv & NIP_MDNS_PTYPE_RESPONSE )
01802         {
01803                 nip_udp_cancel_recv( nip_mdns_sock );
01804                 nip_mdns.recv = NIP_MDNS_PTYPE_NONE;
01805         }
01806 
01807         quit:
01808 #if NIP_MDNS_DISCARD_ON_OUT_OF_MEMORY == 1
01809                 if ( res == NIP_E_OUT_OF_MEMORY
01810                   && count == 0
01811                   && nip_mdns.recv != NIP_MDNS_PTYPE_NONE )
01812                 {
01813                         // remove packet from receive buffer to free needed memory
01814                         nip_udp_cancel_recv( nip_mdns_sock );
01815                         nip_mdns.recv = NIP_MDNS_PTYPE_NONE;
01816 
01817                         // try again.. we might have enough space now
01818                         nip_disp_notify( NIP_DISP_CHECK_MDNS );
01819                 }
01820 #endif
01821 
01822                 if  ( nip_mdns.send != NIP_MDNS_PTYPE_NONE )
01823                 {
01824                         // transmit only if there was any data added to the packet
01825                         if ( count > 0 )
01826                                 NIP_CURR_CMD = &nip_mdns_transmit;
01827                         else
01828                         {
01829                                 nip_udp_cancel_send( nip_mdns_sock );
01830                                 nip_mdns.send = NIP_MDNS_PTYPE_NONE;
01831                         }
01832                 }
01833                 return;
01834 }
01835 
01836 /** Dispatcher function to be called upon receiving mDNS events
01837  */
01838 void nip_mdns_disp_check( void )
01839 {
01840         struct nip_dns_header    *header;
01841         struct nip_udp_sock_addr rem_addr;
01842 
01843         // mDNS ready to receive?
01844         if ( ( nip_mdns.stat == NIP_MDNS_STAT_DOWN )
01845           || ( nip_mdns.stat == NIP_MDNS_STAT_INIT
01846             && nip_mdns_init() != NIP_E_OK )
01847                 )
01848         {
01849                 // discard all incoming mdns packets
01850                 while ( nip_mdns_sock != NIP_UDP_NO_SOCKET
01851                      && nip_udp_recvfrom( nip_mdns_sock, NULL, 0, NULL ) > 0 )
01852                 {
01853                         nip_udp_cancel_recv( nip_mdns_sock );
01854                 }
01855                 return;
01856         }
01857 
01858         // Done parsing previously received mDNS packet?
01859         if ( nip_mdns.stat != NIP_MDNS_STAT_QUERY_CHECK
01860           && nip_mdns.stat != NIP_MDNS_STAT_CACHE_CHECK
01861           && nip_mdns.recv != NIP_MDNS_PTYPE_NONE )
01862         {
01863                 nip_udp_cancel_recv( nip_mdns_sock );
01864                 nip_mdns.recv = NIP_MDNS_PTYPE_NONE;
01865         }
01866 
01867         // No mDNS packet being processed right now?
01868         if ( nip_mdns.recv == NIP_MDNS_PTYPE_NONE )
01869         {
01870                 do
01871                 {
01872                         // check UDP socket for new data
01873                         nip_mdns.recv_size = nip_udp_recvfrom( nip_mdns_sock, NULL, 0, &rem_addr );
01874 
01875                         if ( nip_mdns.recv_size > 0 )
01876                         {
01877 
01878                                 /// @todo check that the packet originated on the local link
01879 
01880                                 // discard packets with invalid size
01881                                 if ( nip_mdns.recv_size < sizeof( struct nip_dns_header ) )
01882                                 {
01883                                         goto discard;
01884                                 }
01885 
01886                                 header = nip_udp_data_ptr( nip_mdns_sock, NIP_UDP_RX_BUF );
01887                                 // failed to retrieve pointer to packet data?.. try again later
01888                                 if ( header == NULL )
01889                                         break;
01890 
01891                                 // check packet type
01892                                 if ( (header->qr_opcode_aa_tc_rd & NIP_DNS_QR_MASK) > 0 )
01893                                         nip_mdns.recv = NIP_MDNS_PTYPE_RESPONSE;
01894                                 else
01895                                 {
01896                                         if ( header->qa_count[ NIP_MDNS_QDCOUNT_OFFSET ] != 0
01897                                           && header->qa_count[ NIP_MDNS_NSCOUNT_OFFSET ] != 0 )
01898                                                 nip_mdns.recv = NIP_MDNS_PTYPE_PROBE;
01899                                         else
01900                                                 nip_mdns.recv = NIP_MDNS_PTYPE_QUERY;
01901                                 }
01902 
01903                                 // check header fields, according to mDNS standard
01904                                 // Discard own Probes
01905                                 if ( (header->qr_opcode_aa_tc_rd & NIP_DNS_OP_MASK) > 0
01906                                   || (header->ra_z_ad_cd_rcode   & NIP_DNS_RC_MASK) > 0
01907                                   || ( nip_mdns.recv == NIP_MDNS_PTYPE_PROBE
01908                                     && nip_memcmp( NIP_IP_ADDR( nip_mdns_if_id ), rem_addr.ip, 4 ) == 0 )
01909                                    )
01910                                 {
01911                                         nip_udp_ptr_release( nip_mdns_sock, NIP_UDP_RX_BUF );
01912                                         goto discard;
01913                                 }
01914 
01915                                 // end header check
01916                                 nip_udp_ptr_release( nip_mdns_sock, NIP_UDP_RX_BUF );
01917                         }
01918 
01919                         continue;
01920 
01921                         discard:
01922                                 nip_mdns.recv = NIP_MDNS_PTYPE_NONE;
01923                                 nip_udp_cancel_recv( nip_mdns_sock );
01924 
01925                 // -> repeat until we have valid or no packet
01926                 } while ( nip_mdns.recv == NIP_MDNS_PTYPE_NONE && nip_mdns.recv_size > 0 );
01927         }
01928 
01929         if ( nip_mdns.stat == NIP_MDNS_STAT_QUERY_CHECK )
01930         {
01931                 NIP_CURR_CMD = &nip_mdns_disp_check_queries;
01932                 goto moveon;
01933         }
01934 
01935         // check cache
01936         NIP_CURR_CMD = &nip_mdns_disp_check_cache;
01937 
01938         /// @todo loop through queries
01939                 /// @todo -> loop through answers in packet
01940                                 /// @todo answer matches local query?
01941                                 ///       -> update cache
01942                                 ///       -> update subscriptions
01943                                 /// @todo answer matches remote query?
01944                                 ///       -> update subscriptions for matching cache entries
01945 
01946         /// @todo loop through queries
01947                 /// @todo remote query? -> loop through subscription list
01948                 ///       -> retrieve answers from cache and fill packet
01949                 /// @todo local query?
01950                 ///       -> check timer
01951                 /// @todo remove query marked for deletion and all notifications done?
01952                 ///       -> remove from list
01953                 ///       -> remove subscriptions
01954 
01955         /// @todo loop through namestrings to remove unused names
01956 
01957         moveon:
01958         return;
01959 
01960 }
01961 
01962 
01963 #endif /* NIP_ENABLE_MDNS == 1 */

Generated on Thu Jul 10 01:09:29 2008 for NIP by  doxygen 1.5.5