import HEAD into svn+ssh://svn.samba.org/home/svn/samba/trunk
[metze/old/v3-2-winbind-ndr.git] / source / wrepld / process.c
1 /* 
2    Unix SMB/CIFS implementation.
3    process incoming packets - main loop
4    Copyright (C) Jean Fran├žois Micouleau      1998-2002.
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22 #include "wins_repl.h"
23
24 extern fd_set *listen_set;
25 extern int listen_number;
26 extern int *sock_array;
27
28 WINS_OWNER global_wins_table[64][64];
29 int partner_count;
30
31 TALLOC_CTX *mem_ctx;
32
33 #define WINS_LIST "wins.tdb"
34 #define INFO_VERSION    "INFO/version"
35 #define INFO_COUNT      "INFO/num_entries"
36 #define INFO_ID_HIGH    "INFO/id_high"
37 #define INFO_ID_LOW     "INFO/id_low"
38 #define ENTRY_PREFIX    "ENTRY/"
39
40
41 /*******************************************************************
42 fill the header of a reply.
43 ********************************************************************/
44 static void fill_header(GENERIC_PACKET *g, int opcode, int ctx, int mess)
45 {
46         if (g==NULL)
47                 return;
48
49         g->header.opcode=opcode;
50         g->header.assoc_ctx=ctx;
51         g->header.mess_type=mess;
52 }
53
54 /*******************************************************************
55 dump the global table, that's a debug code.
56 ********************************************************************/
57 static void dump_global_table(void)
58 {
59         int i,j;
60         
61         for (i=0;i<partner_count;i++) {
62                 DEBUG(10,("\n%d ", i));
63                 for (j=0; global_wins_table[i][j].address.s_addr!=0; j++)
64                         DEBUG(10,("%s:%d \t", inet_ntoa(global_wins_table[i][j].address),
65                                 (int)global_wins_table[i][j].max_version));
66         }
67         DEBUG(10,("\n"));
68 }
69
70 /*******************************************************************
71 start association
72 ********************************************************************/
73 static void start_assoc_process(GENERIC_PACKET *q, GENERIC_PACKET *r)
74 {
75         /*
76          * add this request to our current wins partners list
77          * this list is used to know with who we are in contact
78          *
79          */
80         r->sa_rp.assoc_ctx=time(NULL);
81         fill_header(r, OPCODE_NON_NBT, q->sa_rq.assoc_ctx, MESSAGE_TYPE_START_ASSOC_REPLY);
82
83         /* reply we are a NT4 server */
84         
85         /* w2K is min=2, maj=5 */
86         
87         r->sa_rp.min_ver=1;
88         r->sa_rp.maj_ver=1;
89
90         add_partner(r->sa_rp.assoc_ctx, q->sa_rq.assoc_ctx, False, False);
91 }
92
93 /*******************************************************************
94 start association reply
95 ********************************************************************/
96 static void start_assoc_reply(GENERIC_PACKET *q, GENERIC_PACKET *r)
97 {
98         int i;
99
100         /* check if we have already registered this client */
101         if (!check_partner(q->header.assoc_ctx)) {
102                 DEBUG(0,("start_assoc_reply: unknown client\n"));
103                 stop_packet(q, r, STOP_REASON_USER_REASON);
104                 return;
105         }
106
107         if (!update_server_partner(q->header.assoc_ctx, q->sa_rp.assoc_ctx)) {
108                 DEBUG(0,("start_assoc_reply: can't update server ctx\n"));
109                 stop_packet(q, r, STOP_REASON_USER_REASON);
110                 return;
111         }
112
113         /* if pull, request map table */        
114         if (check_pull_partner(q->header.assoc_ctx)) {
115                 fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
116
117                 r->rep.msg_type=MESSAGE_REP_ADD_VERSION_REQUEST;
118                 DEBUG(5,("start_assoc_reply: requesting map table\n"));
119
120                 return;
121         }
122
123         /* if push, send our table */
124         if (check_push_partner(q->header.assoc_ctx)) {
125                 fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
126                 r->rep.msg_type=MESSAGE_REP_UPDATE_NOTIFY_REQUEST;
127                 r->rep.un_rq.partner_count=partner_count;
128                 
129                 r->rep.un_rq.wins_owner=(WINS_OWNER *)talloc(mem_ctx, partner_count*sizeof(WINS_OWNER));
130                 if (r->rep.un_rq.wins_owner==NULL) {
131                         DEBUG(0,("start_assoc_reply: can't alloc memory\n"));
132                         stop_packet(q, r, STOP_REASON_USER_REASON);
133                         return;
134                 }
135
136                 for (i=0; i<partner_count; i++)
137                         r->rep.un_rq.wins_owner[i]=global_wins_table[0][i];
138                 
139                 DEBUG(5,("start_assoc_reply: sending update table\n"));
140                 return;
141         }
142         
143         /* neither push/pull, stop */
144         /* we should not come here */
145         DEBUG(0,("we have a partner which is neither push nor pull !\n"));
146         stop_packet(q, r, STOP_REASON_USER_REASON);
147 }
148
149 /****************************************************************************
150 initialise and fill the in-memory partner table.
151 ****************************************************************************/
152 int init_wins_partner_table(void)
153 {
154         int i=1,j=0,k;
155         char **partner = str_list_make(lp_wins_partners(), NULL);
156
157         if (partner==NULL) {
158                 DEBUG(0,("wrepld: no partner list in smb.conf, exiting\n"));
159                 exit_server("normal exit");
160                 return(0);
161         }
162
163         DEBUG(4, ("init_wins_partner_table: partners: %s\n", lp_wins_partners()));
164
165         global_wins_table[0][0].address=*iface_n_ip(0);
166         global_wins_table[0][0].max_version=0;
167         global_wins_table[0][0].min_version=0;
168         global_wins_table[0][0].type=0;
169
170         while (partner[j]!=NULL) {
171                 DEBUG(3,("init_wins_partner_table, adding partner: %s\n", partner[j]));
172                 
173                 global_wins_table[0][i].address=*interpret_addr2(partner[j]);
174                 global_wins_table[0][i].max_version=0;
175                 global_wins_table[0][i].min_version=0;
176                 global_wins_table[0][i].type=0;
177                 global_wins_table[0][i].last_pull=0;
178                 global_wins_table[0][i].last_push=0;
179
180                 i++;
181                 j++;
182         }
183         
184         for (k=1; k<i;k++)
185                 for (j=0; j<i; j++)
186                         global_wins_table[k][j]=global_wins_table[0][j];
187         
188         str_list_free (&partner);
189         
190         return i;
191 }
192
193 /****************************************************************************
194 read the last ID from the wins tdb file.
195 ****************************************************************************/
196 static void get_our_last_id(WINS_OWNER *wins_owner)
197 {
198         TDB_CONTEXT *tdb;
199
200         tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
201         if (!tdb) {
202                 DEBUG(2,("get_our_last_id: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
203                 return;
204         }
205         
206         wins_owner->max_version=((SMB_BIG_UINT)tdb_fetch_int32(tdb, INFO_ID_HIGH))<<32 | 
207                                  (SMB_BIG_UINT)tdb_fetch_int32(tdb, INFO_ID_LOW);
208
209         tdb_close(tdb);
210 }
211
212 /****************************************************************************
213 send the list of wins server we know.
214 ****************************************************************************/
215 static void send_version_number_map_table(GENERIC_PACKET *q, GENERIC_PACKET *r)
216 {
217         int i;
218         int s_ctx=get_server_assoc(q->header.assoc_ctx);
219
220         if (s_ctx==0) {
221                 DEBUG(5, ("send_version_number_map_table: request for a partner not in our table\n"));
222                 stop_packet(q, r, STOP_REASON_USER_REASON);
223                 return;
224         }
225
226         /*
227          * return an array of wins servers, we are partner with.
228          * each entry contains the IP address and the version info
229          * version: ID of the last entry we've got
230          */
231
232         /* the first wins server must be self */
233
234         /*
235          * get our last ID from the wins database
236          * it can have been updated since last read
237          * as nmbd got registration/release.
238          */ 
239         get_our_last_id(&global_wins_table[0][0]);
240
241         r->rep.avmt_rep.wins_owner=(WINS_OWNER *)talloc(mem_ctx, partner_count*sizeof(WINS_OWNER));
242         if (r->rep.avmt_rep.wins_owner==NULL) {
243                 stop_packet(q, r, STOP_REASON_USER_REASON);
244                 return;
245         }
246         
247         DEBUG(5,("send_version_number_map_table: partner_count: %d\n", partner_count));
248
249         for (i=0; i<partner_count; i++) {
250                 DEBUG(5,("send_version_number_map_table, partner: %d -> %s, \n", i, inet_ntoa(global_wins_table[0][i].address)));
251                 r->rep.avmt_rep.wins_owner[i]=global_wins_table[0][i];
252         }
253         
254         r->rep.msg_type=1;
255         r->rep.avmt_rep.partner_count=partner_count;
256         r->rep.avmt_rep.initiating_wins_server.s_addr=0; /* blatant lie, NT4/w2K do the same ! */
257         fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
258 }
259
260 /****************************************************************************
261 for a given partner, ask it to send entries we don't have.
262 ****************************************************************************/
263 static BOOL check_partners_and_send_entries(GENERIC_PACKET *q, GENERIC_PACKET *r, int partner)
264 {
265         int server;
266         int other;
267         SMB_BIG_UINT temp;
268         SMB_BIG_UINT current;
269
270
271         /*
272          * we check if our partner has more records than us.
273          * we need to check more than our direct partners as
274          * we can have this case:
275          * us: A, partners: B,C, indirect partner: D
276          * A<->B, A<->C, B<->D, C<->D
277          *
278          * So if we're talking to B, we need to check if between
279          * B and C, which one have more records about D.
280          * and also check if we don't already have the records.
281          */
282
283
284          /* check all servers even indirect */
285          for (server=1; global_wins_table[0][server].address.s_addr!=0; server++) {
286                 current = global_wins_table[partner][server].max_version;
287                 
288                 temp=0;
289                 
290                 for (other=1; other<partner_count; other++) {
291                         /* skip the partner itself */
292                         if (other==partner)
293                                 continue;
294
295                         if (global_wins_table[other][server].max_version > temp)
296                                 temp=global_wins_table[other][server].max_version;
297                 }
298                 
299                 if (current >= temp && current > global_wins_table[0][server].max_version) {
300                         /* 
301                          * it has more records than every body else and more than us,
302                          * ask it the difference between what we have and what it has
303                          */
304                         fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
305
306                         r->rep.msg_type=MESSAGE_REP_SEND_ENTRIES_REQUEST;
307                         r->rep.se_rq.wins_owner.address=global_wins_table[partner][server].address;
308                         
309                         r->rep.se_rq.wins_owner.max_version=global_wins_table[partner][server].max_version;
310                         r->rep.se_rq.wins_owner.min_version=global_wins_table[0][server].max_version;
311                         r->rep.se_rq.wins_owner.type=0;
312                         
313                         write_server_assoc_table(q->header.assoc_ctx, global_wins_table[0][partner].address, global_wins_table[partner][server].address);
314                         
315                         /*
316                          * and we update our version for this server
317                          * as we can't use the IDs returned in the send_entries function
318                          * the max ID can be larger than the largest ID returned
319                          */
320                         
321                         global_wins_table[0][server].max_version=global_wins_table[partner][server].max_version;
322
323                         return True;
324                 }
325         }
326         return False;
327 }
328         
329 /****************************************************************************
330 receive the list of wins server we know.
331 ****************************************************************************/
332 static void receive_version_number_map_table(GENERIC_PACKET *q, GENERIC_PACKET *r)
333 {
334         fstring peer;
335         struct in_addr addr;
336         int i,j,k,l;
337         int s_ctx=get_server_assoc(q->header.assoc_ctx);
338
339         if (s_ctx==0) {
340                 DEBUG(5, ("receive_version_number_map_table: request for a partner not in our table\n"));
341                 stop_packet(q, r, STOP_REASON_USER_REASON);
342                 return;
343         }
344
345         fstrcpy(peer,get_peer_addr(q->fd));
346         addr=*interpret_addr2(peer);
347
348         get_our_last_id(&global_wins_table[0][0]);
349         
350         DEBUG(5,("receive_version_number_map_table: received a map of %d server from: %s\n", 
351                   q->rep.avmt_rep.partner_count ,inet_ntoa(q->rep.avmt_rep.initiating_wins_server)));
352         DEBUG(5,("real peer is: %s\n", peer));
353
354         for (i=0; global_wins_table[0][i].address.s_addr!=addr.s_addr && i<partner_count;i++)
355                 ;
356
357         if (i==partner_count) {
358                 DEBUG(5,("receive_version_number_map_table: unknown partner: %s\n", peer));
359                 stop_packet(q, r, STOP_REASON_USER_REASON);
360                 return;
361         }
362
363         for (j=0; j<q->rep.avmt_rep.partner_count;j++) {
364                 /*
365                  * search if we already have this entry or if it's a new one
366                  * it can be a new one in case of propagation
367                  */
368                 for (k=0; global_wins_table[0][k].address.s_addr!=0 && 
369                           global_wins_table[0][k].address.s_addr!=q->rep.avmt_rep.wins_owner[j].address.s_addr; k++);
370
371                 global_wins_table[i][k].address.s_addr=q->rep.avmt_rep.wins_owner[j].address.s_addr;
372                 global_wins_table[i][k].max_version=q->rep.avmt_rep.wins_owner[j].max_version;
373                 global_wins_table[i][k].min_version=q->rep.avmt_rep.wins_owner[j].min_version;
374                 global_wins_table[i][k].type=q->rep.avmt_rep.wins_owner[j].type;
375                 
376                 /*
377                  * in case it's a new one, rewrite the address for all the partner
378                  * to reserve the slot.
379                  */
380
381                 for(l=0; l<partner_count; l++)
382                         global_wins_table[l][k].address.s_addr=q->rep.avmt_rep.wins_owner[j].address.s_addr;
383         }
384
385         dump_global_table();
386
387         /*
388          * if this server have newer records than what we have
389          * for several wins servers, we need to ask it.
390          * Alas a send entry request is only on one server.
391          * So in the send entry reply, we'll ask for the next server if required.
392          */
393
394         if (check_partners_and_send_entries(q, r, i))
395                 return;
396
397         /* it doesn't have more entries than us */
398         stop_packet(q, r, STOP_REASON_USER_REASON);
399 }
400
401 /****************************************************************************
402 add an entry to the wins list we'll send.
403 ****************************************************************************/
404 static BOOL add_record_to_winsname(WINS_NAME **wins_name, int *max_names, char *name, int type, int wins_flags, int id, struct in_addr *ip_list, int num_ips)
405 {
406         WINS_NAME *temp_list;
407         int i;
408         int current=*max_names;
409
410         temp_list=talloc_realloc(mem_ctx, *wins_name, (current+1)*sizeof(WINS_NAME));
411         if (temp_list==NULL)
412                 return False;
413
414         temp_list[current].name_len=0x11;
415         
416         safe_strcpy(temp_list[current].name, name, 15);
417
418         temp_list[current].type=type;
419         temp_list[current].empty=0;
420
421         temp_list[current].name_flag=wins_flags;
422
423         if ( (wins_flags&0x03) == 1 || (wins_flags&0x03)==2)
424                 temp_list[current].group_flag=0x01000000;
425         else
426                 temp_list[current].group_flag=0x00000000;
427         
428         temp_list[current].id=id;
429         
430         temp_list[current].owner.s_addr=ip_list[0].s_addr;
431
432         if (temp_list[current].name_flag & 2) {
433                 temp_list[current].num_ip=num_ips;
434                 temp_list[current].others=(struct in_addr *)talloc(mem_ctx, sizeof(struct in_addr)*num_ips);
435                 if (temp_list[current].others==NULL)
436                         return False;
437         
438                 for (i=0; i<num_ips; i++)
439                         temp_list[current].others[i].s_addr=ip_list[i].s_addr;
440
441         } else 
442                 temp_list[current].num_ip=1;
443
444         temp_list[current].foo=0xffffffff;
445
446         *wins_name=temp_list;
447         
448         return True;
449 }
450
451 /****************************************************************************
452 send the list of name we have.
453 ****************************************************************************/
454 static void send_entry_request(GENERIC_PACKET *q, GENERIC_PACKET *r)
455 {
456         int max_names=0;
457         int i;
458         time_t time_now = time(NULL);
459         WINS_OWNER *wins_owner;
460         TDB_CONTEXT *tdb;
461         TDB_DATA kbuf, dbuf, newkey;
462         int s_ctx=get_server_assoc(q->header.assoc_ctx);
463         int num_interfaces = iface_count();
464
465         if (s_ctx==0) {
466                 DEBUG(1, ("send_entry_request: request for a partner not in our table\n"));
467                 stop_packet(q, r, STOP_REASON_USER_REASON);
468                 return;
469         }
470
471
472         wins_owner=&q->rep.se_rq.wins_owner;
473         r->rep.se_rp.wins_name=NULL;
474
475         DEBUG(3,("send_entry_request: we have been asked to send the list of wins records\n"));
476         DEBUGADD(3,("owned by: %s and between min: %d and max: %d\n", inet_ntoa(wins_owner->address),
477                     (int)wins_owner->min_version, (int)wins_owner->max_version));
478
479         /*
480          * if we are asked to send records owned by us
481          * we overwrite the wins ip with 0.0.0.0
482          * to make it easy in case of multihomed
483          */
484
485         for (i=0; i<num_interfaces; i++)
486                 if (ip_equal(wins_owner->address, *iface_n_ip(i))) {
487                         wins_owner->address=*interpret_addr2("0.0.0.0");
488                         break;
489                 }
490
491
492         tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
493         if (!tdb) {
494                 DEBUG(2,("send_entry_request: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
495                 return;
496         }
497
498         for (kbuf = tdb_firstkey(tdb); 
499              kbuf.dptr; 
500              newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
501                 fstring name_type;
502                 pstring name, ip_str;
503                 char *p;
504                 int type = 0;
505                 int nb_flags;
506                 int ttl;
507                 unsigned int num_ips;
508                 int low, high;
509                 SMB_BIG_UINT version;
510                 struct in_addr wins_ip;
511                 struct in_addr *ip_list;
512                 int wins_flags;
513                 int len;
514
515                 if (strncmp(kbuf.dptr, ENTRY_PREFIX, strlen(ENTRY_PREFIX)) != 0)
516                         continue;
517                 
518                 
519                 dbuf = tdb_fetch(tdb, kbuf);
520                 if (!dbuf.dptr)
521                         continue;
522
523                 fstrcpy(name_type, kbuf.dptr+strlen(ENTRY_PREFIX));
524                 pstrcpy(name, name_type);
525
526                 if((p = strchr(name,'#')) != NULL) {
527                         *p = 0;
528                         sscanf(p+1,"%x",&type);
529                 }
530
531                 len = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddfddd",
532                                 &nb_flags,
533                                 &high,
534                                 &low,
535                                 ip_str,
536                                 &ttl, 
537                                 &num_ips,
538                                 &wins_flags);
539
540                 wins_ip=*interpret_addr2(ip_str);
541
542                 /* Allocate the space for the ip_list. */
543                 if((ip_list = (struct in_addr *)talloc(mem_ctx,  num_ips * sizeof(struct in_addr))) == NULL) {
544                         SAFE_FREE(dbuf.dptr);
545                         DEBUG(0,("initialise_wins: talloc fail !\n"));
546                         return;
547                 }
548
549                 for (i = 0; i < num_ips; i++) {
550                         len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f", ip_str);
551                         ip_list[i] = *interpret_addr2(ip_str);
552                 }
553
554                 SAFE_FREE(dbuf.dptr);
555
556                 /* add all entries that have 60 seconds or more to live */
557                 if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) {
558                         if(ttl != PERMANENT_TTL)
559                                 ttl -= time_now;
560     
561                         DEBUG( 4, ("send_entry_request: add name: %s#%02x ttl = %d first IP %s flags = %2x\n",
562                             name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
563
564                         /* add the record to the list to send */
565                         version=((SMB_BIG_UINT)high)<<32 | low;
566                         
567                         if (wins_owner->min_version<=version && wins_owner->max_version>=version &&
568                             wins_owner->address.s_addr==wins_ip.s_addr) {
569                                 if(!add_record_to_winsname(&r->rep.se_rp.wins_name, &max_names, name, type, wins_flags, version, ip_list, num_ips))
570                                         return;
571                                 max_names++;
572                         }
573
574                 } else {
575                         DEBUG(4, ("send_entry_request: not adding name (ttl problem) %s#%02x ttl = %d first IP %s flags = %2x\n",
576                                   name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
577                 }
578         }
579     
580         tdb_close(tdb);
581
582         DEBUG(4,("send_entry_request, sending %d records\n", max_names));
583         fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
584         r->rep.msg_type=MESSAGE_REP_SEND_ENTRIES_REPLY; /* reply */
585         r->rep.se_rp.max_names=max_names;
586 }
587
588
589 /****************************************************************************
590 .
591 ****************************************************************************/
592 static void update_notify_request(GENERIC_PACKET *q, GENERIC_PACKET *r)
593 {
594         int i,j,k,l;
595         UPDATE_NOTIFY_REQUEST *u;
596         int s_ctx=get_server_assoc(q->header.assoc_ctx);
597         
598         if (s_ctx==0) {
599                 DEBUG(4, ("update_notify_request: request for a partner not in our table\n"));
600                 stop_packet(q, r, STOP_REASON_USER_REASON);
601                 return;
602         }
603
604         u=&q->rep.un_rq;
605
606         /* check if we already have the range of records */
607
608         DEBUG(5,("update_notify_request: wins server: %s offered this list of %d records:\n",
609                 inet_ntoa(u->initiating_wins_server), u->partner_count));
610
611         get_our_last_id(&global_wins_table[0][0]);
612
613         for (i=0; i<partner_count; i++) {
614                 if (global_wins_table[0][i].address.s_addr==u->initiating_wins_server.s_addr) {
615                         DEBUG(5,("update_notify_request: found initiator at index %d\n", i));
616                         break;
617                 }
618         }
619
620         /*
621          * some explanation is required, before someone say it's crap.
622          *
623          * let's take an example, we have 2 wins partners, we already now
624          * that our max id is 10, partner 1 ID is 20 and partner 2 ID is 30
625          * the array looks like:
626          *
627          *      0       1       2
628          * 0    10      20      30  
629          * 1
630          * 2
631          *
632          * we receive an update from partner 2 saying he has: 1:15, 2:40, 3:50
633          * we must enlarge the array to add partner 3, it will look like:
634          *
635          *      0       1       2       3
636          * 0    10      20      30
637          * 1
638          * 2            15      40      50
639          *
640          * now we know, we should pull from partner 2, the records 30->40 of 2 and 0->50 of 3.
641          * once the pull will be over, our table will look like:
642          *
643          *      0       1       2       3
644          * 0    10      20      40      50
645          * 1
646          * 2            15      40      50
647          *
648          *
649          */
650
651         for (j=0; j<u->partner_count;j++) {
652                 /*
653                  * search if we already have this entry or if it's a new one
654                  * it can be a new one in case of propagation
655                  */
656
657                 for (k=0; global_wins_table[0][k].address.s_addr!=0 && 
658                           global_wins_table[0][k].address.s_addr!=u->wins_owner[j].address.s_addr; k++);
659
660                 global_wins_table[i][k].address.s_addr=u->wins_owner[j].address.s_addr;
661                 global_wins_table[i][k].max_version=u->wins_owner[j].max_version;
662                 global_wins_table[i][k].min_version=u->wins_owner[j].min_version;
663                 global_wins_table[i][k].type=u->wins_owner[j].type;
664                 
665                 /*
666                  * in case it's a new one, rewrite the address for all the partner
667                  * to reserve the slot.
668                  */
669
670                 for(l=0; l<partner_count; l++)
671                         global_wins_table[l][k].address.s_addr=u->wins_owner[j].address.s_addr;
672         }
673
674         dump_global_table();
675
676         stop_packet(q, r, STOP_REASON_USER_REASON);
677 }
678
679 /****************************************************************************
680 .
681 ****************************************************************************/
682 static void send_entry_reply(GENERIC_PACKET *q, GENERIC_PACKET *r)
683 {
684         int i,j,k;
685         struct in_addr partner, server;
686         pid_t pid;
687         int s_ctx=get_server_assoc(q->header.assoc_ctx);
688         WINS_RECORD record;
689         
690         if (s_ctx==0) {
691                 DEBUG(1, ("send_entry_reply: request for a partner not in our table\n"));
692                 stop_packet(q, r, STOP_REASON_USER_REASON);
693                 return;
694         }
695
696         DEBUG(5,("send_entry_reply:got %d new records\n", q->rep.se_rp.max_names));
697
698         /* we got records from a wins partner but that can be from another wins server */
699         /* hopefully we track that */
700
701         /* and the only doc available from MS is wrong ! */
702
703         get_server_assoc_table(q->header.assoc_ctx, &partner, &server);
704
705         for (j=0; global_wins_table[0][j].address.s_addr!=0; j++) {
706                 if (global_wins_table[0][j].address.s_addr==server.s_addr) {
707                         DEBUG(5,("send_entry_reply: found server at index %d\n", j));
708                         break;
709                 }
710         }
711
712         pid = pidfile_pid("nmbd");
713         if (pid == 0) {
714                 DEBUG(0,("send_entry_reply: Can't find pid for nmbd\n"));
715                 return;
716         }
717
718         for (k=0; k<q->rep.se_rp.max_names; k++) {
719                 DEBUG(5,("send_entry_reply: %s<%02x> %d\n", q->rep.se_rp.wins_name[k].name, q->rep.se_rp.wins_name[k].type,
720                          (int)q->rep.se_rp.wins_name[k].id));
721
722                 safe_strcpy(record.name, q->rep.se_rp.wins_name[k].name, 16);
723                 record.type=q->rep.se_rp.wins_name[k].type;
724                 record.id=q->rep.se_rp.wins_name[k].id;
725                 record.wins_flags=q->rep.se_rp.wins_name[k].name_flag&0x00ff;
726                 record.num_ips=q->rep.se_rp.wins_name[k].num_ip;
727
728                 record.wins_ip.s_addr=server.s_addr;
729
730                 if (record.num_ips==1)
731                         record.ip[0]=q->rep.se_rp.wins_name[k].owner;
732                 else
733                         for (i=0; i<record.num_ips; i++)
734                                 record.ip[i]=q->rep.se_rp.wins_name[k].others[i];
735
736                 record.nb_flags=0;
737
738                 if (record.wins_flags&WINS_NGROUP || record.wins_flags&WINS_SGROUP)
739                         record.nb_flags|=NB_GROUP;
740                 
741                 if (record.wins_flags&WINS_ACTIVE)
742                         record.nb_flags|=NB_ACTIVE;
743                 
744                 record.nb_flags|=record.wins_flags&WINS_HNODE;
745                 
746                 message_send_pid(pid, MSG_WINS_NEW_ENTRY, &record, sizeof(record), False);
747
748         }
749
750         dump_global_table();
751
752         /*
753          * we got some entries, 
754          * ask the partner to send us the map table again
755          * to get the other servers entries.
756          *
757          * we're getting the map table 1 time more than really
758          * required. We could remove that call, but that
759          * would complexify the code. I prefer this trade-of. 
760          */
761         fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
762
763         r->rep.msg_type=MESSAGE_REP_ADD_VERSION_REQUEST;
764 }
765
766 /****************************************************************************
767 decode the replication message and reply.
768 ****************************************************************************/
769 static void replicate(GENERIC_PACKET *q, GENERIC_PACKET *r)
770 {
771         switch (q->rep.msg_type) {
772                 case 0:
773                         /* add version number map table request */
774                         send_version_number_map_table(q, r);
775                         break;
776                 case 1:
777                         receive_version_number_map_table(q, r);
778                         break;
779                 case 2:
780                         /* send entry request */
781                         send_entry_request(q, r);
782                         break;
783                 case 3:
784                         /* send entry reply */
785                         send_entry_reply(q, r);
786                         break;
787                 case 4:
788                         /* update notification request */
789                         update_notify_request(q, r);
790                         break;
791         }
792 }
793
794 /****************************************************************************
795 do a switch on the message type, and return the response size
796 ****************************************************************************/
797 static BOOL switch_message(GENERIC_PACKET *q, GENERIC_PACKET *r)
798 {
799         switch (q->header.mess_type) {
800                 case 0:
801                         /* Start association type */                    
802                         start_assoc_process(q, r);
803                         return True;
804                         break;
805                 case 1:
806                         /* start association reply */
807                         start_assoc_reply(q, r);
808                         return True;
809                         break;
810                 case 2:
811                         /* stop association message */
812                         return False;
813                         break;
814                 case 3:
815                         /* replication message */
816                         replicate(q, r);
817                         return True;
818                         break;
819         }
820
821         return False;
822 }
823
824
825 /****************************************************************************
826   construct a reply to the incoming packet
827 ****************************************************************************/
828 void construct_reply(struct wins_packet_struct *p)
829 {
830         GENERIC_PACKET r;
831         struct BUFFER buffer;
832
833         buffer.buffer=NULL;
834         buffer.offset=0;
835         buffer.length=0;
836
837         DEBUG(5,("dump: received packet\n"));
838         dump_generic_packet(p->packet);
839
840         /* Verify if the request we got is from a listed partner */
841         if (!check_partner(p->packet->header.assoc_ctx)) {
842                 fstring peer;
843                 struct in_addr addr;
844                 int i;
845                 fstrcpy(peer,get_peer_addr(p->fd));
846                 addr=*interpret_addr2(peer);
847
848                 for (i=1; i<partner_count; i++)
849                         if (ip_equal(addr, global_wins_table[0][i].address))
850                                 break;
851
852                 if (i==partner_count) {
853                         DEBUG(1,("construct_reply: got a request from a non peer machine: %s\n", peer));
854                         stop_packet(p->packet, &r, STOP_REASON_AUTH_FAILED);
855                         p->stop_packet=True;
856                         encode_generic_packet(&buffer, &r);
857                         if (!send_smb(p->fd, buffer.buffer))
858                                 exit_server("process_smb: send_smb failed.");
859                         return;
860                 }
861         }
862
863         if (switch_message(p->packet, &r)) {
864                 encode_generic_packet(&buffer, &r);
865                 DEBUG(5,("dump: sending packet\n"));
866                 dump_generic_packet(&r);
867
868                 if(buffer.offset > 0) {
869                         if (!send_smb(p->fd, buffer.buffer))
870                                 exit_server("process_smb: send_smb failed.");
871                 }
872         }
873
874         /* if we got a stop assoc or if we send a stop assoc, close the fd after */
875         if (p->packet->header.mess_type==MESSAGE_TYPE_STOP_ASSOC || 
876             r.header.mess_type==MESSAGE_TYPE_STOP_ASSOC) {
877                 remove_partner(p->packet->header.assoc_ctx);
878                 p->stop_packet=True;
879         }
880 }
881
882 /****************************************************************************
883   contact periodically our wins partner to do a pull replication
884 ****************************************************************************/
885 void run_pull_replication(time_t t)
886 {
887         /* we pull every 30 minutes to query about new records*/
888         int i, s;
889         struct BUFFER buffer;
890         GENERIC_PACKET p;
891
892         buffer.buffer=NULL;
893         buffer.offset=0;
894         buffer.length=0;
895
896         for (i=1; i<partner_count; i++) {
897                 if (global_wins_table[0][i].last_pull < t) {
898                         global_wins_table[0][i].last_pull=t+30*60; /* next in 30 minutes */
899                         
900                         /* contact the wins server */
901                         p.header.mess_type=MESSAGE_TYPE_START_ASSOC_REQUEST;
902                         p.header.opcode=OPCODE_NON_NBT;
903                         p.header.assoc_ctx=0;
904                         p.sa_rq.assoc_ctx=(int)t;
905                         p.sa_rq.min_ver=1;
906                         p.sa_rq.maj_ver=1;
907                         
908                         DEBUG(3,("run_pull_replication: contacting wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
909                         encode_generic_packet(&buffer, &p);
910                         dump_generic_packet(&p);
911
912                         /* send the packet to the server and add the descriptor to receive answers */
913                         s=open_socket_out(SOCK_STREAM, &global_wins_table[0][i].address, 42, LONG_CONNECT_TIMEOUT);
914                         if (s==-1) {
915                                 DEBUG(0,("run_pull_replication: can't contact wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
916                                 return;
917                         }
918                         
919                         if(buffer.offset > 0) {
920                                 if (!send_smb(s, buffer.buffer))
921                                         exit_server("run_pull_replication: send_smb failed.");
922                         }
923                         
924                         add_fd_to_sock_array(s);
925                         FD_SET(s, listen_set);
926
927                         /* add ourself as a client */
928                         add_partner((int)t, 0, True, False);
929                 }
930         }
931 }
932
933 /****************************************************************************
934   contact periodically our wins partner to do a push replication
935 ****************************************************************************/
936 void run_push_replication(time_t t)
937 {
938         /* we push every 30 minutes or 25 new entries */
939         int i, s;
940         struct BUFFER buffer;
941         GENERIC_PACKET p;
942
943         buffer.buffer=NULL;
944         buffer.offset=0;
945         buffer.length=0;
946
947         for (i=1; i<partner_count; i++) {
948                 if (global_wins_table[0][i].last_pull < t) {
949                         global_wins_table[0][i].last_pull=t+30*60; /* next in 30 minutes */
950                         
951                         /* contact the wins server */
952                         p.header.mess_type=MESSAGE_TYPE_START_ASSOC_REQUEST;
953                         p.header.opcode=OPCODE_NON_NBT;
954                         p.header.assoc_ctx=0;
955                         p.sa_rq.assoc_ctx=(int)t;
956                         p.sa_rq.min_ver=1;
957                         p.sa_rq.maj_ver=1;
958                         
959                         DEBUG(3,("run_push_replication: contacting wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
960                         encode_generic_packet(&buffer, &p);
961                         dump_generic_packet(&p);
962
963                         /* send the packet to the server and add the descriptor to receive answers */
964                         s=open_socket_out(SOCK_STREAM, &global_wins_table[0][i].address, 42, LONG_CONNECT_TIMEOUT);
965                         if (s==-1) {
966                                 DEBUG(0,("run_push_replication: can't contact wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
967                                 return;
968                         }
969                         
970                         if(buffer.offset > 0) {
971                                 if (!send_smb(s, buffer.buffer))
972                                         exit_server("run_push_replication: send_smb failed.");
973                         }
974                         
975                         add_fd_to_sock_array(s);
976                         FD_SET(s, listen_set);
977
978                         /* add ourself as a client */
979                         add_partner((int)t, 0, False, True);
980                 }
981         }
982 }
983