diff options
Diffstat (limited to 'source4/wrepld')
-rw-r--r-- | source4/wrepld/parser.c | 759 | ||||
-rw-r--r-- | source4/wrepld/partners.c | 200 | ||||
-rw-r--r-- | source4/wrepld/process.c | 983 | ||||
-rw-r--r-- | source4/wrepld/server.c | 737 | ||||
-rw-r--r-- | source4/wrepld/socket.c | 69 | ||||
-rw-r--r-- | source4/wrepld/wins_repl.h | 161 |
6 files changed, 2909 insertions, 0 deletions
diff --git a/source4/wrepld/parser.c b/source4/wrepld/parser.c new file mode 100644 index 0000000000..b619cb0cef --- /dev/null +++ b/source4/wrepld/parser.c @@ -0,0 +1,759 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Jean François Micouleau 1998-2002. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" +#include "wins_repl.h" + +extern TALLOC_CTX *mem_ctx; + +/**************************************************************************** +grow the send buffer if necessary +****************************************************************************/ +BOOL grow_buffer(struct BUFFER *buffer, int more) +{ + char *temp; + + DEBUG(10,("grow_buffer: size is: %d offet is:%d growing by %d\n", buffer->length, buffer->offset, more)); + + /* grow by at least 256 bytes */ + if (more<256) + more=256; + + if (buffer->offset+more >= buffer->length) { + temp=(char *)talloc_realloc(mem_ctx, buffer->buffer, sizeof(char)* (buffer->length+more) ); + if (temp==NULL) { + DEBUG(0,("grow_buffer: can't grow buffer\n")); + return False; + } + buffer->length+=more; + buffer->buffer=temp; + } + + return True; +} + +/**************************************************************************** +check if the buffer has that much data +****************************************************************************/ +static BOOL check_buffer(struct BUFFER *buffer, int more) +{ + DEBUG(10,("check_buffer: size is: %d offet is:%d growing by %d\n", buffer->length, buffer->offset, more)); + + if (buffer->offset+more > buffer->length) { + DEBUG(10,("check_buffer: buffer smaller than requested, size is: %d needed: %d\n", buffer->length, buffer->offset+more)); + return False; + } + + return True; +} + +/**************************************************************************** +decode a WINS_OWNER struct +****************************************************************************/ +static void decode_wins_owner(struct BUFFER *inbuf, WINS_OWNER *wins_owner) +{ + if(!check_buffer(inbuf, 24)) + return; + + wins_owner->address.s_addr=IVAL(inbuf->buffer, inbuf->offset); + wins_owner->max_version=((SMB_BIG_UINT)RIVAL(inbuf->buffer, inbuf->offset+4))<<32; + wins_owner->max_version|=RIVAL(inbuf->buffer, inbuf->offset+8); + wins_owner->min_version=((SMB_BIG_UINT)RIVAL(inbuf->buffer, inbuf->offset+12))<<32; + wins_owner->min_version|=RIVAL(inbuf->buffer, inbuf->offset+16); + wins_owner->type=RIVAL(inbuf->buffer, inbuf->offset+20); + inbuf->offset+=24; + +} + +/**************************************************************************** +decode a WINS_NAME struct +****************************************************************************/ +static void decode_wins_name(struct BUFFER *outbuf, WINS_NAME *wins_name) +{ + char *p; + int i; + + if(!check_buffer(outbuf, 40)) + return; + + wins_name->name_len=RIVAL(outbuf->buffer, outbuf->offset); + outbuf->offset+=4; + memcpy(wins_name->name,outbuf->buffer+outbuf->offset, 15); + wins_name->name[15]='\0'; + if((p = strchr(wins_name->name,' ')) != NULL) + *p = 0; + + outbuf->offset+=15; + + wins_name->type=(int)outbuf->buffer[outbuf->offset++]; + + /* + * fix to bug in WINS replication, + * present in all versions including W2K SP2 ! + */ + if (wins_name->name[0]==0x1B) { + wins_name->name[0]=(char)wins_name->type; + wins_name->type=0x1B; + } + + wins_name->empty=RIVAL(outbuf->buffer, outbuf->offset); + outbuf->offset+=4; + + wins_name->name_flag=RIVAL(outbuf->buffer, outbuf->offset); + outbuf->offset+=4; + wins_name->group_flag=RIVAL(outbuf->buffer, outbuf->offset); + outbuf->offset+=4; + wins_name->id=((SMB_BIG_UINT)RIVAL(outbuf->buffer, outbuf->offset))<<32; + outbuf->offset+=4; + wins_name->id|=RIVAL(outbuf->buffer, outbuf->offset); + outbuf->offset+=4; + + /* special groups have multiple address */ + if (wins_name->name_flag & 2) { + if(!check_buffer(outbuf, 4)) + return; + wins_name->num_ip=IVAL(outbuf->buffer, outbuf->offset); + outbuf->offset+=4; + } + else + wins_name->num_ip=1; + + if(!check_buffer(outbuf, 4)) + return; + wins_name->owner.s_addr=IVAL(outbuf->buffer, outbuf->offset); + outbuf->offset+=4; + + if (wins_name->name_flag & 2) { + wins_name->others=(struct in_addr *)talloc(mem_ctx, sizeof(struct in_addr)*wins_name->num_ip); + if (wins_name->others==NULL) + return; + + if(!check_buffer(outbuf, 4*wins_name->num_ip)) + return; + for (i=0; i<wins_name->num_ip; i++) { + wins_name->others[i].s_addr=IVAL(outbuf->buffer, outbuf->offset); + outbuf->offset+=4; + } + } + + if(!check_buffer(outbuf, 4)) + return; + wins_name->foo=RIVAL(outbuf->buffer, outbuf->offset); + outbuf->offset+=4; + +} + +/**************************************************************************** +decode a update notification request +****************************************************************************/ +static void decode_update_notify_request(struct BUFFER *inbuf, UPDATE_NOTIFY_REQUEST *un_rq) +{ + int i; + + if(!check_buffer(inbuf, 4)) + return; + un_rq->partner_count=RIVAL(inbuf->buffer, inbuf->offset); + inbuf->offset+=4; + + un_rq->wins_owner=(WINS_OWNER *)talloc(mem_ctx, un_rq->partner_count*sizeof(WINS_OWNER)); + if (un_rq->wins_owner==NULL) + return; + + for (i=0; i<un_rq->partner_count; i++) + decode_wins_owner(inbuf, &un_rq->wins_owner[i]); + + if(!check_buffer(inbuf, 4)) + return; + un_rq->initiating_wins_server.s_addr=IVAL(inbuf->buffer, inbuf->offset); + inbuf->offset+=4; +} + +/**************************************************************************** +decode a send entries request +****************************************************************************/ +static void decode_send_entries_request(struct BUFFER *inbuf, SEND_ENTRIES_REQUEST *se_rq) +{ + decode_wins_owner(inbuf, &se_rq->wins_owner); +} + +/**************************************************************************** +decode a send entries reply +****************************************************************************/ +static void decode_send_entries_reply(struct BUFFER *inbuf, SEND_ENTRIES_REPLY *se_rp) +{ + int i; + + if(!check_buffer(inbuf, 4)) + return; + se_rp->max_names = RIVAL(inbuf->buffer, inbuf->offset); + inbuf->offset+=4; + + se_rp->wins_name=(WINS_NAME *)talloc(mem_ctx, se_rp->max_names*sizeof(WINS_NAME)); + if (se_rp->wins_name==NULL) + return; + + for (i=0; i<se_rp->max_names; i++) + decode_wins_name(inbuf, &se_rp->wins_name[i]); +} + +/**************************************************************************** +decode a add version number map table reply +****************************************************************************/ +static void decode_add_version_number_map_table_reply(struct BUFFER *inbuf, AVMT_REP *avmt_rep) +{ + int i; + + if(!check_buffer(inbuf, 4)) + return; + + avmt_rep->partner_count=RIVAL(inbuf->buffer, inbuf->offset); + inbuf->offset+=4; + + avmt_rep->wins_owner=(WINS_OWNER *)talloc(mem_ctx, avmt_rep->partner_count*sizeof(WINS_OWNER)); + if (avmt_rep->wins_owner==NULL) + return; + + for (i=0; i<avmt_rep->partner_count; i++) + decode_wins_owner(inbuf, &avmt_rep->wins_owner[i]); + + if(!check_buffer(inbuf, 4)) + return; + avmt_rep->initiating_wins_server.s_addr=IVAL(inbuf->buffer, inbuf->offset); + inbuf->offset+=4; +} + +/**************************************************************************** +decode a replicate packet and fill a structure +****************************************************************************/ +static void decode_replicate(struct BUFFER *inbuf, REPLICATE *rep) +{ + if(!check_buffer(inbuf, 4)) + return; + + rep->msg_type = RIVAL(inbuf->buffer, inbuf->offset); + + inbuf->offset+=4; + + switch (rep->msg_type) { + case 0: + break; + case 1: + /* add version number map table reply */ + decode_add_version_number_map_table_reply(inbuf, &rep->avmt_rep); + break; + case 2: + /* send entry request */ + decode_send_entries_request(inbuf, &rep->se_rq); + break; + case 3: + /* send entry request */ + decode_send_entries_reply(inbuf, &rep->se_rp); + break; + case 4: + /* update notification request */ + decode_update_notify_request(inbuf, &rep->un_rq); + break; + default: + DEBUG(0,("decode_replicate: unknown message type:%d\n", rep->msg_type)); + break; + } +} + +/**************************************************************************** +read the generic header and fill the struct. +****************************************************************************/ +static void read_generic_header(struct BUFFER *inbuf, generic_header *q) +{ + if(!check_buffer(inbuf, 16)) + return; + + q->data_size = RIVAL(inbuf->buffer, inbuf->offset+0); + q->opcode = RIVAL(inbuf->buffer, inbuf->offset+4); + q->assoc_ctx = RIVAL(inbuf->buffer, inbuf->offset+8); + q->mess_type = RIVAL(inbuf->buffer, inbuf->offset+12); +} + +/******************************************************************* +decode a start association request +********************************************************************/ +static void decode_start_assoc_request(struct BUFFER *inbuf, START_ASSOC_REQUEST *q) +{ + if(!check_buffer(inbuf, 8)) + return; + + q->assoc_ctx = RIVAL(inbuf->buffer, inbuf->offset+0); + q->min_ver = RSVAL(inbuf->buffer, inbuf->offset+4); + q->maj_ver = RSVAL(inbuf->buffer, inbuf->offset+6); +} + +/******************************************************************* +decode a start association reply +********************************************************************/ +static void decode_start_assoc_reply(struct BUFFER *inbuf, START_ASSOC_REPLY *r) +{ + if(!check_buffer(inbuf, 8)) + return; + + r->assoc_ctx=RIVAL(inbuf->buffer, inbuf->offset+0); + r->min_ver = RSVAL(inbuf->buffer, inbuf->offset+4); + r->maj_ver = RSVAL(inbuf->buffer, inbuf->offset+6); +} + +/******************************************************************* +decode a start association reply +********************************************************************/ +static void decode_stop_assoc(struct BUFFER *inbuf, STOP_ASSOC *r) +{ + if(!check_buffer(inbuf, 4)) + return; + + r->reason=RIVAL(inbuf->buffer, inbuf->offset); +} + +/**************************************************************************** +decode a packet and fill a generic structure +****************************************************************************/ +void decode_generic_packet(struct BUFFER *inbuf, GENERIC_PACKET *q) +{ + read_generic_header(inbuf, &q->header); + + inbuf->offset+=16; + + switch (q->header.mess_type) { + case 0: + decode_start_assoc_request(inbuf, &q->sa_rq); + break; + case 1: + decode_start_assoc_reply(inbuf, &q->sa_rp); + break; + case 2: + decode_stop_assoc(inbuf, &q->so); + break; + case 3: + decode_replicate(inbuf, &q->rep); + break; + default: + DEBUG(0,("decode_generic_packet: unknown message type:%d\n", q->header.mess_type)); + break; + } +} + +/**************************************************************************** +encode a WINS_OWNER struct +****************************************************************************/ +static void encode_wins_owner(struct BUFFER *outbuf, WINS_OWNER *wins_owner) +{ + if (!grow_buffer(outbuf, 24)) + return; + + SIVAL(outbuf->buffer, outbuf->offset, wins_owner->address.s_addr); + outbuf->offset+=4; + RSIVAL(outbuf->buffer, outbuf->offset, (int)(wins_owner->max_version>>32)); + outbuf->offset+=4; + RSIVAL(outbuf->buffer, outbuf->offset, (int)(wins_owner->max_version&0xffffffff)); + outbuf->offset+=4; + RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->min_version>>32); + outbuf->offset+=4; + RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->min_version&0xffffffff); + outbuf->offset+=4; + RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->type); + outbuf->offset+=4; + +} + +/**************************************************************************** +encode a WINS_NAME struct +****************************************************************************/ +static void encode_wins_name(struct BUFFER *outbuf, WINS_NAME *wins_name) +{ + int i; + + if (!grow_buffer(outbuf, 48+(4*wins_name->num_ip))) + return; + + RSIVAL(outbuf->buffer, outbuf->offset, wins_name->name_len); + outbuf->offset+=4; + + memset(outbuf->buffer+outbuf->offset, ' ', 15); + + /* to prevent copying the leading \0 */ + memcpy(outbuf->buffer+outbuf->offset, wins_name->name, strlen(wins_name->name)); + outbuf->offset+=15; + + outbuf->buffer[outbuf->offset++]=(char)wins_name->type; + + RSIVAL(outbuf->buffer, outbuf->offset, wins_name->empty); + outbuf->offset+=4; + + RSIVAL(outbuf->buffer, outbuf->offset, wins_name->name_flag); + outbuf->offset+=4; + RSIVAL(outbuf->buffer, outbuf->offset, wins_name->group_flag); + outbuf->offset+=4; + RSIVAL(outbuf->buffer, outbuf->offset, wins_name->id>>32); + outbuf->offset+=4; + RSIVAL(outbuf->buffer, outbuf->offset, wins_name->id); + outbuf->offset+=4; + + if (wins_name->name_flag & 2) { + SIVAL(outbuf->buffer, outbuf->offset, wins_name->num_ip); + outbuf->offset+=4; + } + + SIVAL(outbuf->buffer, outbuf->offset, wins_name->owner.s_addr); + outbuf->offset+=4; + + if (wins_name->name_flag & 2) { + for (i=0;i<wins_name->num_ip;i++) { + SIVAL(outbuf->buffer, outbuf->offset, wins_name->others[i].s_addr); + outbuf->offset+=4; + } + } + + RSIVAL(outbuf->buffer, outbuf->offset, wins_name->foo); + outbuf->offset+=4; +} + +/**************************************************************************** +encode a update notification request +****************************************************************************/ +static void encode_update_notify_request(struct BUFFER *outbuf, UPDATE_NOTIFY_REQUEST *un_rq) +{ + int i; + + if (!grow_buffer(outbuf, 8)) + return; + + RSIVAL(outbuf->buffer, outbuf->offset, un_rq->partner_count); + outbuf->offset+=4; + + for (i=0; i<un_rq->partner_count; i++) + encode_wins_owner(outbuf, &un_rq->wins_owner[i]); + + SIVAL(outbuf->buffer, outbuf->offset, un_rq->initiating_wins_server.s_addr); + outbuf->offset+=4; + +} + +/**************************************************************************** +decode a send entries request +****************************************************************************/ +static void encode_send_entries_request(struct BUFFER *outbuf, SEND_ENTRIES_REQUEST *se_rq) +{ + encode_wins_owner(outbuf, &se_rq->wins_owner); +} + +/**************************************************************************** +decode a send entries reply +****************************************************************************/ +static void encode_send_entries_reply(struct BUFFER *outbuf, SEND_ENTRIES_REPLY *se_rp) +{ + int i; + + if (!grow_buffer(outbuf, 4)) + return; + + RSIVAL(outbuf->buffer, outbuf->offset, se_rp->max_names); + outbuf->offset+=4; + + for (i=0; i<se_rp->max_names; i++) + encode_wins_name(outbuf, &se_rp->wins_name[i]); + +} + +/**************************************************************************** +encode a add version number map table reply +****************************************************************************/ +static void encode_add_version_number_map_table_reply(struct BUFFER *outbuf, AVMT_REP *avmt_rep) +{ + int i; + + if (!grow_buffer(outbuf, 8)) + return; + + RSIVAL(outbuf->buffer, outbuf->offset, avmt_rep->partner_count); + outbuf->offset+=4; + + for (i=0; i<avmt_rep->partner_count; i++) + encode_wins_owner(outbuf, &avmt_rep->wins_owner[i]); + + SIVAL(outbuf->buffer, outbuf->offset, avmt_rep->initiating_wins_server.s_addr); + outbuf->offset+=4; + +} + +/**************************************************************************** +decode a replicate packet and fill a structure +****************************************************************************/ +static void encode_replicate(struct BUFFER *outbuf, REPLICATE *rep) +{ + if (!grow_buffer(outbuf, 4)) + return; + + RSIVAL(outbuf->buffer, outbuf->offset, rep->msg_type); + outbuf->offset+=4; + + switch (rep->msg_type) { + case 0: + break; + case 1: + /* add version number map table reply */ + encode_add_version_number_map_table_reply(outbuf, &rep->avmt_rep); + break; + case 2: + /* send entry request */ + encode_send_entries_request(outbuf, &rep->se_rq); + break; + case 3: + /* send entry request */ + encode_send_entries_reply(outbuf, &rep->se_rp); + break; + case 4: + /* update notification request */ + encode_update_notify_request(outbuf, &rep->un_rq); + break; + default: + DEBUG(0,("encode_replicate: unknown message type:%d\n", rep->msg_type)); + break; + } +} + +/**************************************************************************** +write the generic header. +****************************************************************************/ +static void write_generic_header(struct BUFFER *outbuf, generic_header *r) +{ + RSIVAL(outbuf->buffer, 0, r->data_size); + RSIVAL(outbuf->buffer, 4, r->opcode); + RSIVAL(outbuf->buffer, 8, r->assoc_ctx); + RSIVAL(outbuf->buffer,12, r->mess_type); +} + +/******************************************************************* +decode a start association request +********************************************************************/ +static void encode_start_assoc_request(struct BUFFER *outbuf, START_ASSOC_REQUEST *q) +{ + if (!grow_buffer(outbuf, 45)) + return; + + RSIVAL(outbuf->buffer, outbuf->offset, q->assoc_ctx); + RSSVAL(outbuf->buffer, outbuf->offset+4, q->min_ver); + RSSVAL(outbuf->buffer, outbuf->offset+6, q->maj_ver); + + outbuf->offset=45; +} + +/******************************************************************* +decode a start association reply +********************************************************************/ +static void encode_start_assoc_reply(struct BUFFER *outbuf, START_ASSOC_REPLY *r) +{ + if (!grow_buffer(outbuf, 45)) + return; + + RSIVAL(outbuf->buffer, outbuf->offset, r->assoc_ctx); + RSSVAL(outbuf->buffer, outbuf->offset+4, r->min_ver); + RSSVAL(outbuf->buffer, outbuf->offset+6, r->maj_ver); + + outbuf->offset=45; +} + +/******************************************************************* +decode a start association reply +********************************************************************/ +static void encode_stop_assoc(struct BUFFER *outbuf, STOP_ASSOC *r) +{ + if (!grow_buffer(outbuf, 44)) + return; + + RSIVAL(outbuf->buffer, outbuf->offset, r->reason); + + outbuf->offset=44; +} + +/**************************************************************************** +write the generic header size. +****************************************************************************/ +static void write_generic_header_size(generic_header *r, int size) +{ + /* the buffer size is the total size minus the size field */ + r->data_size=size-4; +} + +/**************************************************************************** +encode a packet and read a generic structure +****************************************************************************/ +void encode_generic_packet(struct BUFFER *outbuf, GENERIC_PACKET *q) +{ + if (!grow_buffer(outbuf, 16)) + return; + + outbuf->offset=16; + + switch (q->header.mess_type) { + case 0: + encode_start_assoc_request(outbuf, &q->sa_rq); + break; + case 1: + encode_start_assoc_reply(outbuf, &q->sa_rp); + break; + case 2: + encode_stop_assoc(outbuf, &q->so); + break; + case 3: + encode_replicate(outbuf, &q->rep); + break; + default: + DEBUG(0,("encode_generic_packet: unknown message type:%d\n", q->header.mess_type)); + break; + } + + write_generic_header_size(&q->header, outbuf->offset); + write_generic_header(outbuf, &q->header); +} + + +/**************************************************************************** +dump a WINS_OWNER structure +****************************************************************************/ +static void dump_wins_owner(WINS_OWNER *wins_owner) +{ + DEBUGADD(10,("\t\t\t\taddress : %s\n", inet_ntoa(wins_owner->address))); + DEBUGADD(10,("\t\t\t\tmax version: %d\n", (int)wins_owner->max_version)); + DEBUGADD(10,("\t\t\t\tmin version: %d\n", (int)wins_owner->min_version)); + DEBUGADD(10,("\t\t\t\ttype : %d\n", wins_owner->type)); +} + +/**************************************************************************** +dump a WINS_NAME structure +****************************************************************************/ +static void dump_wins_name(WINS_NAME *wins_name) +{ + fstring name; + int i; + + strncpy(name, wins_name->name, 15); + + DEBUGADD(10,("name: %d, %s<%02x> %x,%x, %d %s %d ", wins_name->name_len, name, wins_name->type, + wins_name->name_flag, wins_name->group_flag, (int)wins_name->id, + inet_ntoa(wins_name->owner), wins_name->num_ip)); + + if (wins_name->num_ip!=1) + for (i=0; i<wins_name->num_ip; i++) + DEBUGADD(10,("%s ", inet_ntoa(wins_name->others[i]))); + + DEBUGADD(10,("\n")); +} + +/**************************************************************************** +dump a replicate structure +****************************************************************************/ +static void dump_replicate(REPLICATE *rep) +{ + int i; + + DEBUGADD(5,("\t\tmsg_type: %d ", rep->msg_type)); + + switch (rep->msg_type) { + case 0: + DEBUGADD(5,("(Add Version Map Table Request)\n")); + break; + case 1: + DEBUGADD(5,("(Add Version Map Table Reply)\n")); + DEBUGADD(5,("\t\t\tpartner_count : %d\n", rep->avmt_rep.partner_count)); + for (i=0; i<rep->avmt_rep.partner_count; i++) + dump_wins_owner(&rep->avmt_rep.wins_owner[i]); + DEBUGADD(5,("\t\t\tinitiating_wins_server: %s\n", inet_ntoa(rep->avmt_rep.initiating_wins_server))); + break; + case 2: + DEBUGADD(5,("(Send Entries Request)\n")); + dump_wins_owner(&rep->se_rq.wins_owner); + break; + case 3: + DEBUGADD(5,("(Send Entries Reply)\n")); + DEBUGADD(5,("\t\t\tmax_names : %d\n", rep->se_rp.max_names)); + for (i=0; i<rep->se_rp.max_names; i++) + dump_wins_name(&rep->se_rp.wins_name[i]); + break; + case 4: + DEBUGADD(5,("(Update Notify Request)\n")); + DEBUGADD(5,("\t\t\tpartner_count : %d\n", rep->un_rq.partner_count)); + for (i=0; i<rep->un_rq.partner_count; i++) + dump_wins_owner(&rep->un_rq.wins_owner[i]); + DEBUGADD(5,("\t\t\tinitiating_wins_server: %s\n", inet_ntoa(rep->un_rq.initiating_wins_server))); + break; + default: + DEBUG(5,("\n")); + break; + } +} + +/**************************************************************************** +dump a generic structure +****************************************************************************/ +void dump_generic_packet(GENERIC_PACKET *q) +{ + DEBUG(5,("dump_generic_packet:\n")); + DEBUGADD(5,("\tdata_size: %08x\n", q->header.data_size)); + DEBUGADD(5,("\topcode : %08x\n", q->header.opcode)); + DEBUGADD(5,("\tassoc_ctx: %08x\n", q->header.assoc_ctx)); + DEBUGADD(5,("\tmess_type: %08x ", q->header.mess_type)); + + switch (q->header.mess_type) { + case 0: + DEBUGADD(5,("(Start Association Request)\n")); + DEBUGADD(5,("\t\tassoc_ctx: %08x\n", q->sa_rq.assoc_ctx)); + DEBUGADD(5,("\t\tmin_ver : %04x\n", q->sa_rq.min_ver)); + DEBUGADD(5,("\t\tmaj_ver : %04x\n", q->sa_rq.maj_ver)); + break; + case 1: + DEBUGADD(5,("(Start Association Reply)\n")); + DEBUGADD(5,("\t\tassoc_ctx: %08x\n", q->sa_rp.assoc_ctx)); + DEBUGADD(5,("\t\tmin_ver : %04x\n", q->sa_rp.min_ver)); + DEBUGADD(5,("\t\tmaj_ver : %04x\n", q->sa_rp.maj_ver)); + break; + case 2: + DEBUGADD(5,("(Stop Association)\n")); + DEBUGADD(5,("\t\treason: %08x\n", q->so.reason)); + break; + case 3: + DEBUGADD(5,("(Replication Message)\n")); + dump_replicate(&q->rep); + break; + default: + DEBUG(5,("\n")); + break; + } + +} + +/**************************************************************************** +generate a stop packet +****************************************************************************/ +void stop_packet(GENERIC_PACKET *q, GENERIC_PACKET *r, int reason) +{ + r->header.opcode=OPCODE_NON_NBT; + r->header.assoc_ctx=get_server_assoc(q->header.assoc_ctx); + r->header.mess_type=MESSAGE_TYPE_STOP_ASSOC; + r->so.reason=reason; + +} + + diff --git a/source4/wrepld/partners.c b/source4/wrepld/partners.c new file mode 100644 index 0000000000..2387f5b45f --- /dev/null +++ b/source4/wrepld/partners.c @@ -0,0 +1,200 @@ +/* + Unix SMB/CIFS implementation. + process incoming packets - main loop + Copyright (C) Jean François Micouleau 1998-2002. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "wins_repl.h" + +/* we can exchange info with 64 partners at any given time */ +WINS_PARTNER current_partners[64]; +int total_current_partners; + +/******************************************************************* +verify if we know this partner +********************************************************************/ +BOOL check_partner(int assoc) +{ + int i; + + DEBUG(5,("check_partner: total_current_partners: %d\n", total_current_partners)); + + for (i=0; i<total_current_partners; i++) + if (current_partners[i].client_assoc==assoc) + return True; + + return False; +} + +/******************************************************************* +add a new entry to the list +********************************************************************/ +BOOL add_partner(int client_assoc, int server_assoc, BOOL pull, BOOL push) +{ + DEBUG(5,("add_partner: total_current_partners: %d\n", total_current_partners)); + + if (total_current_partners==64) + return False; + + current_partners[total_current_partners].client_assoc=client_assoc; + current_partners[total_current_partners].server_assoc=server_assoc; + current_partners[total_current_partners].pull_partner=pull; + current_partners[total_current_partners].push_partner=push; + + total_current_partners++; + + return True; +} + +/******************************************************************* +remove an entry to the list +********************************************************************/ +BOOL remove_partner(int client_assoc) +{ + int i,j; + + DEBUG(5,("remove_partner: total_current_partners: %d\n", total_current_partners)); + + for (i=0; current_partners[i].client_assoc!=client_assoc && i<total_current_partners; i++) + ; + + if (i==total_current_partners) + return False; + + for (j=i+1; j<total_current_partners; j++) { + current_partners[j-1].client_assoc=current_partners[j].client_assoc; + current_partners[j-1].server_assoc=current_partners[j].server_assoc; + current_partners[j-1].pull_partner=current_partners[j].pull_partner; + current_partners[j-1].push_partner=current_partners[j].push_partner; + current_partners[j-1].partner_server.s_addr=current_partners[j].partner_server.s_addr; + current_partners[j-1].other_server.s_addr=current_partners[j].other_server.s_addr; + } + + total_current_partners--; + + return True; +} + +/******************************************************************* +link the client and server context +********************************************************************/ +BOOL update_server_partner(int client_assoc, int server_assoc) +{ + int i; + + DEBUG(5,("update_server_partner: total_current_partners: %d\n", total_current_partners)); + + for (i=0; i<total_current_partners; i++) + if (current_partners[i].client_assoc==client_assoc) { + current_partners[i].server_assoc=server_assoc; + return True; + } + + return False; +} + +/******************************************************************* +verify if it's a pull partner +********************************************************************/ +BOOL check_pull_partner(int assoc) +{ + int i; + + DEBUG(5,("check_pull_partner: total_current_partners: %d\n", total_current_partners)); + + for (i=0; i<total_current_partners; i++) + if (current_partners[i].client_assoc==assoc && + current_partners[i].pull_partner==True) + return True; + + return False; +} + +/******************************************************************* +verify if it's a push partner +********************************************************************/ +BOOL check_push_partner(int assoc) +{ + int i; + + DEBUG(5,("check_push_partner: total_current_partners: %d\n", total_current_partners)); + + for (i=0; i<total_current_partners; i++) + if (current_partners[i].client_assoc==assoc && + current_partners[i].push_partner==True) + return True; + + return False; +} + +/******************************************************************* +return the server ctx linked to the client ctx +********************************************************************/ +int get_server_assoc(int assoc) +{ + int i; + + DEBUG(5,("get_server_assoc: total_current_partners: %d\n", total_current_partners)); + + for (i=0; i<total_current_partners; i++) + if (current_partners[i].client_assoc==assoc) + return current_partners[i].server_assoc; + + return 0; +} + + +/******************************************************************* +link the client and server context +********************************************************************/ +BOOL write_server_assoc_table(int client_assoc, struct in_addr partner, struct in_addr server) +{ + int i; + + DEBUG(5,("write_server_assoc_table: total_current_partners: %d\n", total_current_partners)); + + for (i=0; i<total_current_partners; i++) + if (current_partners[i].client_assoc==client_assoc) { + current_partners[i].partner_server=partner; + current_partners[i].other_server=server; + return True; + } + + return False; +} + +/******************************************************************* +link the client and server context +********************************************************************/ +BOOL get_server_assoc_table(int client_assoc, struct in_addr *partner, struct in_addr *server) +{ + int i; + + DEBUG(5,("get_server_assoc_table: total_current_partners: %d\n", total_current_partners)); + + for (i=0; i<total_current_partners; i++) + if (current_partners[i].client_assoc==client_assoc) { + partner->s_addr=current_partners[i].partner_server.s_addr; + server->s_addr=current_partners[i].other_server.s_addr; + return True; + } + + return False; +} + + diff --git a/source4/wrepld/process.c b/source4/wrepld/process.c new file mode 100644 index 0000000000..1f96dc996c --- /dev/null +++ b/source4/wrepld/process.c @@ -0,0 +1,983 @@ +/* + Unix SMB/CIFS implementation. + process incoming packets - main loop + Copyright (C) Jean François Micouleau 1998-2002. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "wins_repl.h" + +extern fd_set *listen_set; +extern int listen_number; +extern int *sock_array; + +WINS_OWNER global_wins_table[64][64]; +int partner_count; + +TALLOC_CTX *mem_ctx; + +#define WINS_LIST "wins.tdb" +#define INFO_VERSION "INFO/version" +#define INFO_COUNT "INFO/num_entries" +#define INFO_ID_HIGH "INFO/id_high" +#define INFO_ID_LOW "INFO/id_low" +#define ENTRY_PREFIX "ENTRY/" + + +/******************************************************************* +fill the header of a reply. +********************************************************************/ +static void fill_header(GENERIC_PACKET *g, int opcode, int ctx, int mess) +{ + if (g==NULL) + return; + + g->header.opcode=opcode; + g->header.assoc_ctx=ctx; + g->header.mess_type=mess; +} + +/******************************************************************* +dump the global table, that's a debug code. +********************************************************************/ +static void dump_global_table(void) +{ + int i,j; + + for (i=0;i<partner_count;i++) { + DEBUG(10,("\n%d ", i)); + for (j=0; global_wins_table[i][j].address.s_addr!=0; j++) + DEBUG(10,("%s:%d \t", inet_ntoa(global_wins_table[i][j].address), + (int)global_wins_table[i][j].max_version)); + } + DEBUG(10,("\n")); +} + +/******************************************************************* +start association +********************************************************************/ +static void start_assoc_process(GENERIC_PACKET *q, GENERIC_PACKET *r) +{ + /* + * add this request to our current wins partners list + * this list is used to know with who we are in contact + * + */ + r->sa_rp.assoc_ctx=time(NULL); + fill_header(r, OPCODE_NON_NBT, q->sa_rq.assoc_ctx, MESSAGE_TYPE_START_ASSOC_REPLY); + + /* reply we are a NT4 server */ + + /* w2K is min=2, maj=5 */ + + r->sa_rp.min_ver=1; + r->sa_rp.maj_ver=1; + + add_partner(r->sa_rp.assoc_ctx, q->sa_rq.assoc_ctx, False, False); +} + +/******************************************************************* +start association reply +********************************************************************/ +static void start_assoc_reply(GENERIC_PACKET *q, GENERIC_PACKET *r) +{ + int i; + + /* check if we have already registered this client */ + if (!check_partner(q->header.assoc_ctx)) { + DEBUG(0,("start_assoc_reply: unknown client\n")); + stop_packet(q, r, STOP_REASON_USER_REASON); + return; + } + + if (!update_server_partner(q->header.assoc_ctx, q->sa_rp.assoc_ctx)) { + DEBUG(0,("start_assoc_reply: can't update server ctx\n")); + stop_packet(q, r, STOP_REASON_USER_REASON); + return; + } + + /* if pull, request map table */ + if (check_pull_partner(q->header.assoc_ctx)) { + fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE); + + r->rep.msg_type=MESSAGE_REP_ADD_VERSION_REQUEST; + DEBUG(5,("start_assoc_reply: requesting map table\n")); + + return; + } + + /* if push, send our table */ + if (check_push_partner(q->header.assoc_ctx)) { + fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE); + r->rep.msg_type=MESSAGE_REP_UPDATE_NOTIFY_REQUEST; + r->rep.un_rq.partner_count=partner_count; + + r->rep.un_rq.wins_owner=(WINS_OWNER *)talloc(mem_ctx, partner_count*sizeof(WINS_OWNER)); + if (r->rep.un_rq.wins_owner==NULL) { + DEBUG(0,("start_assoc_reply: can't alloc memory\n")); + stop_packet(q, r, STOP_REASON_USER_REASON); + return; + } + + for (i=0; i<partner_count; i++) + r->rep.un_rq.wins_owner[i]=global_wins_table[0][i]; + + DEBUG(5,("start_assoc_reply: sending update table\n")); + return; + } + + /* neither push/pull, stop */ + /* we should not come here */ + DEBUG(0,("we have a partner which is neither push nor pull !\n")); + stop_packet(q, r, STOP_REASON_USER_REASON); +} + +/**************************************************************************** +initialise and fill the in-memory partner table. +****************************************************************************/ +int init_wins_partner_table(void) +{ + int i=1,j=0,k; + char **partner = str_list_make(lp_wins_partners(), NULL); + + if (partner==NULL) { + DEBUG(0,("wrepld: no partner list in smb.conf, exiting\n")); + exit_server("normal exit"); + return(0); + } + + DEBUG(4, ("init_wins_partner_table: partners: %s\n", lp_wins_partners())); + + global_wins_table[0][0].address=*iface_n_ip(0); + global_wins_table[0][0].max_version=0; + global_wins_table[0][0].min_version=0; + global_wins_table[0][0].type=0; + + while (partner[j]!=NULL) { + DEBUG(3,("init_wins_partner_table, adding partner: %s\n", partner[j])); + + global_wins_table[0][i].address=*interpret_addr2(partner[j]); + global_wins_table[0][i].max_version=0; + global_wins_table[0][i].min_version=0; + global_wins_table[0][i].type=0; + global_wins_table[0][i].last_pull=0; + global_wins_table[0][i].last_push=0; + + i++; + j++; + } + + for (k=1; k<i;k++) + for (j=0; j<i; j++) + global_wins_table[k][j]=global_wins_table[0][j]; + + str_list_free (&partner); + + return i; +} + +/**************************************************************************** +read the last ID from the wins tdb file. +****************************************************************************/ +static void get_our_last_id(WINS_OWNER *wins_owner) +{ + TDB_CONTEXT *tdb; + + tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600); + if (!tdb) { + DEBUG(2,("get_our_last_id: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) )); + return; + } + + wins_owner->max_version=((SMB_BIG_UINT)tdb_fetch_int32(tdb, INFO_ID_HIGH))<<32 | + (SMB_BIG_UINT)tdb_fetch_int32(tdb, INFO_ID_LOW); + + tdb_close(tdb); +} + +/**************************************************************************** +send the list of wins server we know. +****************************************************************************/ +static void send_version_number_map_table(GENERIC_PACKET *q, GENERIC_PACKET *r) +{ + int i; + int s_ctx=get_server_assoc(q->header.assoc_ctx); + + if (s_ctx==0) { + DEBUG(5, ("send_version_number_map_table: request for a partner not in our table\n")); + stop_packet(q, r, STOP_REASON_USER_REASON); + return; + } + + /* + * return an array of wins servers, we are partner with. + * each entry contains the IP address and the version info + * version: ID of the last entry we've got + */ + + /* the first wins server must be self */ + + /* + * get our last ID from the wins database + * it can have been updated since last read + * as nmbd got registration/release. + */ + get_our_last_id(&global_wins_table[0][0]); + + r->rep.avmt_rep.wins_owner=(WINS_OWNER *)talloc(mem_ctx, partner_count*sizeof(WINS_OWNER)); + if (r->rep.avmt_rep.wins_owner==NULL) { + stop_packet(q, r, STOP_REASON_USER_REASON); + return; + } + + DEBUG(5,("send_version_number_map_table: partner_count: %d\n", partner_count)); + + for (i=0; i<partner_count; i++) { + DEBUG(5,("send_version_number_map_table, partner: %d -> %s, \n", i, inet_ntoa(global_wins_table[0][i].address))); + r->rep.avmt_rep.wins_owner[i]=global_wins_table[0][i]; + } + + r->rep.msg_type=1; + r->rep.avmt_rep.partner_count=partner_count; + r->rep.avmt_rep.initiating_wins_server.s_addr=0; /* blatant lie, NT4/w2K do the same ! */ + fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE); +} + +/**************************************************************************** +for a given partner, ask it to send entries we don't have. +****************************************************************************/ +static BOOL check_partners_and_send_entries(GENERIC_PACKET *q, GENERIC_PACKET *r, int partner) +{ + int server; + int other; + SMB_BIG_UINT temp; + SMB_BIG_UINT current; + + + /* + * we check if our partner has more records than us. + * we need to check more than our direct partners as + * we can have this case: + * us: A, partners: B,C, indirect partner: D + * A<->B, A<->C, B<->D, C<->D + * + * So if we're talking to B, we need to check if between + * B and C, which one have more records about D. + * and also check if we don't already have the records. + */ + + + /* check all servers even indirect */ + for (server=1; global_wins_table[0][server].address.s_addr!=0; server++) { + current = global_wins_table[partner][server].max_version; + + temp=0; + + for (other=1; other<partner_count; other++) { + /* skip the partner itself */ + if (other==partner) + continue; + + if (global_wins_table[other][server].max_version > temp) + temp=global_wins_table[other][server].max_version; + } + + if (current >= temp && current > global_wins_table[0][server].max_version) { + /* + * it has more records than every body else and more than us, + * ask it the difference between what we have and what it has + */ + fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE); + + r->rep.msg_type=MESSAGE_REP_SEND_ENTRIES_REQUEST; + r->rep.se_rq.wins_owner.address=global_wins_table[partner][server].address; + + r->rep.se_rq.wins_owner.max_version=global_wins_table[partner][server].max_version; + r->rep.se_rq.wins_owner.min_version=global_wins_table[0][server].max_version; + r->rep.se_rq.wins_owner.type=0; + + write_server_assoc_table(q->header.assoc_ctx, global_wins_table[0][partner].address, global_wins_table[partner][server].address); + + /* + * and we update our version for this server + * as we can't use the IDs returned in the send_entries function + * the max ID can be larger than the largest ID returned + */ + + global_wins_table[0][server].max_version=global_wins_table[partner][server].max_version; + + return True; + } + } + return False; +} + +/**************************************************************************** +receive the list of wins server we know. +****************************************************************************/ +static void receive_version_number_map_table(GENERIC_PACKET *q, GENERIC_PACKET *r) +{ + fstring peer; + struct in_addr addr; + int i,j,k,l; + int s_ctx=get_server_assoc(q->header.assoc_ctx); + + if (s_ctx==0) { + DEBUG(5, ("receive_version_number_map_table: request for a partner not in our table\n")); + stop_packet(q, r, STOP_REASON_USER_REASON); + return; + } + + fstrcpy(peer,get_socket_addr(q->fd)); + addr=*interpret_addr2(peer); + + get_our_last_id(&global_wins_table[0][0]); + + DEBUG(5,("receive_version_number_map_table: received a map of %d server from: %s\n", + q->rep.avmt_rep.partner_count ,inet_ntoa(q->rep.avmt_rep.initiating_wins_server))); + DEBUG(5,("real peer is: %s\n", peer)); + + for (i=0; global_wins_table[0][i].address.s_addr!=addr.s_addr && i<partner_count;i++) + ; + + if (i==partner_count) { + DEBUG(5,("receive_version_number_map_table: unknown partner: %s\n", peer)); + stop_packet(q, r, STOP_REASON_USER_REASON); + return; + } + + for (j=0; j<q->rep.avmt_rep.partner_count;j++) { + /* + * search if we already have this entry or if it's a new one + * it can be a new one in case of propagation + */ + for (k=0; global_wins_table[0][k].address.s_addr!=0 && + global_wins_table[0][k].address.s_addr!=q->rep.avmt_rep.wins_owner[j].address.s_addr; k++); + + global_wins_table[i][k].address.s_addr=q->rep.avmt_rep.wins_owner[j].address.s_addr; + global_wins_table[i][k].max_version=q->rep.avmt_rep.wins_owner[j].max_version; + global_wins_table[i][k].min_version=q->rep.avmt_rep.wins_owner[j].min_version; + global_wins_table[i][k].type=q->rep.avmt_rep.wins_owner[j].type; + + /* + * in case it's a new one, rewrite the address for all the partner + * to reserve the slot. + */ + + for(l=0; l<partner_count; l++) + global_wins_table[l][k].address.s_addr=q->rep.avmt_rep.wins_owner[j].address.s_addr; + } + + dump_global_table(); + + /* + * if this server have newer records than what we have + * for several wins servers, we need to ask it. + * Alas a send entry request is only on one server. + * So in the send entry reply, we'll ask for the next server if required. + */ + + if (check_partners_and_send_entries(q, r, i)) + return; + + /* it doesn't have more entries than us */ + stop_packet(q, r, STOP_REASON_USER_REASON); +} + +/**************************************************************************** +add an entry to the wins list we'll send. +****************************************************************************/ +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) +{ + WINS_NAME *temp_list; + int i; + int current=*max_names; + + temp_list=talloc_realloc(mem_ctx, *wins_name, (current+1)*sizeof(WINS_NAME)); + if (temp_list==NULL) + return False; + + temp_list[current].name_len=0x11; + + safe_strcpy(temp_list[current].name, name, 15); + + temp_list[current].type=type; + temp_list[current].empty=0; + + temp_list[current].name_flag=wins_flags; + + if ( (wins_flags&0x03) == 1 || (wins_flags&0x03)==2) + temp_list[current].group_flag=0x01000000; + else + temp_list[current].group_flag=0x00000000; + + temp_list[current].id=id; + + temp_list[current].owner.s_addr=ip_list[0].s_addr; + + if (temp_list[current].name_flag & 2) { + temp_list[current].num_ip=num_ips; + temp_list[current].others=(struct in_addr *)talloc(mem_ctx, sizeof(struct in_addr)*num_ips); + if (temp_list[current].others==NULL) + return False; + + for (i=0; i<num_ips; i++) + temp_list[current].others[i].s_addr=ip_list[i].s_addr; + + } else + temp_list[current].num_ip=1; + + temp_list[current].foo=0xffffffff; + + *wins_name=temp_list; + + return True; +} + +/**************************************************************************** +send the list of name we have. +****************************************************************************/ +static void send_entry_request(GENERIC_PACKET *q, GENERIC_PACKET *r) +{ + int max_names=0; + int i; + time_t time_now = time(NULL); + WINS_OWNER *wins_owner; + TDB_CONTEXT *tdb; + TDB_DATA kbuf, dbuf, newkey; + int s_ctx=get_server_assoc(q->header.assoc_ctx); + int num_interfaces = iface_count(); + + if (s_ctx==0) { + DEBUG(1, ("send_entry_request: request for a partner not in our table\n")); + stop_packet(q, r, STOP_REASON_USER_REASON); + return; + } + + + wins_owner=&q->rep.se_rq.wins_owner; + r->rep.se_rp.wins_name=NULL; + + DEBUG(3,("send_entry_request: we have been asked to send the list of wins records\n")); + DEBUGADD(3,("owned by: %s and between min: %d and max: %d\n", inet_ntoa(wins_owner->address), + (int)wins_owner->min_version, (int)wins_owner->max_version)); + + /* + * if we are asked to send records owned by us + * we overwrite the wins ip with 0.0.0.0 + * to make it easy in case of multihomed + */ + + for (i=0; i<num_interfaces; i++) + if (ip_equal(wins_owner->address, *iface_n_ip(i))) { + wins_owner->address=*interpret_addr2("0.0.0.0"); + break; + } + + + tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600); + if (!tdb) { + DEBUG(2,("send_entry_request: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) )); + return; + } + + for (kbuf = tdb_firstkey(tdb); + kbuf.dptr; + newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) { + fstring name_type; + pstring name, ip_str; + char *p; + int type = 0; + int nb_flags; + int ttl; + unsigned int num_ips; + int low, high; + SMB_BIG_UINT version; + struct in_addr wins_ip; + struct in_addr *ip_list; + int wins_flags; + int len; + + if (strncmp(kbuf.dptr, ENTRY_PREFIX, strlen(ENTRY_PREFIX)) != 0) + continue; + + + dbuf = tdb_fetch(tdb, kbuf); + if (!dbuf.dptr) + continue; + + fstrcpy(name_type, kbuf.dptr+strlen(ENTRY_PREFIX)); + pstrcpy(name, name_type); + + if((p = strchr(name,'#')) != NULL) { + *p = 0; + sscanf(p+1,"%x",&type); + } + + len = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddfddd", + &nb_flags, + &high, + &low, + ip_str, + &ttl, + &num_ips, + &wins_flags); + + wins_ip=*interpret_addr2(ip_str); + + /* Allocate the space for the ip_list. */ + if((ip_list = (struct in_addr *)talloc(mem_ctx, num_ips * sizeof(struct in_addr))) == NULL) { + SAFE_FREE(dbuf.dptr); + DEBUG(0,("initialise_wins: talloc fail !\n")); + return; + } + + for (i = 0; i < num_ips; i++) { + len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f", ip_str); + ip_list[i] = *interpret_addr2(ip_str); + } + + SAFE_FREE(dbuf.dptr); + + /* add all entries that have 60 seconds or more to live */ + if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) { + if(ttl != PERMANENT_TTL) + ttl -= time_now; + + DEBUG( 4, ("send_entry_request: add name: %s#%02x ttl = %d first IP %s flags = %2x\n", + name, type, ttl, inet_ntoa(ip_list[0]), nb_flags)); + + /* add the record to the list to send */ + version=((SMB_BIG_UINT)high)<<32 | low; + + if (wins_owner->min_version<=version && wins_owner->max_version>=version && + wins_owner->address.s_addr==wins_ip.s_addr) { + if(!add_record_to_winsname(&r->rep.se_rp.wins_name, &max_names, name, type, wins_flags, version, ip_list, num_ips)) + return; + max_names++; + } + + } else { + DEBUG(4, ("send_entry_request: not adding name (ttl problem) %s#%02x ttl = %d first IP %s flags = %2x\n", + name, type, ttl, inet_ntoa(ip_list[0]), nb_flags)); + } + } + + tdb_close(tdb); + + DEBUG(4,("send_entry_request, sending %d records\n", max_names)); + fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE); + r->rep.msg_type=MESSAGE_REP_SEND_ENTRIES_REPLY; /* reply */ + r->rep.se_rp.max_names=max_names; +} + + +/**************************************************************************** +. +****************************************************************************/ +static void update_notify_request(GENERIC_PACKET *q, GENERIC_PACKET *r) +{ + int i,j,k,l; + UPDATE_NOTIFY_REQUEST *u; + int s_ctx=get_server_assoc(q->header.assoc_ctx); + + if (s_ctx==0) { + DEBUG(4, ("update_notify_request: request for a partner not in our table\n")); + stop_packet(q, r, STOP_REASON_USER_REASON); + return; + } + + u=&q->rep.un_rq; + + /* check if we already have the range of records */ + + DEBUG(5,("update_notify_request: wins server: %s offered this list of %d records:\n", + inet_ntoa(u->initiating_wins_server), u->partner_count)); + + get_our_last_id(&global_wins_table[0][0]); + + for (i=0; i<partner_count; i++) { + if (global_wins_table[0][i].address.s_addr==u->initiating_wins_server.s_addr) { + DEBUG(5,("update_notify_request: found initiator at index %d\n", i)); + break; + } + } + + /* + * some explanation is required, before someone say it's crap. + * + * let's take an example, we have 2 wins partners, we already now + * that our max id is 10, partner 1 ID is 20 and partner 2 ID is 30 + * the array looks like: + * + * 0 1 2 + * 0 10 20 30 + * 1 + * 2 + * + * we receive an update from partner 2 saying he has: 1:15, 2:40, 3:50 + * we must enlarge the array to add partner 3, it will look like: + * + * 0 1 2 3 + * 0 10 20 30 + * 1 + * 2 15 40 50 + * + * now we know, we should pull from partner 2, the records 30->40 of 2 and 0->50 of 3. + * once the pull will be over, our table will look like: + * + * 0 1 2 3 + * 0 10 20 40 50 + * 1 + * 2 15 40 50 + * + * + */ + + for (j=0; j<u->partner_count;j++) { + /* + * search if we already have this entry or if it's a new one + * it can be a new one in case of propagation + */ + + for (k=0; global_wins_table[0][k].address.s_addr!=0 && + global_wins_table[0][k].address.s_addr!=u->wins_owner[j].address.s_addr; k++); + + global_wins_table[i][k].address.s_addr=u->wins_owner[j].address.s_addr; + global_wins_table[i][k].max_version=u->wins_owner[j].max_version; + global_wins_table[i][k].min_version=u->wins_owner[j].min_version; + global_wins_table[i][k].type=u->wins_owner[j].type; + + /* + * in case it's a new one, rewrite the address for all the partner + * to reserve the slot. + */ + + for(l=0; l<partner_count; l++) + global_wins_table[l][k].address.s_addr=u->wins_owner[j].address.s_addr; + } + + dump_global_table(); + + stop_packet(q, r, STOP_REASON_USER_REASON); +} + +/**************************************************************************** +. +****************************************************************************/ +static void send_entry_reply(GENERIC_PACKET *q, GENERIC_PACKET *r) +{ + int i,j,k; + struct in_addr partner, server; + pid_t pid; + int s_ctx=get_server_assoc(q->header.assoc_ctx); + WINS_RECORD record; + + if (s_ctx==0) { + DEBUG(1, ("send_entry_reply: request for a partner not in our table\n")); + stop_packet(q, r, STOP_REASON_USER_REASON); + return; + } + + DEBUG(5,("send_entry_reply:got %d new records\n", q->rep.se_rp.max_names)); + + /* we got records from a wins partner but that can be from another wins server */ + /* hopefully we track that */ + + /* and the only doc available from MS is wrong ! */ + + get_server_assoc_table(q->header.assoc_ctx, &partner, &server); + + for (j=0; global_wins_table[0][j].address.s_addr!=0; j++) { + if (global_wins_table[0][j].address.s_addr==server.s_addr) { + DEBUG(5,("send_entry_reply: found server at index %d\n", j)); + break; + } + } + + pid = pidfile_pid("nmbd"); + if (pid == 0) { + DEBUG(0,("send_entry_reply: Can't find pid for nmbd\n")); + return; + } + + for (k=0; k<q->rep.se_rp.max_names; k++) { + 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, + (int)q->rep.se_rp.wins_name[k].id)); + + safe_strcpy(record.name, q->rep.se_rp.wins_name[k].name, 16); + record.type=q->rep.se_rp.wins_name[k].type; + record.id=q->rep.se_rp.wins_name[k].id; + record.wins_flags=q->rep.se_rp.wins_name[k].name_flag&0x00ff; + record.num_ips=q->rep.se_rp.wins_name[k].num_ip; + + record.wins_ip.s_addr=server.s_addr; + + if (record.num_ips==1) + record.ip[0]=q->rep.se_rp.wins_name[k].owner; + else + for (i=0; i<record.num_ips; i++) + record.ip[i]=q->rep.se_rp.wins_name[k].others[i]; + + record.nb_flags=0; + + if (record.wins_flags&WINS_NGROUP || record.wins_flags&WINS_SGROUP) + record.nb_flags|=NB_GROUP; + + if (record.wins_flags&WINS_ACTIVE) + record.nb_flags|=NB_ACTIVE; + + record.nb_flags|=record.wins_flags&WINS_HNODE; + + message_send_pid(pid, MSG_WINS_NEW_ENTRY, &record, sizeof(record), False); + + } + + dump_global_table(); + + /* + * we got some entries, + * ask the partner to send us the map table again + * to get the other servers entries. + * + * we're getting the map table 1 time more than really + * required. We could remove that call, but that + * would complexify the code. I prefer this trade-of. + */ + fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE); + + r->rep.msg_type=MESSAGE_REP_ADD_VERSION_REQUEST; +} + +/**************************************************************************** +decode the replication message and reply. +****************************************************************************/ +static void replicate(GENERIC_PACKET *q, GENERIC_PACKET *r) +{ + switch (q->rep.msg_type) { + case 0: + /* add version number map table request */ + send_version_number_map_table(q, r); + break; + case 1: + receive_version_number_map_table(q, r); + break; + case 2: + /* send entry request */ + send_entry_request(q, r); + break; + case 3: + /* send entry reply */ + send_entry_reply(q, r); + break; + case 4: + /* update notification request */ + update_notify_request(q, r); + break; + } +} + +/**************************************************************************** +do a switch on the message type, and return the response size +****************************************************************************/ +static BOOL switch_message(GENERIC_PACKET *q, GENERIC_PACKET *r) +{ + switch (q->header.mess_type) { + case 0: + /* Start association type */ + start_assoc_process(q, r); + return True; + break; + case 1: + /* start association reply */ + start_assoc_reply(q, r); + return True; + break; + case 2: + /* stop association message */ + return False; + break; + case 3: + /* replication message */ + replicate(q, r); + return True; + break; + } + + return False; +} + + +/**************************************************************************** + construct a reply to the incoming packet +****************************************************************************/ +void construct_reply(struct wins_packet_struct *p) +{ + GENERIC_PACKET r; + struct BUFFER buffer; + + buffer.buffer=NULL; + buffer.offset=0; + buffer.length=0; + + DEBUG(5,("dump: received packet\n")); + dump_generic_packet(p->packet); + + /* Verify if the request we got is from a listed partner */ + if (!check_partner(p->packet->header.assoc_ctx)) { + fstring peer; + struct in_addr addr; + int i; + fstrcpy(peer,get_socket_addr(p->fd)); + addr=*interpret_addr2(peer); + + for (i=1; i<partner_count; i++) + if (ip_equal(addr, global_wins_table[0][i].address)) + break; + + if (i==partner_count) { + DEBUG(1,("construct_reply: got a request from a non peer machine: %s\n", peer)); + stop_packet(p->packet, &r, STOP_REASON_AUTH_FAILED); + p->stop_packet=True; + encode_generic_packet(&buffer, &r); + if (!send_smb(p->fd, buffer.buffer)) + exit_server("process_smb: send_smb failed."); + return; + } + } + + if (switch_message(p->packet, &r)) { + encode_generic_packet(&buffer, &r); + DEBUG(5,("dump: sending packet\n")); + dump_generic_packet(&r); + + if(buffer.offset > 0) { + if (!send_smb(p->fd, buffer.buffer)) + exit_server("process_smb: send_smb failed."); + } + } + + /* if we got a stop assoc or if we send a stop assoc, close the fd after */ + if (p->packet->header.mess_type==MESSAGE_TYPE_STOP_ASSOC || + r.header.mess_type==MESSAGE_TYPE_STOP_ASSOC) { + remove_partner(p->packet->header.assoc_ctx); + p->stop_packet=True; + } +} + +/**************************************************************************** + contact periodically our wins partner to do a pull replication +****************************************************************************/ +void run_pull_replication(time_t t) +{ + /* we pull every 30 minutes to query about new records*/ + int i, s; + struct BUFFER buffer; + GENERIC_PACKET p; + + buffer.buffer=NULL; + buffer.offset=0; + buffer.length=0; + + for (i=1; i<partner_count; i++) { + if (global_wins_table[0][i].last_pull < t) { + global_wins_table[0][i].last_pull=t+30*60; /* next in 30 minutes */ + + /* contact the wins server */ + p.header.mess_type=MESSAGE_TYPE_START_ASSOC_REQUEST; + p.header.opcode=OPCODE_NON_NBT; + p.header.assoc_ctx=0; + p.sa_rq.assoc_ctx=(int)t; + p.sa_rq.min_ver=1; + p.sa_rq.maj_ver=1; + + DEBUG(3,("run_pull_replication: contacting wins server %s.\n", inet_ntoa(global_wins_table[0][i].address))); + encode_generic_packet(&buffer, &p); + dump_generic_packet(&p); + + /* send the packet to the server and add the descriptor to receive answers */ + s=open_socket_out(SOCK_STREAM, &global_wins_table[0][i].address, 42, LONG_CONNECT_TIMEOUT); + if (s==-1) { + DEBUG(0,("run_pull_replication: can't contact wins server %s.\n", inet_ntoa(global_wins_table[0][i].address))); + return; + } + + if(buffer.offset > 0) { + if (!send_smb(s, buffer.buffer)) + exit_server("run_pull_replication: send_smb failed."); + } + + add_fd_to_sock_array(s); + FD_SET(s, listen_set); + + /* add ourself as a client */ + add_partner((int)t, 0, True, False); + } + } +} + +/**************************************************************************** + contact periodically our wins partner to do a push replication +****************************************************************************/ +void run_push_replication(time_t t) +{ + /* we push every 30 minutes or 25 new entries */ + int i, s; + struct BUFFER buffer; + GENERIC_PACKET p; + + buffer.buffer=NULL; + buffer.offset=0; + buffer.length=0; + + for (i=1; i<partner_count; i++) { + if (global_wins_table[0][i].last_pull < t) { + global_wins_table[0][i].last_pull=t+30*60; /* next in 30 minutes */ + + /* contact the wins server */ + p.header.mess_type=MESSAGE_TYPE_START_ASSOC_REQUEST; + p.header.opcode=OPCODE_NON_NBT; + p.header.assoc_ctx=0; + p.sa_rq.assoc_ctx=(int)t; + p.sa_rq.min_ver=1; + p.sa_rq.maj_ver=1; + + DEBUG(3,("run_push_replication: contacting wins server %s.\n", inet_ntoa(global_wins_table[0][i].address))); + encode_generic_packet(&buffer, &p); + dump_generic_packet(&p); + + /* send the packet to the server and add the descriptor to receive answers */ + s=open_socket_out(SOCK_STREAM, &global_wins_table[0][i].address, 42, LONG_CONNECT_TIMEOUT); + if (s==-1) { + DEBUG(0,("run_push_replication: can't contact wins server %s.\n", inet_ntoa(global_wins_table[0][i].address))); + return; + } + + if(buffer.offset > 0) { + if (!send_smb(s, buffer.buffer)) + exit_server("run_push_replication: send_smb failed."); + } + + add_fd_to_sock_array(s); + FD_SET(s, listen_set); + + /* add ourself as a client */ + add_partner((int)t, 0, False, True); + } + } +} + diff --git a/source4/wrepld/server.c b/source4/wrepld/server.c new file mode 100644 index 0000000000..afeec3402b --- /dev/null +++ b/source4/wrepld/server.c @@ -0,0 +1,737 @@ +/* + Unix SMB/CIFS implementation. + Main SMB server routines + Copyright (C) Jean François Micouleau 1998-2002. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "wins_repl.h" + +extern WINS_OWNER *global_wins_table; +extern int partner_count; + +extern fd_set *listen_set; +extern int listen_number; +extern int *sock_array; + +extern TALLOC_CTX *mem_ctx; + +int wins_port = 42; + +/**************************************************************************** + when exiting, take the whole family +****************************************************************************/ +static void *dflt_sig(void) +{ + exit_server("caught signal"); + return NULL; +} + +/**************************************************************************** + reload the services file + **************************************************************************/ +BOOL reload_services(BOOL test) +{ + BOOL ret; + + if (lp_loaded()) { + pstring fname; + pstrcpy(fname,lp_configfile()); + if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) { + pstrcpy(dyn_CONFIGFILE,fname); + test = False; + } + } + + reopen_logs(); + + if (test && !lp_file_list_changed()) + return(True); + + ret = lp_load(dyn_CONFIGFILE,False,False,True); + + + /* perhaps the config filename is now set */ + if (!test) + reload_services(True); + + reopen_logs(); + + load_interfaces(); + + return(ret); +} + +/**************************************************************************** + Catch a sighup. +****************************************************************************/ + +VOLATILE sig_atomic_t reload_after_sighup = False; + +static void sig_hup(int sig) +{ + BlockSignals(True,SIGHUP); + DEBUG(0,("Got SIGHUP\n")); + + sys_select_signal(); + reload_after_sighup = True; + BlockSignals(False,SIGHUP); +} + +#if DUMP_CORE +/******************************************************************* +prepare to dump a core file - carefully! +********************************************************************/ +static BOOL dump_core(void) +{ + char *p; + pstring dname; + pstrcpy(dname,lp_logfile()); + if ((p=strrchr_m(dname,'/'))) *p=0; + pstrcat(dname,"/corefiles"); + mkdir(dname,0700); + sys_chown(dname,getuid(),getgid()); + chmod(dname,0700); + if (chdir(dname)) return(False); + umask(~(0700)); + +#ifdef HAVE_GETRLIMIT +#ifdef RLIMIT_CORE + { + struct rlimit rlp; + getrlimit(RLIMIT_CORE, &rlp); + rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur); + setrlimit(RLIMIT_CORE, &rlp); + getrlimit(RLIMIT_CORE, &rlp); + DEBUG(3,("Core limits now %d %d\n", + (int)rlp.rlim_cur,(int)rlp.rlim_max)); + } +#endif +#endif + + + DEBUG(0,("Dumping core in %s\n",dname)); + abort(); + return(True); +} +#endif + +/**************************************************************************** +exit the server +****************************************************************************/ +void exit_server(const char *reason) +{ + static int firsttime=1; + + if (!firsttime) + exit(0); + firsttime = 0; + + DEBUG(2,("Closing connections\n")); + + if (!reason) { + int oldlevel = DEBUGLEVEL; + DEBUGLEVEL = 10; + DEBUGLEVEL = oldlevel; + DEBUG(0,("===============================================================\n")); +#if DUMP_CORE + if (dump_core()) return; +#endif + } + + DEBUG(3,("Server exit (%s)\n", (reason ? reason : ""))); + exit(0); +} + +/**************************************************************************** + Usage of the program. +****************************************************************************/ + +static void usage(char *pname) +{ + + d_printf("Usage: %s [-DFSaioPh?V] [-d debuglevel] [-l log basename] [-p port]\n", pname); + d_printf(" [-O socket options] [-s services file]\n"); + d_printf("\t-D Become a daemon (default)\n"); + d_printf("\t-F Run daemon in foreground (for daemontools, etc)\n"); + d_printf("\t-S Log to stdout\n"); + d_printf("\t-a Append to log file (default)\n"); + d_printf("\t-i Run interactive (not a daemon)\n" ); + d_printf("\t-o Overwrite log file, don't append\n"); + d_printf("\t-h Print usage\n"); + d_printf("\t-? Print usage\n"); + d_printf("\t-V Print version\n"); + d_printf("\t-d debuglevel Set the debuglevel\n"); + d_printf("\t-l log basename. Basename for log/debug files\n"); + d_printf("\t-p port Listen on the specified port\n"); + d_printf("\t-O socket options Socket options\n"); + d_printf("\t-s services file. Filename of services file\n"); + d_printf("\n"); +} + +/**************************************************************************** + Create an fd_set containing all the sockets in the subnet structures, + plus the broadcast sockets. +***************************************************************************/ + +static BOOL create_listen_fdset(void) +{ + int i; + int num_interfaces = iface_count(); + int s; + + listen_set = (fd_set *)malloc(sizeof(fd_set)); + if(listen_set == NULL) { + DEBUG(0,("create_listen_fdset: malloc fail !\n")); + return True; + } + +#ifdef HAVE_ATEXIT + { + static int atexit_set; + if(atexit_set == 0) { + atexit_set=1; + } + } +#endif + + FD_ZERO(listen_set); + + if(lp_interfaces() && lp_bind_interfaces_only()) { + /* We have been given an interfaces line, and been + told to only bind to those interfaces. Create a + socket per interface and bind to only these. + */ + + if(num_interfaces > FD_SETSIZE) { + DEBUG(0,("create_listen_fdset: Too many interfaces specified to bind to. Number was %d max can be %d\n", num_interfaces, FD_SETSIZE)); + return False; + } + + /* Now open a listen socket for each of the interfaces. */ + for(i = 0; i < num_interfaces; i++) { + struct in_addr *ifip = iface_n_ip(i); + + if(ifip == NULL) { + DEBUG(0,("create_listen_fdset: interface %d has NULL IP address !\n", i)); + continue; + } + s = open_socket_in(SOCK_STREAM, wins_port, 0, ifip->s_addr, True); + if(s == -1) + return False; + + /* ready to listen */ + set_socket_options(s,"SO_KEEPALIVE"); + set_socket_options(s,lp_socket_options()); + + if (listen(s, 5) == -1) { + DEBUG(5,("listen: %s\n",strerror(errno))); + close(s); + return False; + } + add_fd_to_sock_array(s); + FD_SET(s, listen_set); + } + } else { + /* Just bind to 0.0.0.0 - accept connections from anywhere. */ + num_interfaces = 1; + + /* open an incoming socket */ + s = open_socket_in(SOCK_STREAM, wins_port, 0, interpret_addr(lp_socket_address()),True); + if (s == -1) + return(False); + + /* ready to listen */ + set_socket_options(s,"SO_KEEPALIVE"); + set_socket_options(s, lp_socket_options()); + + if (listen(s, 5) == -1) { + DEBUG(0,("create_listen_fdset: listen: %s\n", strerror(errno))); + close(s); + return False; + } + + add_fd_to_sock_array(s); + FD_SET(s, listen_set); + } + + return True; +} + +/******************************************************************* + read a packet from a socket and parse it, returning a packet ready + to be used or put on the queue. This assumes a UDP socket + ******************************************************************/ +static struct wins_packet_struct *read_wins_packet(int fd, int timeout) +{ + struct wins_packet_struct *p; + GENERIC_PACKET *q; + struct BUFFER inbuf; + ssize_t len=0; + size_t total=0; + ssize_t ret; + BOOL ok = False; + + inbuf.buffer=NULL; + inbuf.length=0; + inbuf.offset=0; + + if(!grow_buffer(&inbuf, 4)) + return NULL; + + ok = (read(fd, inbuf.buffer,4) == 4); + if (!ok) + return NULL; + len = smb_len(inbuf.buffer); + + if (len<=0) + return NULL; + + if(!grow_buffer(&inbuf, len)) + return NULL; + + while (total < len) { + ret = read(fd, inbuf.buffer + total + 4, len - total); + if (ret == 0) { + DEBUG(10,("read_socket_data: recv of %d returned 0. Error = %s\n", (int)(len - total), strerror(errno) )); + return NULL; + } + if (ret == -1) { + DEBUG(0,("read_socket_data: recv failure for %d. Error = %s\n", (int)(len - total), strerror(errno) )); + return NULL; + } + total += ret; + } + + q = (GENERIC_PACKET *)talloc(mem_ctx, sizeof(GENERIC_PACKET)); + p = (struct wins_packet_struct *)talloc(mem_ctx, sizeof(*p)); + if (q==NULL || p==NULL) + return NULL; + + decode_generic_packet(&inbuf, q); + + q->fd=fd; + + p->next = NULL; + p->prev = NULL; + p->stop_packet = False; + p->timestamp = time(NULL); + p->fd = fd; + p->packet=q; + + return p; +} + +static struct wins_packet_struct *packet_queue = NULL; + +/******************************************************************* + Queue a packet into a packet queue +******************************************************************/ +static void queue_packet(struct wins_packet_struct *packet) +{ + struct wins_packet_struct *p; + + if (!packet_queue) { + packet->prev = NULL; + packet->next = NULL; + packet_queue = packet; + return; + } + + /* find the bottom */ + for (p=packet_queue;p->next;p=p->next) + ; + + p->next = packet; + packet->next = NULL; + packet->prev = p; +} + +/**************************************************************************** + Listens for NMB or DGRAM packets, and queues them. + return True if the socket is dead +***************************************************************************/ +static BOOL listen_for_wins_packets(void) +{ + int num_interfaces = iface_count(); + fd_set fds; + int i, num, s, new_s; + struct timeval timeout; + + if(listen_set == NULL) { + if(!create_listen_fdset()) { + DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n")); + return True; + } + } + + memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set)); + + timeout.tv_sec = NMBD_SELECT_LOOP; + timeout.tv_usec = 0; + + /* Prepare for the select - allow certain signals. */ + + BlockSignals(False, SIGTERM); + + num = sys_select(FD_SETSIZE, &fds, NULL, NULL, &timeout); + + /* We can only take signals when we are in the select - block them again here. */ + + BlockSignals(True, SIGTERM); + + if(num == -1) + return False; + + for (; num > 0; num--) { + s = -1; + /* check the sockets we are only listening on, waiting to accept */ + for (i=0; i<num_interfaces; i++) { + struct sockaddr addr; + socklen_t in_addrlen = sizeof(addr); + + if(FD_ISSET(sock_array[i], &fds)) { + s = sock_array[i]; + /* Clear this so we don't look at it again. */ + FD_CLR(sock_array[i], &fds); + + /* accept and add the new socket to the listen set */ + new_s=accept(s, &addr, &in_addrlen); + + if (new_s < 0) + continue; + + DEBUG(5,("listen_for_wins_packets: new connection, old: %d, new : %d\n", s, new_s)); + + set_socket_options(new_s, "SO_KEEPALIVE"); + set_socket_options(new_s, lp_socket_options()); + FD_SET(new_s, listen_set); + add_fd_to_sock_array(new_s); + } + } + + /* + * check for the sockets we are waiting data from + * either client sending datas + * or reply to our requests + */ + for (i=num_interfaces; i<listen_number; i++) { + if(FD_ISSET(sock_array[i], &fds)) { + struct wins_packet_struct *packet = read_wins_packet(sock_array[i], timeout.tv_sec); + if (packet) { + packet->fd = sock_array[i]; + queue_packet(packet); + } + DEBUG(2,("listen_for_wins_packets: some data on fd %d\n", sock_array[i])); + FD_CLR(sock_array[i], &fds); + break; + } + + } + + } + + return False; +} + + +/******************************************************************* + Run elements off the packet queue till its empty +******************************************************************/ + +static void run_wins_packet_queue(void) +{ + struct wins_packet_struct *p; + + while ((p = packet_queue)) { + packet_queue = p->next; + if (packet_queue) + packet_queue->prev = NULL; + p->next = p->prev = NULL; + + construct_reply(p); + + /* if it was a stop assoc, close the connection */ + if (p->stop_packet) { + FD_CLR(p->fd, listen_set); + remove_fd_from_sock_array(p->fd); + close(p->fd); + } + } +} + +/**************************************************************************** ** + The main select loop. + **************************************************************************** */ +static void process(void) +{ + + while( True ) { + time_t t = time(NULL); + + /* check for internal messages */ + message_dispatch(); + + if(listen_for_wins_packets()) + return; + + run_wins_packet_queue(); + + run_pull_replication(t); + + run_push_replication(t); + + /* + * Reload the services file if we got a sighup. + */ + + if(reload_after_sighup) { + reload_services( True ); + reopen_logs(); + reload_after_sighup = False; + } + + /* free temp memory */ + talloc_destroy_pool(mem_ctx); + + /* free up temp memory */ + lp_talloc_free(); + } +} /* process */ + +/**************************************************************************** + main program +****************************************************************************/ + int main(int argc,char *argv[]) +{ + extern char *optarg; + /* shall I run as a daemon */ + BOOL is_daemon = False; + BOOL interactive = False; + BOOL specified_logfile = False; + BOOL Fork = True; + BOOL log_stdout = False; + int opt; + pstring logfile; + +#ifdef HAVE_SET_AUTH_PARAMETERS + set_auth_parameters(argc,argv); +#endif + + /* this is for people who can't start the program correctly */ + while (argc > 1 && (*argv[1] != '-')) { + argv++; + argc--; + } + + while ( EOF != (opt = getopt(argc, argv, "FSO:l:s:d:Dp:h?Vaiof:")) ) + switch (opt) { + case 'F': + Fork = False; + break; + case 'S': + log_stdout = True; + break; + case 'O': + lp_set_cmdline("socket options", optarg); + break; + + case 's': + pstrcpy(dyn_CONFIGFILE,optarg); + break; + + case 'l': + specified_logfile = True; + slprintf(logfile, sizeof(logfile)-1, "%s/log.wrepld", optarg); + lp_set_logfile(logfile); + break; + + case 'i': + interactive = True; + Fork = False; + log_stdout = True; + break; + + case 'D': + is_daemon = True; + break; + + case 'd': + if (*optarg == 'A') + DEBUGLEVEL = 10000; + else + DEBUGLEVEL = atoi(optarg); + break; + + case 'p': + wins_port = atoi(optarg); + break; + + case 'h': + case '?': + usage(argv[0]); + exit(0); + break; + + case 'V': + d_printf("Version %s\n",VERSION); + exit(0); + break; + default: + DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n")); + usage(argv[0]); + exit(1); + } + if (log_stdout && Fork) { + d_printf("Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n"); + usage(argv[0]); + exit(1); + } + +#ifdef HAVE_SETLUID + /* needed for SecureWare on SCO */ + setluid(0); +#endif + + sec_init(); + + load_case_tables(); + + if(!specified_logfile) { + slprintf(logfile, sizeof(logfile)-1, "%s/log.wrepld", + dyn_LOGFILEBASE); + lp_set_logfile(logfile); + } + + set_remote_machine_name("wrepld"); + + setup_logging(argv[0],log_stdout); + + /* we want to re-seed early to prevent time delays causing + client problems at a later date. (tridge) */ + generate_random_buffer(NULL, 0, False); + + /* make absolutely sure we run as root - to handle cases where people + are crazy enough to have it setuid */ + + gain_root_privilege(); + gain_root_group_privilege(); + + fault_setup((void (*)(void *))exit_server); + CatchSignal(SIGTERM , SIGNAL_CAST dflt_sig); + + /* we are never interested in SIGPIPE */ + BlockSignals(True,SIGPIPE); + +#if defined(SIGFPE) + /* we are never interested in SIGFPE */ + BlockSignals(True,SIGFPE); +#endif + +#if defined(SIGUSR2) + /* We are no longer interested in USR2 */ + BlockSignals(True,SIGUSR2); +#endif + + /* POSIX demands that signals are inherited. If the invoking process has + * these signals masked, we will have problems, as we won't recieve them. */ + BlockSignals(False, SIGHUP); + BlockSignals(False, SIGUSR1); + + /* we want total control over the permissions on created files, + so set our umask to 0 */ + umask(0); + + reopen_logs(); + + DEBUG(1,( "wrepld version %s started.\n", VERSION)); + DEBUGADD(1,( "Copyright Andrew Tridgell and the Samba Team 1992-2002\n")); + + DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n", + (int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid())); + + if (sizeof(uint16) < 2 || sizeof(uint32) < 4) { + DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n")); + exit(1); + } + + /* + * Do this before reload_services. + */ + + if (!reload_services(False)) + return(-1); + + if (!init_names()) + return -1; + +#ifdef WITH_PROFILE + if (!profile_setup(False)) { + DEBUG(0,("ERROR: failed to setup profiling\n")); + return -1; + } +#endif + + CatchSignal(SIGHUP,SIGNAL_CAST sig_hup); + + DEBUG(3,( "loaded services\n")); + + if (!is_daemon && !is_a_socket(0)) { + DEBUG(0,("standard input is not a socket, assuming -D option\n")); + is_daemon = True; + } + + if (is_daemon && !interactive) { + DEBUG( 3, ( "Becoming a daemon.\n" ) ); + become_daemon(Fork); + } + +#if HAVE_SETPGID + /* + * If we're interactive we want to set our own process group for + * signal management. + */ + if (interactive) + setpgid( (pid_t)0, (pid_t)0); +#endif + + if (!directory_exist(lp_lockdir(), NULL)) { + mkdir(lp_lockdir(), 0755); + } + + if (is_daemon) { + pidfile_create("wrepld"); + } + + if (!message_init()) { + exit(1); + } + + /* Initialise the memory context */ + mem_ctx=talloc_init("wins repl talloc ctx"); + + /* initialise the global partners table */ + partner_count=init_wins_partner_table(); + + /* We can only take signals in the select. */ + BlockSignals( True, SIGTERM ); + + process(); + + exit_server("normal exit"); + return(0); +} diff --git a/source4/wrepld/socket.c b/source4/wrepld/socket.c new file mode 100644 index 0000000000..3d759f0ab8 --- /dev/null +++ b/source4/wrepld/socket.c @@ -0,0 +1,69 @@ +/* + Unix SMB/CIFS implementation. + process incoming packets - main loop + Copyright (C) Jean François Micouleau 1998-2002. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "wins_repl.h" + +fd_set *listen_set = NULL; +int listen_number = 0; +int *sock_array = NULL; + +/******************************************************************* + Add an fd from the sock_array +******************************************************************/ +void add_fd_to_sock_array(int fd) +{ + int *temp_sock=NULL; + + temp_sock=(int *)Realloc(sock_array, (listen_number+1)*sizeof(int)); + if (temp_sock==NULL) + return; + + sock_array=temp_sock; + sock_array[listen_number]=fd; + listen_number++; +} + + +/******************************************************************* + Remove an fd from the sock_array +******************************************************************/ +void remove_fd_from_sock_array(int fd) +{ + int i,j; + + for (i=0; sock_array[i]!=fd && i<listen_number; i++) + ; + + if (i==listen_number) { + DEBUG(0,("remove_fd_from_sock_array: unknown fd: %d\n", fd)); + return; + } + + if (i==listen_number-1) { + sock_array=(int *)Realloc(sock_array, --listen_number*sizeof(int)); + return; + } + + for (j=i; j<listen_number-1; j++) + sock_array[j]=sock_array[j+1]; + + sock_array=(int *)Realloc(sock_array, --listen_number*sizeof(int)); +} diff --git a/source4/wrepld/wins_repl.h b/source4/wrepld/wins_repl.h new file mode 100644 index 0000000000..25b4442212 --- /dev/null +++ b/source4/wrepld/wins_repl.h @@ -0,0 +1,161 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Jean François Micouleau 1998-2002. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define OPCODE_NON_NBT 0x00007800 + +/* the messages */ +#define MESSAGE_TYPE_START_ASSOC_REQUEST 0 +#define MESSAGE_TYPE_START_ASSOC_REPLY 1 +#define MESSAGE_TYPE_STOP_ASSOC 2 +#define MESSAGE_TYPE_REPLICATE 3 + +/* the replication sub-message */ +#define MESSAGE_REP_ADD_VERSION_REQUEST 0 +#define MESSAGE_REP_ADD_VERSION_REPLY 1 +#define MESSAGE_REP_SEND_ENTRIES_REQUEST 2 +#define MESSAGE_REP_SEND_ENTRIES_REPLY 3 +#define MESSAGE_REP_UPDATE_NOTIFY_REQUEST 4 + +/* stop reasons */ +#define STOP_REASON_USER_REASON 0 +#define STOP_REASON_AUTH_FAILED 1 +#define STOP_REASON_INCOMPLETE_VERSION 2 +#define STOP_REASON_BUG_CHECK 3 +#define STOP_REASON_MESSAGE_ERROR 4 + + +typedef struct _WINS_OWNER { + struct in_addr address; + SMB_BIG_UINT max_version; + SMB_BIG_UINT min_version; + int type; + time_t last_pull; + time_t last_push; +} WINS_OWNER; + +typedef struct _WINS_NAME { + int name_len; /* always 0x11 */ + char name[16]; + char type; + int empty; + int name_flag; + int group_flag; + SMB_BIG_UINT id; + int num_ip; + struct in_addr owner; + struct in_addr *others; + int foo; /* 0xffffff */ +} WINS_NAME; + +typedef struct _WINS_PARTNERS +{ + int client_assoc; + int server_assoc; + BOOL pull_partner; + BOOL push_partner; + struct in_addr partner_server; + struct in_addr other_server; +} WINS_PARTNER; + +typedef struct _generic_header{ + int data_size; + int opcode; + int assoc_ctx; + int mess_type; +} generic_header; + +typedef struct _START_ASSOC_REQUEST { + int assoc_ctx; + int min_ver; + int maj_ver; +} START_ASSOC_REQUEST; + +typedef struct _START_ASSOC_REPLY { + int assoc_ctx; + int min_ver; + int maj_ver; +} START_ASSOC_REPLY; + +typedef struct _STOP_ASSOC { + int reason; +} STOP_ASSOC; + +typedef struct _AVMT_REP { + int partner_count; + WINS_OWNER *wins_owner; + struct in_addr initiating_wins_server; +} AVMT_REP; + +typedef struct _SEND_ENTRIES_REQUEST { + WINS_OWNER wins_owner; +} SEND_ENTRIES_REQUEST; + +typedef struct _SEND_ENTRIES_REPLY { + int max_names; + WINS_NAME *wins_name; +} SEND_ENTRIES_REPLY; + +typedef struct _UPDATE_NOTIFY_REQUEST { + int partner_count; + WINS_OWNER *wins_owner; + struct in_addr initiating_wins_server; +} UPDATE_NOTIFY_REQUEST; + +typedef struct _REPLICATE { + int msg_type; + + AVMT_REP avmt_rep; + SEND_ENTRIES_REQUEST se_rq; + SEND_ENTRIES_REPLY se_rp; + UPDATE_NOTIFY_REQUEST un_rq; +} REPLICATE; + + +typedef struct _GENERIC_PACKET { + int fd; + + generic_header header; + + START_ASSOC_REQUEST sa_rq; + START_ASSOC_REPLY sa_rp; + STOP_ASSOC so; + REPLICATE rep; +} GENERIC_PACKET; + +struct wins_packet_struct +{ + struct wins_packet_struct *next; + struct wins_packet_struct *prev; + BOOL stop_packet; + int fd; + time_t timestamp; + GENERIC_PACKET *packet; +}; + +struct BUFFER { + char *buffer; + int offset; + int length; +}; + + + +#include "wrepld_proto.h" + |