r2655: fixed an error in the shutdown of the sock->transport->session->tree
[samba.git] / source4 / libcli / raw / clitree.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB client tree context management functions
4    Copyright (C) Andrew Tridgell 1994-1998
5    Copyright (C) James Myers 2003 <myersjj@samba.org>
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 #define SETUP_REQUEST_TREE(cmd, wct, buflen) do { \
25         req = smbcli_request_setup(tree, cmd, wct, buflen); \
26         if (!req) return NULL; \
27 } while (0)
28
29
30 /****************************************************************************
31  Initialize the tree context
32 ****************************************************************************/
33 struct smbcli_tree *smbcli_tree_init(struct smbcli_session *session)
34 {
35         struct smbcli_tree *tree;
36
37         tree = talloc_p(session, struct smbcli_tree);
38         if (!tree) {
39                 return NULL;
40         }
41
42         ZERO_STRUCTP(tree);
43         tree->session = session;
44         tree->session->reference_count++;
45
46         return tree;
47 }
48
49 /****************************************************************************
50 reduce reference count on a tree and destroy if <= 0
51 ****************************************************************************/
52 void smbcli_tree_close(struct smbcli_tree *tree)
53 {
54         if (!tree) return;
55         tree->reference_count--;
56         if (tree->reference_count <= 0) {
57                 smbcli_session_close(tree->session);
58         }
59 }
60
61
62 /****************************************************************************
63  Send a tconX (async send)
64 ****************************************************************************/
65 struct smbcli_request *smb_tree_connect_send(struct smbcli_tree *tree, union smb_tcon *parms)
66 {
67         struct smbcli_request *req = NULL;
68
69         switch (parms->tcon.level) {
70         case RAW_TCON_TCON:
71                 SETUP_REQUEST_TREE(SMBtcon, 0, 0);
72                 smbcli_req_append_ascii4(req, parms->tcon.in.service, STR_ASCII);
73                 smbcli_req_append_ascii4(req, parms->tcon.in.password,STR_ASCII);
74                 smbcli_req_append_ascii4(req, parms->tcon.in.dev,     STR_ASCII);
75                 break;
76
77         case RAW_TCON_TCONX:
78                 SETUP_REQUEST_TREE(SMBtconX, 4, 0);
79                 SSVAL(req->out.vwv, VWV(0), 0xFF);
80                 SSVAL(req->out.vwv, VWV(1), 0);
81                 SSVAL(req->out.vwv, VWV(2), parms->tconx.in.flags);
82                 SSVAL(req->out.vwv, VWV(3), parms->tconx.in.password.length);
83                 smbcli_req_append_blob(req, &parms->tconx.in.password);
84                 smbcli_req_append_string(req, parms->tconx.in.path,   STR_TERMINATE | STR_UPPER);
85                 smbcli_req_append_string(req, parms->tconx.in.device, STR_TERMINATE | STR_ASCII);
86                 break;
87         }
88
89         if (!smbcli_request_send(req)) {
90                 smbcli_request_destroy(req);
91                 return NULL;
92         }
93
94         return req;
95 }
96
97 /****************************************************************************
98  Send a tconX (async recv)
99 ****************************************************************************/
100 NTSTATUS smb_tree_connect_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_tcon *parms)
101 {
102         char *p;
103
104         if (!smbcli_request_receive(req) ||
105             smbcli_request_is_error(req)) {
106                 goto failed;
107         }
108
109         switch (parms->tcon.level) {
110         case RAW_TCON_TCON:
111                 SMBCLI_CHECK_WCT(req, 2);
112                 parms->tcon.out.max_xmit = SVAL(req->in.vwv, VWV(0));
113                 parms->tcon.out.cnum = SVAL(req->in.vwv, VWV(1));
114                 break;
115
116         case RAW_TCON_TCONX:
117                 ZERO_STRUCT(parms->tconx.out);
118                 parms->tconx.out.cnum = SVAL(req->in.hdr, HDR_TID);
119                 if (req->in.wct >= 4) {
120                         parms->tconx.out.options = SVAL(req->in.vwv, VWV(3));
121                 }
122
123                 /* output is actual service name */
124                 p = req->in.data;
125                 if (!p) break;
126
127                 p += smbcli_req_pull_string(req, mem_ctx, &parms->tconx.out.dev_type, 
128                                          p, -1, STR_ASCII | STR_TERMINATE);
129                 p += smbcli_req_pull_string(req, mem_ctx, &parms->tconx.out.fs_type, 
130                                          p, -1, STR_TERMINATE);
131                 break;
132         }
133
134 failed:
135         return smbcli_request_destroy(req);
136 }
137
138 /****************************************************************************
139  Send a tconX (sync interface)
140 ****************************************************************************/
141 NTSTATUS smb_tree_connect(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_tcon *parms)
142 {
143         struct smbcli_request *req = smb_tree_connect_send(tree, parms);
144         return smb_tree_connect_recv(req, mem_ctx, parms);
145 }
146
147
148 /****************************************************************************
149  Send a tree disconnect.
150 ****************************************************************************/
151 NTSTATUS smb_tree_disconnect(struct smbcli_tree *tree)
152 {
153         struct smbcli_request *req;
154
155         if (!tree) return NT_STATUS_OK;
156         req = smbcli_request_setup(tree, SMBtdis, 0, 0);
157
158         if (smbcli_request_send(req)) {
159                 smbcli_request_receive(req);
160         }
161         return smbcli_request_destroy(req);
162 }
163
164
165 /*
166   a convenient function to establish a smbcli_tree from scratch, using reasonable default
167   parameters
168 */
169 NTSTATUS smbcli_tree_full_connection(struct smbcli_tree **ret_tree, 
170                                      const char *my_name, 
171                                      const char *dest_host, int port,
172                                      const char *service, const char *service_type,
173                                      const char *user, const char *domain, 
174                                      const char *password)
175 {
176         struct smbcli_socket *sock;
177         struct smbcli_transport *transport;
178         struct smbcli_session *session;
179         struct smbcli_tree *tree;
180         NTSTATUS status;
181         struct nmb_name calling;
182         struct nmb_name called;
183         union smb_sesssetup setup;
184         union smb_tcon tcon;
185         TALLOC_CTX *mem_ctx;
186         char *in_path = NULL;
187
188         *ret_tree = NULL;
189
190         sock = smbcli_sock_init();
191         if (!sock) {
192                 return NT_STATUS_NO_MEMORY;
193         }
194
195         talloc_set_name_const(sock, "smbcli_tree_full_connection");
196
197         /* open a TCP socket to the server */
198         if (!smbcli_sock_connect_byname(sock, dest_host, port)) {
199                 DEBUG(2,("Failed to establish socket connection - %s\n", strerror(errno)));
200                 return NT_STATUS_UNSUCCESSFUL;
201         }
202
203         transport = smbcli_transport_init(sock);
204         if (!transport) {
205                 smbcli_sock_close(sock);
206                 return NT_STATUS_NO_MEMORY;
207         }
208
209         /* send a NBT session request, if applicable */
210         make_nmb_name(&calling, my_name, 0x0);
211         choose_called_name(&called,  dest_host, 0x20);
212
213         if (!smbcli_transport_connect(transport, &calling, &called)) {
214                 smbcli_transport_close(transport);
215                 return NT_STATUS_UNSUCCESSFUL;
216         }
217
218
219         /* negotiate protocol options with the server */
220         status = smb_raw_negotiate(transport);
221         if (!NT_STATUS_IS_OK(status)) {
222                 smbcli_transport_close(transport);
223                 return status;
224         }
225
226         session = smbcli_session_init(transport);
227         if (!session) {
228                 smbcli_transport_close(transport);
229                 return NT_STATUS_NO_MEMORY;
230         }
231
232         /* prepare a session setup to establish a security context */
233         setup.generic.level = RAW_SESSSETUP_GENERIC;
234         setup.generic.in.sesskey = transport->negotiate.sesskey;
235         setup.generic.in.capabilities = transport->negotiate.capabilities;
236         if (!user || !user[0]) {
237                 setup.generic.in.password = NULL;
238                 setup.generic.in.user = "";
239                 setup.generic.in.domain = "";
240                 setup.generic.in.capabilities &= ~CAP_EXTENDED_SECURITY;
241         } else {
242                 setup.generic.in.password = password;
243                 setup.generic.in.user = user;
244                 setup.generic.in.domain = domain;
245         }
246
247         mem_ctx = talloc_init("tcon");
248         if (!mem_ctx) {
249                 return NT_STATUS_NO_MEMORY;
250         }
251
252         status = smb_raw_session_setup(session, mem_ctx, &setup);
253         if (!NT_STATUS_IS_OK(status)) {
254                 smbcli_session_close(session);
255                 talloc_free(mem_ctx);
256                 return status;
257         }
258
259         session->vuid = setup.generic.out.vuid;
260
261         tree = smbcli_tree_init(session);
262         if (!tree) {
263                 smbcli_session_close(session);
264                 talloc_free(mem_ctx);
265                 return NT_STATUS_NO_MEMORY;
266         }
267
268         /* connect to a share using a tree connect */
269         tcon.generic.level = RAW_TCON_TCONX;
270         tcon.tconx.in.flags = 0;
271         tcon.tconx.in.password = data_blob(NULL, 0);    
272         asprintf(&in_path, "\\\\%s\\%s", dest_host, service);
273         tcon.tconx.in.path = in_path;
274         if (!service_type) {
275                 if (strequal(service, "IPC$"))
276                         service_type = "IPC";
277                 else
278                         service_type = "?????";
279         }
280         tcon.tconx.in.device = service_type;
281         
282         status = smb_tree_connect(tree, mem_ctx, &tcon);
283
284         SAFE_FREE(in_path);
285
286         if (!NT_STATUS_IS_OK(status)) {
287                 smbcli_tree_close(tree);
288                 talloc_free(mem_ctx);
289                 return status;
290         }
291
292         tree->tid = tcon.tconx.out.cnum;
293         if (tcon.tconx.out.dev_type) {
294                 tree->device = talloc_strdup(tree, tcon.tconx.out.dev_type);
295         }
296         if (tcon.tconx.out.fs_type) {
297                 tree->fs_type = talloc_strdup(tree, tcon.tconx.out.fs_type);
298         }
299
300         talloc_free(mem_ctx);
301
302         *ret_tree = tree;
303         return NT_STATUS_OK;
304 }