r3304: changed the API to lib/socket/ a little.
[bbaumbach/samba-autobuild/.git] / source4 / librpc / rpc / dcerpc_sock.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    dcerpc over standard sockets transport
5
6    Copyright (C) Andrew Tridgell 2003
7    Copyright (C) Jelmer Vernooij 2004
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
26 #define MIN_HDR_SIZE 16
27
28 struct sock_blob {
29         struct sock_blob *next, *prev;
30         DATA_BLOB data;
31 };
32
33 /* transport private information used by general socket pipe transports */
34 struct sock_private {
35         struct event_context *event_ctx;
36         struct fd_event *fde;
37         struct socket_context *sock;
38         char *server_name;
39         uint32_t port;
40
41         struct sock_blob *pending_send;
42
43         struct {
44                 size_t received;
45                 DATA_BLOB data;
46                 uint_t pending_count;
47         } recv;
48 };
49
50
51 /*
52   mark the socket dead
53 */
54 static void sock_dead(struct dcerpc_pipe *p, NTSTATUS status)
55 {
56         struct sock_private *sock = p->transport.private;
57
58         if (sock && sock->sock != NULL) {
59                 talloc_free(sock->sock);
60                 sock->sock = NULL;
61         }
62
63         /* wipe any pending sends */
64         while (sock->pending_send) {
65                 struct sock_blob *blob = sock->pending_send;
66                 DLIST_REMOVE(sock->pending_send, blob);
67                 talloc_free(blob);
68         }
69
70         if (!NT_STATUS_IS_OK(status)) {
71                 p->transport.recv_data(p, NULL, status);
72         }
73 }
74
75 /*
76   process send requests
77 */
78 static void sock_process_send(struct dcerpc_pipe *p)
79 {
80         struct sock_private *sock = p->transport.private;
81
82         while (sock->pending_send) {
83                 struct sock_blob *blob = sock->pending_send;
84                 NTSTATUS status;
85                 size_t sent;
86                 status = socket_send(sock->sock, &blob->data, &sent, 0);
87                 if (NT_STATUS_IS_ERR(status)) {
88                         sock_dead(p, NT_STATUS_NET_WRITE_FAULT);
89                         break;
90                 }
91                 if (sent == 0) {
92                         break;
93                 }
94
95                 blob->data.data += sent;
96                 blob->data.length -= sent;
97
98                 if (blob->data.length != 0) {
99                         break;
100                 }
101
102                 DLIST_REMOVE(sock->pending_send, blob);
103                 talloc_free(blob);
104         }
105
106         if (sock->pending_send == NULL) {
107                 sock->fde->flags &= ~EVENT_FD_WRITE;
108         }
109 }
110
111
112 /*
113   process recv requests
114 */
115 static void sock_process_recv(struct dcerpc_pipe *p)
116 {
117         struct sock_private *sock = p->transport.private;
118         NTSTATUS status;
119         size_t nread;
120
121         if (sock->recv.data.data == NULL) {
122                 sock->recv.data = data_blob_talloc(sock, NULL, MIN_HDR_SIZE);
123         }
124
125         /* read in the base header to get the fragment length */
126         if (sock->recv.received < MIN_HDR_SIZE) {
127                 uint32_t frag_length;
128
129                 status = socket_recv(sock->sock, 
130                                      sock->recv.data.data + sock->recv.received, 
131                                      MIN_HDR_SIZE - sock->recv.received, 
132                                      &nread, 0);
133                 if (NT_STATUS_IS_ERR(status)) {
134                         sock_dead(p, NT_STATUS_NET_WRITE_FAULT);
135                         return;
136                 }
137                 if (nread == 0) {
138                         return;
139                 }
140                 
141                 sock->recv.received += nread;
142
143                 if (sock->recv.received != MIN_HDR_SIZE) {
144                         return;
145                 }
146                 frag_length = dcerpc_get_frag_length(&sock->recv.data);
147
148                 sock->recv.data.data = talloc_realloc(sock, sock->recv.data.data,
149                                                      frag_length);
150                 if (sock->recv.data.data == NULL) {
151                         sock_dead(p, NT_STATUS_NO_MEMORY);
152                         return;
153                 }
154                 sock->recv.data.length = frag_length;
155         }
156
157         /* read in the rest of the packet */
158         status = socket_recv(sock->sock, 
159                              sock->recv.data.data + sock->recv.received, 
160                              sock->recv.data.length - sock->recv.received, 
161                              &nread, 0);
162         if (NT_STATUS_IS_ERR(status)) {
163                 sock_dead(p, NT_STATUS_NET_WRITE_FAULT);
164                 return;
165         }
166         if (nread == 0) {
167                 return;
168         }
169         sock->recv.received += nread;
170
171         if (sock->recv.received != sock->recv.data.length) {
172                 return;
173         }
174
175         /* we have a full packet */
176         p->transport.recv_data(p, &sock->recv.data, NT_STATUS_OK);
177         talloc_free(sock->recv.data.data);
178         sock->recv.data = data_blob(NULL, 0);
179         sock->recv.received = 0;
180         sock->recv.pending_count--;
181         if (sock->recv.pending_count == 0) {
182                 sock->fde->flags &= ~EVENT_FD_READ;
183         }
184 }
185
186 /*
187   called when a IO is triggered by the events system
188 */
189 static void sock_io_handler(struct event_context *ev, struct fd_event *fde, 
190                            time_t t, uint16_t flags)
191 {
192         struct dcerpc_pipe *p = fde->private;
193         struct sock_private *sock = p->transport.private;
194
195         if (flags & EVENT_FD_WRITE) {
196                 sock_process_send(p);
197         }
198
199         if (sock->sock == NULL) {
200                 return;
201         }
202
203         if (flags & EVENT_FD_READ) {
204                 sock_process_recv(p);
205         }
206 }
207
208 /* 
209    initiate a read request 
210 */
211 static NTSTATUS sock_send_read(struct dcerpc_pipe *p)
212 {
213         struct sock_private *sock = p->transport.private;
214
215         sock->recv.pending_count++;
216         if (sock->recv.pending_count == 1) {
217                 sock->fde->flags |= EVENT_FD_READ;
218         }
219         return NT_STATUS_OK;
220 }
221
222 /* 
223    send an initial pdu in a multi-pdu sequence
224 */
225 static NTSTATUS sock_send_request(struct dcerpc_pipe *p, DATA_BLOB *data, BOOL trigger_read)
226 {
227         struct sock_private *sock = p->transport.private;
228         struct sock_blob *blob;
229
230         blob = talloc_p(sock, struct sock_blob);
231         if (blob == NULL) {
232                 return NT_STATUS_NO_MEMORY;
233         }
234
235         blob->data = data_blob_talloc(blob, data->data, data->length);
236         if (blob->data.data == NULL) {
237                 talloc_free(blob);
238                 return NT_STATUS_NO_MEMORY;
239         }
240
241         DLIST_ADD_END(sock->pending_send, blob, struct sock_blob *);
242
243         sock->fde->flags |= EVENT_FD_WRITE;
244
245         if (trigger_read) {
246                 sock_send_read(p);
247         }
248
249         return NT_STATUS_OK;
250 }
251
252 /* 
253    return the event context so the caller can process asynchronously
254 */
255 static struct event_context *sock_event_context(struct dcerpc_pipe *p)
256 {
257         struct sock_private *sock = p->transport.private;
258
259         return sock->event_ctx;
260 }
261
262 /* 
263    shutdown sock pipe connection
264 */
265 static NTSTATUS sock_shutdown_pipe(struct dcerpc_pipe *p)
266 {
267         sock_dead(p, NT_STATUS_OK);
268
269         return NT_STATUS_OK;
270 }
271
272 /*
273   return sock server name
274 */
275 static const char *sock_peer_name(struct dcerpc_pipe *p)
276 {
277         struct sock_private *sock = p->transport.private;
278         return sock->server_name;
279 }
280
281 /* 
282    open a rpc connection using the generic socket library
283 */
284 static NTSTATUS dcerpc_pipe_open_socket(struct dcerpc_pipe **p, 
285                                         const char *server,
286                                         uint32_t port, 
287                                         const char *type,
288                                         enum dcerpc_transport_t transport)
289 {
290         struct sock_private *sock;
291         struct socket_context *socket_ctx;
292         struct fd_event fde;
293         NTSTATUS status;
294
295         if (port == 0) {
296                 port = EPMAPPER_PORT;
297         }
298
299         if (!(*p = dcerpc_pipe_init())) {
300                 return NT_STATUS_NO_MEMORY;
301         }
302  
303         sock = talloc_p((*p), struct sock_private);
304         if (!sock) {
305                 talloc_free(*p);
306                 return NT_STATUS_NO_MEMORY;
307         }
308
309         status = socket_create(type, SOCKET_TYPE_STREAM, &socket_ctx, 0);
310         if (!NT_STATUS_IS_OK(status)) {
311                 talloc_free(*p);
312                 return status;
313         }
314         talloc_steal(sock, socket_ctx);
315
316         status = socket_connect(socket_ctx, NULL, 0, server, port, 0);
317         if (!NT_STATUS_IS_OK(status)) {
318                 talloc_free(*p);
319                 return status;
320         }
321
322         /*
323           fill in the transport methods
324         */
325         (*p)->transport.transport = transport;
326         (*p)->transport.private = NULL;
327
328         (*p)->transport.send_request = sock_send_request;
329         (*p)->transport.send_read = sock_send_read;
330         (*p)->transport.event_context = sock_event_context;
331         (*p)->transport.recv_data = NULL;
332
333         (*p)->transport.shutdown_pipe = sock_shutdown_pipe;
334         (*p)->transport.peer_name = sock_peer_name;
335         
336         sock->sock = socket_ctx;
337         sock->server_name = talloc_strdup((*p), server);
338         sock->event_ctx = event_context_init(sock);
339         sock->pending_send = NULL;
340         sock->recv.received = 0;
341         sock->recv.data = data_blob(NULL, 0);
342         sock->recv.pending_count = 0;
343
344         fde.fd = socket_get_fd(sock->sock);
345         fde.flags = 0;
346         fde.handler = sock_io_handler;
347         fde.private = *p;
348
349         sock->fde = event_add_fd(sock->event_ctx, &fde);
350
351         (*p)->transport.private = sock;
352
353         /* ensure we don't get SIGPIPE */
354         BlockSignals(True,SIGPIPE);
355
356         return NT_STATUS_OK;
357 }
358
359 /* 
360    open a rpc connection using tcp
361 */
362 NTSTATUS dcerpc_pipe_open_tcp(struct dcerpc_pipe **p, const char *server, uint32_t port)
363 {
364         return dcerpc_pipe_open_socket(p, server, port, "ip", NCACN_IP_TCP);
365 }
366
367 /* 
368    open a rpc connection to a unix socket 
369 */
370 NTSTATUS dcerpc_pipe_open_unix_stream(struct dcerpc_pipe **p, const char *path)
371 {
372         return dcerpc_pipe_open_socket(p, path, 0, "unix", NCACN_UNIX_STREAM);
373 }
374
375 /* 
376    open a rpc connection to a named pipe 
377 */
378 NTSTATUS dcerpc_pipe_open_pipe(struct dcerpc_pipe **p, const char *identifier)
379 {
380         NTSTATUS status;
381         char *canon, *full_path;
382
383         canon = talloc_strdup(NULL, identifier);
384
385         string_replace(canon, '/', '\\');
386         full_path = talloc_asprintf(canon, "%s/%s", lp_ncalrpc_dir(), canon);
387
388         status = dcerpc_pipe_open_socket(p, full_path, 0, "unix", NCALRPC);
389         talloc_free(canon);
390
391         return status;
392 }