r4943: Smplified the events handling code a lot. The first source of
[nivanova/samba-autobuild/.git] / source4 / 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 /*
41   create a smbcli_socket context
42   The event_ctx is optional - if not supplied one will be created
43 */
44 struct smbcli_socket *smbcli_sock_init(TALLOC_CTX *mem_ctx, 
45                                        struct event_context *event_ctx)
46 {
47         struct smbcli_socket *sock;
48
49         sock = talloc_zero(mem_ctx, struct smbcli_socket);
50         if (!sock) {
51                 return NULL;
52         }
53
54         if (event_ctx) {
55                 sock->event.ctx = talloc_reference(sock, event_ctx);
56         } else {
57                 sock->event.ctx = event_context_init(sock);
58         }
59         if (sock->event.ctx == NULL) {
60                 talloc_free(sock);
61                 return NULL;
62         }
63
64         return sock;
65 }
66
67 static NTSTATUS smbcli_sock_connect_one(struct smbcli_socket *sock, 
68                                         const char *hostaddr, int port);
69
70 /*
71   handle socket write events during an async connect. These happen when the OS
72   has either completed the connect() or has returned an error
73 */
74 static void smbcli_sock_connect_handler(struct event_context *ev, struct fd_event *fde, 
75                                         struct timeval t, uint16_t flags)
76 {
77         struct smbcli_composite *c = talloc_get_type(fde->private, struct smbcli_composite);
78         struct clisocket_connect *conn = talloc_get_type(c->private, struct clisocket_connect);
79         int i;
80         
81         c->status = socket_connect_complete(conn->sock->sock, 0);
82         if (NT_STATUS_IS_OK(c->status)) {
83                 socket_set_option(conn->sock->sock, lp_socket_options(), NULL);
84                 conn->sock->hostname = talloc_strdup(conn->sock, conn->dest_host);
85                 c->state = SMBCLI_REQUEST_DONE;
86                 if (c->async.fn) {
87                         c->async.fn(c);
88                 }
89                 return;
90         }
91
92         /* that port failed - try the next port */
93         for (i=conn->port_num+1;conn->iports[i];i++) {
94                 conn->port_num = i;
95                 c->status = smbcli_sock_connect_one(conn->sock, 
96                                                     conn->dest_host, 
97                                                     conn->iports[i]);
98                 if (NT_STATUS_IS_OK(c->status) ||
99                     NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
100                         conn->sock->event.fde->private = c;
101                         return;
102                 }
103         }
104
105         c->state = SMBCLI_REQUEST_ERROR;
106         if (c->async.fn) {
107                 c->async.fn(c);
108         }
109 }
110
111
112 /*
113   try to connect to the given address/port
114 */
115 static NTSTATUS smbcli_sock_connect_one(struct smbcli_socket *sock, 
116                                         const char *hostaddr, int port)
117 {
118         struct fd_event fde;
119         NTSTATUS status;
120
121         if (sock->sock) {
122                 talloc_free(sock->sock);
123                 sock->sock = NULL;
124         }
125         talloc_free(sock->event.fde);
126
127         status = socket_create("ip", SOCKET_TYPE_STREAM, &sock->sock, 0);
128         if (!NT_STATUS_IS_OK(status)) {
129                 return status;
130         }
131
132         talloc_steal(sock, sock->sock);
133
134         /* we initially look for write - see the man page on
135            non-blocking connect */
136         fde.fd = socket_get_fd(sock->sock);
137         fde.flags = EVENT_FD_WRITE;
138         fde.handler = smbcli_sock_connect_handler;
139         fde.private = sock;
140
141         sock->event.fde = event_add_fd(sock->event.ctx, &fde);
142         talloc_steal(sock, sock->event.fde);
143
144         sock->port = port;
145         set_blocking(fde.fd, False);
146
147         return socket_connect(sock->sock, NULL, 0, hostaddr, port, 0);
148 }
149                                         
150
151 /*
152   connect a smbcli_socket context to an IP/port pair
153   if port is 0 then choose 445 then 139
154
155   this is the async send side of the interface
156 */
157 struct smbcli_composite *smbcli_sock_connect_send(struct smbcli_socket *sock, 
158                                                   const char *host_addr, int port)
159 {
160         struct smbcli_composite *c;
161         struct clisocket_connect *conn;
162         int i;
163
164         c = talloc_zero(sock, struct smbcli_composite);
165         if (c == NULL) return NULL;
166
167         c->event_ctx = sock->event.ctx;
168
169         conn = talloc(c, struct clisocket_connect);
170         if (conn == NULL) goto failed;
171
172         conn->sock = sock;
173
174         /* work out what ports we will try */
175         if (port == 0) {
176                 const char **ports = lp_smb_ports();
177                 for (i=0;ports[i];i++) /* noop */ ;
178                 conn->iports = talloc_array(c, int, i+1);
179                 if (conn->iports == NULL) goto failed;
180                 for (i=0;ports[i];i++) {
181                         conn->iports[i] = atoi(ports[i]);
182                 }
183                 conn->iports[i] = 0;
184         } else {
185                 conn->iports = talloc_array(c, int, 2);
186                 if (conn->iports == NULL) goto failed;
187                 conn->iports[0] = port;
188                 conn->iports[1] = 0;
189         }
190
191         conn->dest_host = talloc_strdup(c, host_addr);
192         if (conn->dest_host == NULL) goto failed;
193
194         c->private = conn;
195         c->state = SMBCLI_REQUEST_SEND;
196
197         /* startup the connect process for each port in turn until one
198            succeeds or tells us that it is pending */
199         for (i=0;conn->iports[i];i++) {
200                 conn->port_num = i;
201                 conn->sock->port = conn->iports[i];
202                 c->status = smbcli_sock_connect_one(sock, 
203                                                     conn->dest_host, 
204                                                     conn->iports[i]);
205                 if (NT_STATUS_IS_OK(c->status) ||
206                     NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
207                         sock->event.fde->private = c;
208                         return c;
209                 }
210         }
211
212         c->state = SMBCLI_REQUEST_ERROR;
213         return c;
214         
215 failed:
216         talloc_free(c);
217         return NULL;
218 }
219
220 /*
221   finish a smbcli_sock_connect_send() operation
222 */
223 NTSTATUS smbcli_sock_connect_recv(struct smbcli_composite *c)
224 {
225         NTSTATUS status;
226         status = smb_composite_wait(c);
227         talloc_free(c);
228         return status;
229 }
230
231 /*
232   connect a smbcli_socket context to an IP/port pair
233   if port is 0 then choose the ports listed in smb.conf (normally 445 then 139)
234
235   sync version of the function
236 */
237 NTSTATUS smbcli_sock_connect(struct smbcli_socket *sock, const char *host_addr, int port)
238 {
239         struct smbcli_composite *c;
240
241         c = smbcli_sock_connect_send(sock, host_addr, port);
242         if (c == NULL) {
243                 return NT_STATUS_NO_MEMORY;
244         }
245
246         return smbcli_sock_connect_recv(c);
247 }
248
249
250 /****************************************************************************
251  mark the socket as dead
252 ****************************************************************************/
253 void smbcli_sock_dead(struct smbcli_socket *sock)
254 {
255         if (sock->sock != NULL) {
256                 talloc_free(sock->sock);
257                 sock->sock = NULL;
258         }
259 }
260
261 /****************************************************************************
262  Set socket options on a open connection.
263 ****************************************************************************/
264 void smbcli_sock_set_options(struct smbcli_socket *sock, const char *options)
265 {
266         socket_set_option(sock->sock, options, NULL);
267 }
268
269 /****************************************************************************
270  Write to socket. Return amount written.
271 ****************************************************************************/
272 ssize_t smbcli_sock_write(struct smbcli_socket *sock, const uint8_t *data, size_t len)
273 {
274         NTSTATUS status;
275         DATA_BLOB blob;
276         size_t nsent;
277
278         if (sock->sock == NULL) {
279                 errno = EIO;
280                 return -1;
281         }
282
283         blob.data = discard_const(data);
284         blob.length = len;
285
286         status = socket_send(sock->sock, &blob, &nsent, 0);
287         if (NT_STATUS_IS_ERR(status)) {
288                 return -1;
289         }
290
291         return nsent;
292 }
293
294
295 /****************************************************************************
296  Read from socket. return amount read
297 ****************************************************************************/
298 ssize_t smbcli_sock_read(struct smbcli_socket *sock, uint8_t *data, size_t len)
299 {
300         NTSTATUS status;
301         size_t nread;
302
303         if (sock->sock == NULL) {
304                 errno = EIO;
305                 return -1;
306         }
307
308         status = socket_recv(sock->sock, data, len, &nread, 0);
309         if (NT_STATUS_IS_ERR(status)) {
310                 return -1;
311         }
312
313         return nread;
314 }
315
316
317 /****************************************************************************
318 resolve a hostname and connect 
319 ****************************************************************************/
320 BOOL smbcli_sock_connect_byname(struct smbcli_socket *sock, const char *host, int port)
321 {
322         int name_type = NBT_NAME_SERVER;
323         const char *address;
324         NTSTATUS status;
325         struct nbt_name nbt_name;
326         char *name, *p;
327
328         name = talloc_strdup(sock, host);
329
330         /* allow hostnames of the form NAME#xx and do a netbios lookup */
331         if ((p = strchr(name, '#'))) {
332                 name_type = strtol(p+1, NULL, 16);
333                 *p = 0;
334         }
335
336         nbt_name.name = name;
337         nbt_name.type = name_type;
338         nbt_name.scope = NULL;
339         
340         status = resolve_name(&nbt_name, sock, &address);
341         if (!NT_STATUS_IS_OK(status)) {
342                 return False;
343         }
344
345         sock->hostname = name;
346
347         status = smbcli_sock_connect(sock, address, port);
348
349         return NT_STATUS_IS_OK(status);
350 }