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