r4924: continue the effort to simplify and generalise the composite
[jelmer/samba4-debian.git] / source / libcli / raw / clisocket.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    SMB client socket context management functions
5
6    Copyright (C) Andrew Tridgell 1994-2005
7    Copyright (C) James Myers 2003 <myersjj@samba.org>
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "events.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/composite/composite.h"
28
29 /*
30   this private structure is used during async connection handling
31 */
32 struct clisocket_connect {
33         int port_num;
34         int *iports;
35         struct smbcli_socket *sock;
36         const char *dest_host;
37 };
38
39
40 static int smbcli_sock_destructor(void *ptr)
41 {
42         struct smbcli_socket *sock = talloc_get_type(ptr, struct smbcli_socket);
43
44         if (sock->event.fde && sock->event.ctx) {
45                 event_remove_fd(sock->event.ctx, sock->event.fde);
46         }
47         return 0;
48 }
49
50 /*
51   create a smbcli_socket context
52 */
53 struct smbcli_socket *smbcli_sock_init(TALLOC_CTX *mem_ctx)
54 {
55         struct smbcli_socket *sock;
56
57         sock = talloc_zero(mem_ctx, struct smbcli_socket);
58         if (!sock) {
59                 return NULL;
60         }
61
62         sock->event.ctx = event_context_init(sock);
63         if (sock->event.ctx == NULL) {
64                 talloc_free(sock);
65                 return NULL;
66         }
67
68         talloc_set_destructor(sock, smbcli_sock_destructor);
69
70         return sock;
71 }
72
73 static NTSTATUS smbcli_sock_connect_one(struct smbcli_socket *sock, 
74                                         const char *hostaddr, int port);
75
76 /*
77   handle socket write events during an async connect. These happen when the OS
78   has either completed the connect() or has returned an error
79 */
80 static void smbcli_sock_connect_handler(struct event_context *ev, struct fd_event *fde, 
81                                         struct timeval t, uint16_t flags)
82 {
83         struct smbcli_composite *c = talloc_get_type(fde->private, struct smbcli_composite);
84         struct clisocket_connect *conn = talloc_get_type(c->private, struct clisocket_connect);
85         int i;
86         
87         c->status = socket_connect_complete(conn->sock->sock, 0);
88         if (NT_STATUS_IS_OK(c->status)) {
89                 socket_set_option(conn->sock->sock, lp_socket_options(), NULL);
90                 conn->sock->hostname = talloc_strdup(conn->sock, conn->dest_host);
91                 c->state = SMBCLI_REQUEST_DONE;
92                 if (c->async.fn) {
93                         c->async.fn(c);
94                 }
95                 return;
96         }
97
98         /* that port failed - try the next port */
99         for (i=conn->port_num+1;conn->iports[i];i++) {
100                 conn->port_num = i;
101                 c->status = smbcli_sock_connect_one(conn->sock, 
102                                                     conn->dest_host, 
103                                                     conn->iports[i]);
104                 if (NT_STATUS_IS_OK(c->status) ||
105                     NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
106                         conn->sock->event.fde->private = c;
107                         return;
108                 }
109         }
110
111         c->state = SMBCLI_REQUEST_ERROR;
112         if (c->async.fn) {
113                 c->async.fn(c);
114         }
115 }
116
117
118 /*
119   try to connect to the given address/port
120 */
121 static NTSTATUS smbcli_sock_connect_one(struct smbcli_socket *sock, 
122                                         const char *hostaddr, int port)
123 {
124         struct fd_event fde;
125         NTSTATUS status;
126
127         if (sock->sock) {
128                 talloc_free(sock->sock);
129                 sock->sock = NULL;
130         }
131
132         if (sock->event.fde) {
133                 event_remove_fd(sock->event.ctx, sock->event.fde);
134                 sock->event.fde = NULL;
135         }
136
137         status = socket_create("ip", SOCKET_TYPE_STREAM, &sock->sock, 0);
138         if (!NT_STATUS_IS_OK(status)) {
139                 return status;
140         }
141
142         talloc_steal(sock, sock->sock);
143
144         /* we initially look for write - see the man page on
145            non-blocking connect */
146         fde.fd = socket_get_fd(sock->sock);
147         fde.flags = EVENT_FD_WRITE;
148         fde.handler = smbcli_sock_connect_handler;
149         fde.private = sock;
150
151         sock->event.fde = event_add_fd(sock->event.ctx, &fde);
152         sock->port = port;
153         set_blocking(fde.fd, False);
154
155         return socket_connect(sock->sock, NULL, 0, hostaddr, port, 0);
156 }
157                                         
158
159 /*
160   connect a smbcli_socket context to an IP/port pair
161   if port is 0 then choose 445 then 139
162
163   this is the async send side of the interface
164 */
165 struct smbcli_composite *smbcli_sock_connect_send(struct smbcli_socket *sock, 
166                                                   const char *host_addr, int port)
167 {
168         struct smbcli_composite *c;
169         struct clisocket_connect *conn;
170         int i;
171
172         c = talloc_zero(sock, struct smbcli_composite);
173         if (c == NULL) return NULL;
174
175         c->event_ctx = sock->event.ctx;
176
177         conn = talloc(c, struct clisocket_connect);
178         if (conn == NULL) goto failed;
179
180         conn->sock = sock;
181
182         /* work out what ports we will try */
183         if (port == 0) {
184                 const char **ports = lp_smb_ports();
185                 for (i=0;ports[i];i++) /* noop */ ;
186                 conn->iports = talloc_array(c, int, i+1);
187                 if (conn->iports == NULL) goto failed;
188                 for (i=0;ports[i];i++) {
189                         conn->iports[i] = atoi(ports[i]);
190                 }
191                 conn->iports[i] = 0;
192         } else {
193                 conn->iports = talloc_array(c, int, 2);
194                 if (conn->iports == NULL) goto failed;
195                 conn->iports[0] = port;
196                 conn->iports[1] = 0;
197         }
198
199         conn->dest_host = talloc_strdup(c, host_addr);
200         if (conn->dest_host == NULL) goto failed;
201
202         c->private = conn;
203         c->state = SMBCLI_REQUEST_SEND;
204
205         /* startup the connect process for each port in turn until one
206            succeeds or tells us that it is pending */
207         for (i=0;conn->iports[i];i++) {
208                 conn->port_num = i;
209                 conn->sock->port = conn->iports[i];
210                 c->status = smbcli_sock_connect_one(sock, 
211                                                     conn->dest_host, 
212                                                     conn->iports[i]);
213                 if (NT_STATUS_IS_OK(c->status) ||
214                     NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
215                         sock->event.fde->private = c;
216                         return c;
217                 }
218         }
219
220         c->state = SMBCLI_REQUEST_ERROR;
221         return c;
222         
223 failed:
224         talloc_free(c);
225         return NULL;
226 }
227
228 /*
229   finish a smbcli_sock_connect_send() operation
230 */
231 NTSTATUS smbcli_sock_connect_recv(struct smbcli_composite *c)
232 {
233         NTSTATUS status;
234         status = smb_composite_wait(c);
235         talloc_free(c);
236         return status;
237 }
238
239 /*
240   connect a smbcli_socket context to an IP/port pair
241   if port is 0 then choose the ports listed in smb.conf (normally 445 then 139)
242
243   sync version of the function
244 */
245 NTSTATUS smbcli_sock_connect(struct smbcli_socket *sock, const char *host_addr, int port)
246 {
247         struct smbcli_composite *c;
248
249         c = smbcli_sock_connect_send(sock, host_addr, port);
250         if (c == NULL) {
251                 return NT_STATUS_NO_MEMORY;
252         }
253
254         return smbcli_sock_connect_recv(c);
255 }
256
257
258 /****************************************************************************
259  mark the socket as dead
260 ****************************************************************************/
261 void smbcli_sock_dead(struct smbcli_socket *sock)
262 {
263         if (sock->sock != NULL) {
264                 talloc_free(sock->sock);
265                 sock->sock = NULL;
266         }
267 }
268
269 /****************************************************************************
270  Set socket options on a open connection.
271 ****************************************************************************/
272 void smbcli_sock_set_options(struct smbcli_socket *sock, const char *options)
273 {
274         socket_set_option(sock->sock, options, NULL);
275 }
276
277 /****************************************************************************
278  Write to socket. Return amount written.
279 ****************************************************************************/
280 ssize_t smbcli_sock_write(struct smbcli_socket *sock, const uint8_t *data, size_t len)
281 {
282         NTSTATUS status;
283         DATA_BLOB blob;
284         size_t nsent;
285
286         if (sock->sock == NULL) {
287                 errno = EIO;
288                 return -1;
289         }
290
291         blob.data = discard_const(data);
292         blob.length = len;
293
294         status = socket_send(sock->sock, &blob, &nsent, 0);
295         if (NT_STATUS_IS_ERR(status)) {
296                 return -1;
297         }
298
299         return nsent;
300 }
301
302
303 /****************************************************************************
304  Read from socket. return amount read
305 ****************************************************************************/
306 ssize_t smbcli_sock_read(struct smbcli_socket *sock, uint8_t *data, size_t len)
307 {
308         NTSTATUS status;
309         size_t nread;
310
311         if (sock->sock == NULL) {
312                 errno = EIO;
313                 return -1;
314         }
315
316         status = socket_recv(sock->sock, data, len, &nread, 0);
317         if (NT_STATUS_IS_ERR(status)) {
318                 return -1;
319         }
320
321         return nread;
322 }
323
324
325 /****************************************************************************
326 resolve a hostname and connect 
327 ****************************************************************************/
328 BOOL smbcli_sock_connect_byname(struct smbcli_socket *sock, const char *host, int port)
329 {
330         int name_type = NBT_NAME_SERVER;
331         const char *address;
332         NTSTATUS status;
333         struct nbt_name nbt_name;
334         char *name, *p;
335
336         name = talloc_strdup(sock, host);
337
338         /* allow hostnames of the form NAME#xx and do a netbios lookup */
339         if ((p = strchr(name, '#'))) {
340                 name_type = strtol(p+1, NULL, 16);
341                 *p = 0;
342         }
343
344         nbt_name.name = name;
345         nbt_name.type = name_type;
346         nbt_name.scope = NULL;
347         
348         status = resolve_name(&nbt_name, sock, &address);
349         if (!NT_STATUS_IS_OK(status)) {
350                 return False;
351         }
352
353         sock->hostname = name;
354
355         status = smbcli_sock_connect(sock, address, port);
356
357         return NT_STATUS_IS_OK(status);
358 }