1/*
2   Unix SMB/CIFS implementation.
3   NBT netbios library routines
4   Copyright (C) Andrew Tridgell 1994-1998
5   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
6   Copyright (C) Jeremy Allison 1994-1998
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23
24#include "includes.h"
25
26int num_response_packets = 0;
27
28/***************************************************************************
29  Add an expected response record into the list
30  **************************************************************************/
31
32static void add_response_record(struct subnet_record *subrec,
33				struct response_record *rrec)
34{
35	struct response_record *rrec2;
36
37	num_response_packets++; /* count of total number of packets still around */
38
39	DEBUG(4,("add_response_record: adding response record id:%hu to subnet %s. num_records:%d\n",
40		rrec->response_id, subrec->subnet_name, num_response_packets));
41
42	if (!subrec->responselist) {
43		subrec->responselist = rrec;
44		rrec->prev = NULL;
45		rrec->next = NULL;
46		return;
47	}
48
49	for (rrec2 = subrec->responselist; rrec2->next; rrec2 = rrec2->next)
50		;
51
52	rrec2->next = rrec;
53	rrec->next = NULL;
54	rrec->prev = rrec2;
55}
56
57/***************************************************************************
58  Remove an expected response record from the list
59  **************************************************************************/
60
61void remove_response_record(struct subnet_record *subrec,
62				struct response_record *rrec)
63{
64	if (rrec->prev)
65		rrec->prev->next = rrec->next;
66	if (rrec->next)
67		rrec->next->prev = rrec->prev;
68
69	if (subrec->responselist == rrec)
70		subrec->responselist = rrec->next;
71
72	if(rrec->userdata) {
73		if(rrec->userdata->free_fn) {
74			(*rrec->userdata->free_fn)(rrec->userdata);
75		} else {
76			ZERO_STRUCTP(rrec->userdata);
77			SAFE_FREE(rrec->userdata);
78		}
79	}
80
81	/* Ensure we can delete. */
82	rrec->packet->locked = False;
83	free_packet(rrec->packet);
84
85	ZERO_STRUCTP(rrec);
86	SAFE_FREE(rrec);
87
88	num_response_packets--; /* count of total number of packets still around */
89}
90
91/****************************************************************************
92  Create a response record for an outgoing packet.
93  **************************************************************************/
94
95struct response_record *make_response_record( struct subnet_record *subrec,
96					      struct packet_struct *p,
97					      response_function resp_fn,
98					      timeout_response_function timeout_fn,
99					      success_function success_fn,
100					      fail_function fail_fn,
101					      struct userdata_struct *userdata)
102{
103	struct response_record *rrec;
104	struct nmb_packet *nmb = &p->packet.nmb;
105
106	if (!(rrec = SMB_MALLOC_P(struct response_record))) {
107		DEBUG(0,("make_response_queue_record: malloc fail for response_record.\n"));
108		return NULL;
109	}
110
111	memset((char *)rrec, '\0', sizeof(*rrec));
112
113	rrec->response_id = nmb->header.name_trn_id;
114
115	rrec->resp_fn = resp_fn;
116	rrec->timeout_fn = timeout_fn;
117	rrec->success_fn = success_fn;
118	rrec->fail_fn = fail_fn;
119
120	rrec->packet = p;
121
122	if(userdata) {
123		/* Intelligent userdata. */
124		if(userdata->copy_fn) {
125			if((rrec->userdata = (*userdata->copy_fn)(userdata)) == NULL) {
126				DEBUG(0,("make_response_queue_record: copy fail for userdata.\n"));
127				ZERO_STRUCTP(rrec);
128				SAFE_FREE(rrec);
129				return NULL;
130			}
131		} else {
132			/* Primitive userdata, do a memcpy. */
133			if((rrec->userdata = (struct userdata_struct *)
134					SMB_MALLOC(sizeof(struct userdata_struct)+userdata->userdata_len)) == NULL) {
135				DEBUG(0,("make_response_queue_record: malloc fail for userdata.\n"));
136				ZERO_STRUCTP(rrec);
137				SAFE_FREE(rrec);
138				return NULL;
139			}
140			rrec->userdata->copy_fn = userdata->copy_fn;
141			rrec->userdata->free_fn = userdata->free_fn;
142			rrec->userdata->userdata_len = userdata->userdata_len;
143			memcpy(rrec->userdata->data, userdata->data, userdata->userdata_len);
144		}
145	} else {
146		rrec->userdata = NULL;
147	}
148
149	rrec->num_msgs = 0;
150
151	if(!nmb->header.nm_flags.bcast)
152		rrec->repeat_interval = 5; /* 5 seconds for unicast packets. */
153	else
154		rrec->repeat_interval = 1; /* XXXX should be in ms */
155	rrec->repeat_count = 3; /* 3 retries */
156	rrec->repeat_time = time(NULL) + rrec->repeat_interval; /* initial retry time */
157
158	/* This packet is not being processed. */
159	rrec->in_expiration_processing = False;
160
161	/* Lock the packet so we won't lose it while it's on the list. */
162	p->locked = True;
163
164	add_response_record(subrec, rrec);
165
166	return rrec;
167}
168
169/****************************************************************************
170  Find a response in a subnet's name query response list.
171  **************************************************************************/
172
173static struct response_record *find_response_record_on_subnet(
174                                struct subnet_record *subrec, uint16 id)
175{
176	struct response_record *rrec = NULL;
177
178	for (rrec = subrec->responselist; rrec; rrec = rrec->next) {
179		if (rrec->response_id == id) {
180			DEBUG(4, ("find_response_record: found response record id = %hu on subnet %s\n",
181				id, subrec->subnet_name));
182			break;
183		}
184	}
185	return rrec;
186}
187
188/****************************************************************************
189  Find a response in any subnet's name query response list.
190  **************************************************************************/
191
192struct response_record *find_response_record(struct subnet_record **ppsubrec,
193				uint16 id)
194{
195	struct response_record *rrec = NULL;
196
197	for ((*ppsubrec) = FIRST_SUBNET; (*ppsubrec);
198				(*ppsubrec) = NEXT_SUBNET_INCLUDING_UNICAST(*ppsubrec)) {
199		if((rrec = find_response_record_on_subnet(*ppsubrec, id)) != NULL)
200			return rrec;
201	}
202
203	/* There should never be response records on the remote_broadcast subnet.
204			Sanity check to ensure this is so. */
205	if(remote_broadcast_subnet->responselist != NULL) {
206		DEBUG(0,("find_response_record: response record found on subnet %s. This should \
207never happen !\n", remote_broadcast_subnet->subnet_name));
208	}
209
210	/* Now check the WINS server subnet if it exists. */
211	if(wins_server_subnet != NULL) {
212		*ppsubrec = wins_server_subnet;
213		if((rrec = find_response_record_on_subnet(*ppsubrec, id))!= NULL)
214			return rrec;
215	}
216
217	DEBUG(3,("find_response_record: response packet id %hu received with no \
218matching record.\n", id));
219
220	*ppsubrec = NULL;
221
222	return NULL;
223}
224
225/****************************************************************************
226  Check if a refresh is queued for a particular name on a particular subnet.
227  **************************************************************************/
228
229BOOL is_refresh_already_queued(struct subnet_record *subrec, struct name_record *namerec)
230{
231	struct response_record *rrec = NULL;
232
233	for (rrec = subrec->responselist; rrec; rrec = rrec->next) {
234		struct packet_struct *p = rrec->packet;
235		struct nmb_packet *nmb = &p->packet.nmb;
236
237		if((nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) ||
238				(nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9)) {
239			/* Yes it's a queued refresh - check if the name is correct. */
240			if(nmb_name_equal(&nmb->question.question_name, &namerec->name))
241			return True;
242		}
243	}
244
245	return False;
246}
247